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 && !tape.player_participates[i])
4233 struct PlayerInfo *player = &stored_player[i];
4234 int jx = player->jx, jy = player->jy;
4236 player->active = FALSE;
4237 StorePlayer[jx][jy] = 0;
4238 Feld[jx][jy] = EL_EMPTY;
4243 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4245 /* when in single player mode, eliminate all but the first active player */
4247 for (i = 0; i < MAX_PLAYERS; i++)
4249 if (stored_player[i].active)
4251 for (j = i + 1; j < MAX_PLAYERS; j++)
4253 if (stored_player[j].active)
4255 struct PlayerInfo *player = &stored_player[j];
4256 int jx = player->jx, jy = player->jy;
4258 player->active = FALSE;
4259 player->present = FALSE;
4261 StorePlayer[jx][jy] = 0;
4262 Feld[jx][jy] = EL_EMPTY;
4269 /* when recording the game, store which players take part in the game */
4272 #if USE_NEW_PLAYER_ASSIGNMENTS
4273 for (i = 0; i < MAX_PLAYERS; i++)
4274 if (stored_player[i].connected)
4275 tape.player_participates[i] = TRUE;
4277 for (i = 0; i < MAX_PLAYERS; i++)
4278 if (stored_player[i].active)
4279 tape.player_participates[i] = TRUE;
4285 for (i = 0; i < MAX_PLAYERS; i++)
4287 struct PlayerInfo *player = &stored_player[i];
4289 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4294 if (local_player == player)
4295 printf("Player %d is local player.\n", i+1);
4299 if (BorderElement == EL_EMPTY)
4302 SBX_Right = lev_fieldx - SCR_FIELDX;
4304 SBY_Lower = lev_fieldy - SCR_FIELDY;
4309 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4311 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4314 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4315 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4317 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4318 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4320 /* if local player not found, look for custom element that might create
4321 the player (make some assumptions about the right custom element) */
4322 if (!local_player->present)
4324 int start_x = 0, start_y = 0;
4325 int found_rating = 0;
4326 int found_element = EL_UNDEFINED;
4327 int player_nr = local_player->index_nr;
4329 SCAN_PLAYFIELD(x, y)
4331 int element = Feld[x][y];
4336 if (level.use_start_element[player_nr] &&
4337 level.start_element[player_nr] == element &&
4344 found_element = element;
4347 if (!IS_CUSTOM_ELEMENT(element))
4350 if (CAN_CHANGE(element))
4352 for (i = 0; i < element_info[element].num_change_pages; i++)
4354 /* check for player created from custom element as single target */
4355 content = element_info[element].change_page[i].target_element;
4356 is_player = ELEM_IS_PLAYER(content);
4358 if (is_player && (found_rating < 3 ||
4359 (found_rating == 3 && element < found_element)))
4365 found_element = element;
4370 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4372 /* check for player created from custom element as explosion content */
4373 content = element_info[element].content.e[xx][yy];
4374 is_player = ELEM_IS_PLAYER(content);
4376 if (is_player && (found_rating < 2 ||
4377 (found_rating == 2 && element < found_element)))
4379 start_x = x + xx - 1;
4380 start_y = y + yy - 1;
4383 found_element = element;
4386 if (!CAN_CHANGE(element))
4389 for (i = 0; i < element_info[element].num_change_pages; i++)
4391 /* check for player created from custom element as extended target */
4393 element_info[element].change_page[i].target_content.e[xx][yy];
4395 is_player = ELEM_IS_PLAYER(content);
4397 if (is_player && (found_rating < 1 ||
4398 (found_rating == 1 && element < found_element)))
4400 start_x = x + xx - 1;
4401 start_y = y + yy - 1;
4404 found_element = element;
4410 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4411 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4414 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4415 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4420 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4421 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4422 local_player->jx - MIDPOSX);
4424 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4425 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4426 local_player->jy - MIDPOSY);
4430 /* do not use PLAYING mask for fading out from main screen */
4431 game_status = GAME_MODE_MAIN;
4436 if (!game.restart_level)
4437 CloseDoor(DOOR_CLOSE_1);
4440 if (level_editor_test_game)
4441 FadeSkipNextFadeIn();
4443 FadeSetEnterScreen();
4445 if (level_editor_test_game)
4446 fading = fading_none;
4448 fading = menu.destination;
4452 FadeOut(REDRAW_FIELD);
4455 FadeOut(REDRAW_FIELD);
4459 game_status = GAME_MODE_PLAYING;
4462 /* !!! FIX THIS (START) !!! */
4463 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4465 InitGameEngine_EM();
4467 /* blit playfield from scroll buffer to normal back buffer for fading in */
4468 BlitScreenToBitmap_EM(backbuffer);
4475 /* after drawing the level, correct some elements */
4476 if (game.timegate_time_left == 0)
4477 CloseAllOpenTimegates();
4479 /* blit playfield from scroll buffer to normal back buffer for fading in */
4480 if (setup.soft_scrolling)
4481 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4483 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4485 /* !!! FIX THIS (END) !!! */
4488 FadeIn(REDRAW_FIELD);
4491 FadeIn(REDRAW_FIELD);
4496 if (!game.restart_level)
4498 /* copy default game door content to main double buffer */
4499 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4500 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4503 SetPanelBackground();
4504 SetDrawBackgroundMask(REDRAW_DOOR_1);
4507 UpdateAndDisplayGameControlValues();
4509 UpdateGameDoorValues();
4510 DrawGameDoorValues();
4513 if (!game.restart_level)
4517 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4518 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4519 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4523 /* copy actual game door content to door double buffer for OpenDoor() */
4524 BlitBitmap(drawto, bitmap_db_door,
4525 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4527 OpenDoor(DOOR_OPEN_ALL);
4529 PlaySound(SND_GAME_STARTING);
4531 if (setup.sound_music)
4534 KeyboardAutoRepeatOffUnlessAutoplay();
4538 for (i = 0; i < MAX_PLAYERS; i++)
4539 printf("Player %d %sactive.\n",
4540 i + 1, (stored_player[i].active ? "" : "not "));
4551 game.restart_level = FALSE;
4554 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4556 /* this is used for non-R'n'D game engines to update certain engine values */
4558 /* needed to determine if sounds are played within the visible screen area */
4559 scroll_x = actual_scroll_x;
4560 scroll_y = actual_scroll_y;
4563 void InitMovDir(int x, int y)
4565 int i, element = Feld[x][y];
4566 static int xy[4][2] =
4573 static int direction[3][4] =
4575 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4576 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4577 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4586 Feld[x][y] = EL_BUG;
4587 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4590 case EL_SPACESHIP_RIGHT:
4591 case EL_SPACESHIP_UP:
4592 case EL_SPACESHIP_LEFT:
4593 case EL_SPACESHIP_DOWN:
4594 Feld[x][y] = EL_SPACESHIP;
4595 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4598 case EL_BD_BUTTERFLY_RIGHT:
4599 case EL_BD_BUTTERFLY_UP:
4600 case EL_BD_BUTTERFLY_LEFT:
4601 case EL_BD_BUTTERFLY_DOWN:
4602 Feld[x][y] = EL_BD_BUTTERFLY;
4603 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4606 case EL_BD_FIREFLY_RIGHT:
4607 case EL_BD_FIREFLY_UP:
4608 case EL_BD_FIREFLY_LEFT:
4609 case EL_BD_FIREFLY_DOWN:
4610 Feld[x][y] = EL_BD_FIREFLY;
4611 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4614 case EL_PACMAN_RIGHT:
4616 case EL_PACMAN_LEFT:
4617 case EL_PACMAN_DOWN:
4618 Feld[x][y] = EL_PACMAN;
4619 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4622 case EL_YAMYAM_LEFT:
4623 case EL_YAMYAM_RIGHT:
4625 case EL_YAMYAM_DOWN:
4626 Feld[x][y] = EL_YAMYAM;
4627 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4630 case EL_SP_SNIKSNAK:
4631 MovDir[x][y] = MV_UP;
4634 case EL_SP_ELECTRON:
4635 MovDir[x][y] = MV_LEFT;
4642 Feld[x][y] = EL_MOLE;
4643 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4647 if (IS_CUSTOM_ELEMENT(element))
4649 struct ElementInfo *ei = &element_info[element];
4650 int move_direction_initial = ei->move_direction_initial;
4651 int move_pattern = ei->move_pattern;
4653 if (move_direction_initial == MV_START_PREVIOUS)
4655 if (MovDir[x][y] != MV_NONE)
4658 move_direction_initial = MV_START_AUTOMATIC;
4661 if (move_direction_initial == MV_START_RANDOM)
4662 MovDir[x][y] = 1 << RND(4);
4663 else if (move_direction_initial & MV_ANY_DIRECTION)
4664 MovDir[x][y] = move_direction_initial;
4665 else if (move_pattern == MV_ALL_DIRECTIONS ||
4666 move_pattern == MV_TURNING_LEFT ||
4667 move_pattern == MV_TURNING_RIGHT ||
4668 move_pattern == MV_TURNING_LEFT_RIGHT ||
4669 move_pattern == MV_TURNING_RIGHT_LEFT ||
4670 move_pattern == MV_TURNING_RANDOM)
4671 MovDir[x][y] = 1 << RND(4);
4672 else if (move_pattern == MV_HORIZONTAL)
4673 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4674 else if (move_pattern == MV_VERTICAL)
4675 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4676 else if (move_pattern & MV_ANY_DIRECTION)
4677 MovDir[x][y] = element_info[element].move_pattern;
4678 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4679 move_pattern == MV_ALONG_RIGHT_SIDE)
4681 /* use random direction as default start direction */
4682 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4683 MovDir[x][y] = 1 << RND(4);
4685 for (i = 0; i < NUM_DIRECTIONS; i++)
4687 int x1 = x + xy[i][0];
4688 int y1 = y + xy[i][1];
4690 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4692 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4693 MovDir[x][y] = direction[0][i];
4695 MovDir[x][y] = direction[1][i];
4704 MovDir[x][y] = 1 << RND(4);
4706 if (element != EL_BUG &&
4707 element != EL_SPACESHIP &&
4708 element != EL_BD_BUTTERFLY &&
4709 element != EL_BD_FIREFLY)
4712 for (i = 0; i < NUM_DIRECTIONS; i++)
4714 int x1 = x + xy[i][0];
4715 int y1 = y + xy[i][1];
4717 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4719 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4721 MovDir[x][y] = direction[0][i];
4724 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4725 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4727 MovDir[x][y] = direction[1][i];
4736 GfxDir[x][y] = MovDir[x][y];
4739 void InitAmoebaNr(int x, int y)
4742 int group_nr = AmoebeNachbarNr(x, y);
4746 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4748 if (AmoebaCnt[i] == 0)
4756 AmoebaNr[x][y] = group_nr;
4757 AmoebaCnt[group_nr]++;
4758 AmoebaCnt2[group_nr]++;
4761 static void PlayerWins(struct PlayerInfo *player)
4763 player->LevelSolved = TRUE;
4764 player->GameOver = TRUE;
4766 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4767 level.native_em_level->lev->score : player->score);
4769 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4770 player->LevelSolved_CountingScore = player->score_final;
4775 static int time, time_final;
4776 static int score, score_final;
4777 static int game_over_delay_1 = 0;
4778 static int game_over_delay_2 = 0;
4779 int game_over_delay_value_1 = 50;
4780 int game_over_delay_value_2 = 50;
4782 if (!local_player->LevelSolved_GameWon)
4786 /* do not start end game actions before the player stops moving (to exit) */
4787 if (local_player->MovPos)
4790 local_player->LevelSolved_GameWon = TRUE;
4791 local_player->LevelSolved_SaveTape = tape.recording;
4792 local_player->LevelSolved_SaveScore = !tape.playing;
4794 if (tape.auto_play) /* tape might already be stopped here */
4795 tape.auto_play_level_solved = TRUE;
4801 game_over_delay_1 = game_over_delay_value_1;
4802 game_over_delay_2 = game_over_delay_value_2;
4804 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4805 score = score_final = local_player->score_final;
4810 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4812 else if (level.time == 0 && TimePlayed < 999)
4815 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4818 local_player->score_final = score_final;
4820 if (level_editor_test_game)
4823 score = score_final;
4826 local_player->LevelSolved_CountingTime = time;
4827 local_player->LevelSolved_CountingScore = score;
4829 game_panel_controls[GAME_PANEL_TIME].value = time;
4830 game_panel_controls[GAME_PANEL_SCORE].value = score;
4832 DisplayGameControlValues();
4834 DrawGameValue_Time(time);
4835 DrawGameValue_Score(score);
4839 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4841 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4843 /* close exit door after last player */
4844 if ((AllPlayersGone &&
4845 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4846 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4847 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4848 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4849 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4851 int element = Feld[ExitX][ExitY];
4854 if (element == EL_EM_EXIT_OPEN ||
4855 element == EL_EM_STEEL_EXIT_OPEN)
4862 Feld[ExitX][ExitY] =
4863 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4864 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4865 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4866 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4867 EL_EM_STEEL_EXIT_CLOSING);
4869 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4873 /* player disappears */
4874 DrawLevelField(ExitX, ExitY);
4877 for (i = 0; i < MAX_PLAYERS; i++)
4879 struct PlayerInfo *player = &stored_player[i];
4881 if (player->present)
4883 RemovePlayer(player);
4885 /* player disappears */
4886 DrawLevelField(player->jx, player->jy);
4891 PlaySound(SND_GAME_WINNING);
4894 if (game_over_delay_1 > 0)
4896 game_over_delay_1--;
4901 if (time != time_final)
4903 int time_to_go = ABS(time_final - time);
4904 int time_count_dir = (time < time_final ? +1 : -1);
4905 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4907 time += time_count_steps * time_count_dir;
4908 score += time_count_steps * level.score[SC_TIME_BONUS];
4911 local_player->LevelSolved_CountingTime = time;
4912 local_player->LevelSolved_CountingScore = score;
4914 game_panel_controls[GAME_PANEL_TIME].value = time;
4915 game_panel_controls[GAME_PANEL_SCORE].value = score;
4917 DisplayGameControlValues();
4919 DrawGameValue_Time(time);
4920 DrawGameValue_Score(score);
4923 if (time == time_final)
4924 StopSound(SND_GAME_LEVELTIME_BONUS);
4925 else if (setup.sound_loops)
4926 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4928 PlaySound(SND_GAME_LEVELTIME_BONUS);
4933 local_player->LevelSolved_PanelOff = TRUE;
4935 if (game_over_delay_2 > 0)
4937 game_over_delay_2--;
4950 boolean raise_level = FALSE;
4952 local_player->LevelSolved_GameEnd = TRUE;
4954 CloseDoor(DOOR_CLOSE_1);
4956 if (local_player->LevelSolved_SaveTape)
4963 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4965 SaveTape(tape.level_nr); /* ask to save tape */
4969 if (level_editor_test_game)
4971 game_status = GAME_MODE_MAIN;
4974 DrawAndFadeInMainMenu(REDRAW_FIELD);
4982 if (!local_player->LevelSolved_SaveScore)
4985 FadeOut(REDRAW_FIELD);
4988 game_status = GAME_MODE_MAIN;
4990 DrawAndFadeInMainMenu(REDRAW_FIELD);
4995 if (level_nr == leveldir_current->handicap_level)
4997 leveldir_current->handicap_level++;
4998 SaveLevelSetup_SeriesInfo();
5001 if (level_nr < leveldir_current->last_level)
5002 raise_level = TRUE; /* advance to next level */
5004 if ((hi_pos = NewHiScore()) >= 0)
5006 game_status = GAME_MODE_SCORES;
5008 DrawHallOfFame(hi_pos);
5019 FadeOut(REDRAW_FIELD);
5022 game_status = GAME_MODE_MAIN;
5030 DrawAndFadeInMainMenu(REDRAW_FIELD);
5039 LoadScore(level_nr);
5041 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5042 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5045 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5047 if (local_player->score_final > highscore[k].Score)
5049 /* player has made it to the hall of fame */
5051 if (k < MAX_SCORE_ENTRIES - 1)
5053 int m = MAX_SCORE_ENTRIES - 1;
5056 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5057 if (strEqual(setup.player_name, highscore[l].Name))
5059 if (m == k) /* player's new highscore overwrites his old one */
5063 for (l = m; l > k; l--)
5065 strcpy(highscore[l].Name, highscore[l - 1].Name);
5066 highscore[l].Score = highscore[l - 1].Score;
5073 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5074 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5075 highscore[k].Score = local_player->score_final;
5081 else if (!strncmp(setup.player_name, highscore[k].Name,
5082 MAX_PLAYER_NAME_LEN))
5083 break; /* player already there with a higher score */
5089 SaveScore(level_nr);
5094 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5096 int element = Feld[x][y];
5097 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5098 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5099 int horiz_move = (dx != 0);
5100 int sign = (horiz_move ? dx : dy);
5101 int step = sign * element_info[element].move_stepsize;
5103 /* special values for move stepsize for spring and things on conveyor belt */
5106 if (CAN_FALL(element) &&
5107 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5108 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5109 else if (element == EL_SPRING)
5110 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5116 inline static int getElementMoveStepsize(int x, int y)
5118 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5121 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5123 if (player->GfxAction != action || player->GfxDir != dir)
5126 printf("Player frame reset! (%d => %d, %d => %d)\n",
5127 player->GfxAction, action, player->GfxDir, dir);
5130 player->GfxAction = action;
5131 player->GfxDir = dir;
5133 player->StepFrame = 0;
5137 #if USE_GFX_RESET_GFX_ANIMATION
5138 static void ResetGfxFrame(int x, int y, boolean redraw)
5140 int element = Feld[x][y];
5141 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5142 int last_gfx_frame = GfxFrame[x][y];
5144 if (graphic_info[graphic].anim_global_sync)
5145 GfxFrame[x][y] = FrameCounter;
5146 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5147 GfxFrame[x][y] = CustomValue[x][y];
5148 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5149 GfxFrame[x][y] = element_info[element].collect_score;
5150 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5151 GfxFrame[x][y] = ChangeDelay[x][y];
5153 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5154 DrawLevelGraphicAnimation(x, y, graphic);
5158 static void ResetGfxAnimation(int x, int y)
5160 GfxAction[x][y] = ACTION_DEFAULT;
5161 GfxDir[x][y] = MovDir[x][y];
5164 #if USE_GFX_RESET_GFX_ANIMATION
5165 ResetGfxFrame(x, y, FALSE);
5169 static void ResetRandomAnimationValue(int x, int y)
5171 GfxRandom[x][y] = INIT_GFX_RANDOM();
5174 void InitMovingField(int x, int y, int direction)
5176 int element = Feld[x][y];
5177 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5178 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5181 boolean is_moving_before, is_moving_after;
5183 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5186 /* check if element was/is moving or being moved before/after mode change */
5189 is_moving_before = (WasJustMoving[x][y] != 0);
5191 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5192 is_moving_before = WasJustMoving[x][y];
5195 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5197 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5199 /* reset animation only for moving elements which change direction of moving
5200 or which just started or stopped moving
5201 (else CEs with property "can move" / "not moving" are reset each frame) */
5202 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5204 if (is_moving_before != is_moving_after ||
5205 direction != MovDir[x][y])
5206 ResetGfxAnimation(x, y);
5208 if ((is_moving_before || is_moving_after) && !continues_moving)
5209 ResetGfxAnimation(x, y);
5212 if (!continues_moving)
5213 ResetGfxAnimation(x, y);
5216 MovDir[x][y] = direction;
5217 GfxDir[x][y] = direction;
5219 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5220 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5221 direction == MV_DOWN && CAN_FALL(element) ?
5222 ACTION_FALLING : ACTION_MOVING);
5224 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5225 ACTION_FALLING : ACTION_MOVING);
5228 /* this is needed for CEs with property "can move" / "not moving" */
5230 if (is_moving_after)
5232 if (Feld[newx][newy] == EL_EMPTY)
5233 Feld[newx][newy] = EL_BLOCKED;
5235 MovDir[newx][newy] = MovDir[x][y];
5237 #if USE_NEW_CUSTOM_VALUE
5238 CustomValue[newx][newy] = CustomValue[x][y];
5241 GfxFrame[newx][newy] = GfxFrame[x][y];
5242 GfxRandom[newx][newy] = GfxRandom[x][y];
5243 GfxAction[newx][newy] = GfxAction[x][y];
5244 GfxDir[newx][newy] = GfxDir[x][y];
5248 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5250 int direction = MovDir[x][y];
5251 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5252 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5258 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5260 int oldx = x, oldy = y;
5261 int direction = MovDir[x][y];
5263 if (direction == MV_LEFT)
5265 else if (direction == MV_RIGHT)
5267 else if (direction == MV_UP)
5269 else if (direction == MV_DOWN)
5272 *comes_from_x = oldx;
5273 *comes_from_y = oldy;
5276 int MovingOrBlocked2Element(int x, int y)
5278 int element = Feld[x][y];
5280 if (element == EL_BLOCKED)
5284 Blocked2Moving(x, y, &oldx, &oldy);
5285 return Feld[oldx][oldy];
5291 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5293 /* like MovingOrBlocked2Element(), but if element is moving
5294 and (x,y) is the field the moving element is just leaving,
5295 return EL_BLOCKED instead of the element value */
5296 int element = Feld[x][y];
5298 if (IS_MOVING(x, y))
5300 if (element == EL_BLOCKED)
5304 Blocked2Moving(x, y, &oldx, &oldy);
5305 return Feld[oldx][oldy];
5314 static void RemoveField(int x, int y)
5316 Feld[x][y] = EL_EMPTY;
5322 #if USE_NEW_CUSTOM_VALUE
5323 CustomValue[x][y] = 0;
5327 ChangeDelay[x][y] = 0;
5328 ChangePage[x][y] = -1;
5329 Pushed[x][y] = FALSE;
5332 ExplodeField[x][y] = EX_TYPE_NONE;
5335 GfxElement[x][y] = EL_UNDEFINED;
5336 GfxAction[x][y] = ACTION_DEFAULT;
5337 GfxDir[x][y] = MV_NONE;
5339 /* !!! this would prevent the removed tile from being redrawn !!! */
5340 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5344 void RemoveMovingField(int x, int y)
5346 int oldx = x, oldy = y, newx = x, newy = y;
5347 int element = Feld[x][y];
5348 int next_element = EL_UNDEFINED;
5350 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5353 if (IS_MOVING(x, y))
5355 Moving2Blocked(x, y, &newx, &newy);
5357 if (Feld[newx][newy] != EL_BLOCKED)
5359 /* element is moving, but target field is not free (blocked), but
5360 already occupied by something different (example: acid pool);
5361 in this case, only remove the moving field, but not the target */
5363 RemoveField(oldx, oldy);
5365 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5367 TEST_DrawLevelField(oldx, oldy);
5372 else if (element == EL_BLOCKED)
5374 Blocked2Moving(x, y, &oldx, &oldy);
5375 if (!IS_MOVING(oldx, oldy))
5379 if (element == EL_BLOCKED &&
5380 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5381 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5382 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5383 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5384 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5385 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5386 next_element = get_next_element(Feld[oldx][oldy]);
5388 RemoveField(oldx, oldy);
5389 RemoveField(newx, newy);
5391 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5393 if (next_element != EL_UNDEFINED)
5394 Feld[oldx][oldy] = next_element;
5396 TEST_DrawLevelField(oldx, oldy);
5397 TEST_DrawLevelField(newx, newy);
5400 void DrawDynamite(int x, int y)
5402 int sx = SCREENX(x), sy = SCREENY(y);
5403 int graphic = el2img(Feld[x][y]);
5406 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5409 if (IS_WALKABLE_INSIDE(Back[x][y]))
5413 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5414 else if (Store[x][y])
5415 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5417 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5419 if (Back[x][y] || Store[x][y])
5420 DrawGraphicThruMask(sx, sy, graphic, frame);
5422 DrawGraphic(sx, sy, graphic, frame);
5425 void CheckDynamite(int x, int y)
5427 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5431 if (MovDelay[x][y] != 0)
5434 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5440 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5445 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5447 boolean num_checked_players = 0;
5450 for (i = 0; i < MAX_PLAYERS; i++)
5452 if (stored_player[i].active)
5454 int sx = stored_player[i].jx;
5455 int sy = stored_player[i].jy;
5457 if (num_checked_players == 0)
5464 *sx1 = MIN(*sx1, sx);
5465 *sy1 = MIN(*sy1, sy);
5466 *sx2 = MAX(*sx2, sx);
5467 *sy2 = MAX(*sy2, sy);
5470 num_checked_players++;
5475 static boolean checkIfAllPlayersFitToScreen_RND()
5477 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5479 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5481 return (sx2 - sx1 < SCR_FIELDX &&
5482 sy2 - sy1 < SCR_FIELDY);
5485 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5487 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5489 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5491 *sx = (sx1 + sx2) / 2;
5492 *sy = (sy1 + sy2) / 2;
5495 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5496 boolean center_screen, boolean quick_relocation)
5498 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5499 boolean no_delay = (tape.warp_forward);
5500 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5501 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5503 if (quick_relocation)
5505 int offset = game.scroll_delay_value;
5507 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5509 if (!level.shifted_relocation || center_screen)
5511 /* quick relocation (without scrolling), with centering of screen */
5513 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5514 x > SBX_Right + MIDPOSX ? SBX_Right :
5517 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5518 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5523 /* quick relocation (without scrolling), but do not center screen */
5525 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5526 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5529 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5530 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5533 int offset_x = x + (scroll_x - center_scroll_x);
5534 int offset_y = y + (scroll_y - center_scroll_y);
5536 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5537 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5538 offset_x - MIDPOSX);
5540 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5541 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5542 offset_y - MIDPOSY);
5547 /* quick relocation (without scrolling), inside visible screen area */
5549 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5550 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5551 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5553 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5554 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5555 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5557 /* don't scroll over playfield boundaries */
5558 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5559 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5561 /* don't scroll over playfield boundaries */
5562 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5563 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5566 RedrawPlayfield(TRUE, 0,0,0,0);
5571 int scroll_xx, scroll_yy;
5573 if (!level.shifted_relocation || center_screen)
5575 /* visible relocation (with scrolling), with centering of screen */
5577 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5578 x > SBX_Right + MIDPOSX ? SBX_Right :
5581 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587 /* visible relocation (with scrolling), but do not center screen */
5589 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5590 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5593 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5594 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5597 int offset_x = x + (scroll_x - center_scroll_x);
5598 int offset_y = y + (scroll_y - center_scroll_y);
5600 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5601 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5602 offset_x - MIDPOSX);
5604 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5605 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5606 offset_y - MIDPOSY);
5611 /* visible relocation (with scrolling), with centering of screen */
5613 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5614 x > SBX_Right + MIDPOSX ? SBX_Right :
5617 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5618 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5622 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5624 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5627 int fx = FX, fy = FY;
5629 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5630 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5632 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5638 fx += dx * TILEX / 2;
5639 fy += dy * TILEY / 2;
5641 ScrollLevel(dx, dy);
5644 /* scroll in two steps of half tile size to make things smoother */
5645 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5647 Delay(wait_delay_value);
5649 /* scroll second step to align at full tile size */
5651 Delay(wait_delay_value);
5656 Delay(wait_delay_value);
5660 void RelocatePlayer(int jx, int jy, int el_player_raw)
5662 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5663 int player_nr = GET_PLAYER_NR(el_player);
5664 struct PlayerInfo *player = &stored_player[player_nr];
5665 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5666 boolean no_delay = (tape.warp_forward);
5667 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5668 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5669 int old_jx = player->jx;
5670 int old_jy = player->jy;
5671 int old_element = Feld[old_jx][old_jy];
5672 int element = Feld[jx][jy];
5673 boolean player_relocated = (old_jx != jx || old_jy != jy);
5675 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5676 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5677 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5678 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5679 int leave_side_horiz = move_dir_horiz;
5680 int leave_side_vert = move_dir_vert;
5681 int enter_side = enter_side_horiz | enter_side_vert;
5682 int leave_side = leave_side_horiz | leave_side_vert;
5684 if (player->GameOver) /* do not reanimate dead player */
5687 if (!player_relocated) /* no need to relocate the player */
5690 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5692 RemoveField(jx, jy); /* temporarily remove newly placed player */
5693 DrawLevelField(jx, jy);
5696 if (player->present)
5698 while (player->MovPos)
5700 ScrollPlayer(player, SCROLL_GO_ON);
5701 ScrollScreen(NULL, SCROLL_GO_ON);
5703 AdvanceFrameAndPlayerCounters(player->index_nr);
5708 Delay(wait_delay_value);
5711 DrawPlayer(player); /* needed here only to cleanup last field */
5712 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5714 player->is_moving = FALSE;
5717 if (IS_CUSTOM_ELEMENT(old_element))
5718 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5720 player->index_bit, leave_side);
5722 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5724 player->index_bit, leave_side);
5726 Feld[jx][jy] = el_player;
5727 InitPlayerField(jx, jy, el_player, TRUE);
5729 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5731 Feld[jx][jy] = element;
5732 InitField(jx, jy, FALSE);
5735 /* only visually relocate centered player */
5736 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5737 FALSE, level.instant_relocation);
5739 TestIfPlayerTouchesBadThing(jx, jy);
5740 TestIfPlayerTouchesCustomElement(jx, jy);
5742 if (IS_CUSTOM_ELEMENT(element))
5743 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5744 player->index_bit, enter_side);
5746 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5747 player->index_bit, enter_side);
5750 if (player->is_switching)
5752 /* ensure that relocation while still switching an element does not cause
5753 a new element to be treated as also switched directly after relocation
5754 (this is important for teleporter switches that teleport the player to
5755 a place where another teleporter switch is in the same direction, which
5756 would then incorrectly be treated as immediately switched before the
5757 direction key that caused the switch was released) */
5759 player->switch_x += jx - old_jx;
5760 player->switch_y += jy - old_jy;
5765 void Explode(int ex, int ey, int phase, int mode)
5771 /* !!! eliminate this variable !!! */
5772 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5774 if (game.explosions_delayed)
5776 ExplodeField[ex][ey] = mode;
5780 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5782 int center_element = Feld[ex][ey];
5783 int artwork_element, explosion_element; /* set these values later */
5786 /* --- This is only really needed (and now handled) in "Impact()". --- */
5787 /* do not explode moving elements that left the explode field in time */
5788 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5789 center_element == EL_EMPTY &&
5790 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5795 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5796 if (mode == EX_TYPE_NORMAL ||
5797 mode == EX_TYPE_CENTER ||
5798 mode == EX_TYPE_CROSS)
5799 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5802 /* remove things displayed in background while burning dynamite */
5803 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5806 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5808 /* put moving element to center field (and let it explode there) */
5809 center_element = MovingOrBlocked2Element(ex, ey);
5810 RemoveMovingField(ex, ey);
5811 Feld[ex][ey] = center_element;
5814 /* now "center_element" is finally determined -- set related values now */
5815 artwork_element = center_element; /* for custom player artwork */
5816 explosion_element = center_element; /* for custom player artwork */
5818 if (IS_PLAYER(ex, ey))
5820 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5822 artwork_element = stored_player[player_nr].artwork_element;
5824 if (level.use_explosion_element[player_nr])
5826 explosion_element = level.explosion_element[player_nr];
5827 artwork_element = explosion_element;
5832 if (mode == EX_TYPE_NORMAL ||
5833 mode == EX_TYPE_CENTER ||
5834 mode == EX_TYPE_CROSS)
5835 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5838 last_phase = element_info[explosion_element].explosion_delay + 1;
5840 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5842 int xx = x - ex + 1;
5843 int yy = y - ey + 1;
5846 if (!IN_LEV_FIELD(x, y) ||
5847 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5848 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5851 element = Feld[x][y];
5853 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5855 element = MovingOrBlocked2Element(x, y);
5857 if (!IS_EXPLOSION_PROOF(element))
5858 RemoveMovingField(x, y);
5861 /* indestructible elements can only explode in center (but not flames) */
5862 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5863 mode == EX_TYPE_BORDER)) ||
5864 element == EL_FLAMES)
5867 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5868 behaviour, for example when touching a yamyam that explodes to rocks
5869 with active deadly shield, a rock is created under the player !!! */
5870 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5872 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5873 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5874 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5876 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5879 if (IS_ACTIVE_BOMB(element))
5881 /* re-activate things under the bomb like gate or penguin */
5882 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5889 /* save walkable background elements while explosion on same tile */
5890 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5891 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5892 Back[x][y] = element;
5894 /* ignite explodable elements reached by other explosion */
5895 if (element == EL_EXPLOSION)
5896 element = Store2[x][y];
5898 if (AmoebaNr[x][y] &&
5899 (element == EL_AMOEBA_FULL ||
5900 element == EL_BD_AMOEBA ||
5901 element == EL_AMOEBA_GROWING))
5903 AmoebaCnt[AmoebaNr[x][y]]--;
5904 AmoebaCnt2[AmoebaNr[x][y]]--;
5909 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5911 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5913 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5915 if (PLAYERINFO(ex, ey)->use_murphy)
5916 Store[x][y] = EL_EMPTY;
5919 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5920 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5921 else if (ELEM_IS_PLAYER(center_element))
5922 Store[x][y] = EL_EMPTY;
5923 else if (center_element == EL_YAMYAM)
5924 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5925 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5926 Store[x][y] = element_info[center_element].content.e[xx][yy];
5928 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5929 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5930 otherwise) -- FIX THIS !!! */
5931 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5932 Store[x][y] = element_info[element].content.e[1][1];
5934 else if (!CAN_EXPLODE(element))
5935 Store[x][y] = element_info[element].content.e[1][1];
5938 Store[x][y] = EL_EMPTY;
5940 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5941 center_element == EL_AMOEBA_TO_DIAMOND)
5942 Store2[x][y] = element;
5944 Feld[x][y] = EL_EXPLOSION;
5945 GfxElement[x][y] = artwork_element;
5947 ExplodePhase[x][y] = 1;
5948 ExplodeDelay[x][y] = last_phase;
5953 if (center_element == EL_YAMYAM)
5954 game.yamyam_content_nr =
5955 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5967 GfxFrame[x][y] = 0; /* restart explosion animation */
5969 last_phase = ExplodeDelay[x][y];
5971 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5975 /* activate this even in non-DEBUG version until cause for crash in
5976 getGraphicAnimationFrame() (see below) is found and eliminated */
5982 /* this can happen if the player leaves an explosion just in time */
5983 if (GfxElement[x][y] == EL_UNDEFINED)
5984 GfxElement[x][y] = EL_EMPTY;
5986 if (GfxElement[x][y] == EL_UNDEFINED)
5989 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5990 printf("Explode(): This should never happen!\n");
5993 GfxElement[x][y] = EL_EMPTY;
5999 border_element = Store2[x][y];
6000 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6001 border_element = StorePlayer[x][y];
6003 if (phase == element_info[border_element].ignition_delay ||
6004 phase == last_phase)
6006 boolean border_explosion = FALSE;
6008 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6009 !PLAYER_EXPLOSION_PROTECTED(x, y))
6011 KillPlayerUnlessExplosionProtected(x, y);
6012 border_explosion = TRUE;
6014 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6016 Feld[x][y] = Store2[x][y];
6019 border_explosion = TRUE;
6021 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6023 AmoebeUmwandeln(x, y);
6025 border_explosion = TRUE;
6028 /* if an element just explodes due to another explosion (chain-reaction),
6029 do not immediately end the new explosion when it was the last frame of
6030 the explosion (as it would be done in the following "if"-statement!) */
6031 if (border_explosion && phase == last_phase)
6035 if (phase == last_phase)
6039 element = Feld[x][y] = Store[x][y];
6040 Store[x][y] = Store2[x][y] = 0;
6041 GfxElement[x][y] = EL_UNDEFINED;
6043 /* player can escape from explosions and might therefore be still alive */
6044 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6045 element <= EL_PLAYER_IS_EXPLODING_4)
6047 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6048 int explosion_element = EL_PLAYER_1 + player_nr;
6049 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6050 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6052 if (level.use_explosion_element[player_nr])
6053 explosion_element = level.explosion_element[player_nr];
6055 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6056 element_info[explosion_element].content.e[xx][yy]);
6059 /* restore probably existing indestructible background element */
6060 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6061 element = Feld[x][y] = Back[x][y];
6064 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6065 GfxDir[x][y] = MV_NONE;
6066 ChangeDelay[x][y] = 0;
6067 ChangePage[x][y] = -1;
6069 #if USE_NEW_CUSTOM_VALUE
6070 CustomValue[x][y] = 0;
6073 InitField_WithBug2(x, y, FALSE);
6075 TEST_DrawLevelField(x, y);
6077 TestIfElementTouchesCustomElement(x, y);
6079 if (GFX_CRUMBLED(element))
6080 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6082 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6083 StorePlayer[x][y] = 0;
6085 if (ELEM_IS_PLAYER(element))
6086 RelocatePlayer(x, y, element);
6088 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6090 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6091 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6094 TEST_DrawLevelFieldCrumbledSand(x, y);
6096 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6098 DrawLevelElement(x, y, Back[x][y]);
6099 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6101 else if (IS_WALKABLE_UNDER(Back[x][y]))
6103 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6104 DrawLevelElementThruMask(x, y, Back[x][y]);
6106 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6107 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6111 void DynaExplode(int ex, int ey)
6114 int dynabomb_element = Feld[ex][ey];
6115 int dynabomb_size = 1;
6116 boolean dynabomb_xl = FALSE;
6117 struct PlayerInfo *player;
6118 static int xy[4][2] =
6126 if (IS_ACTIVE_BOMB(dynabomb_element))
6128 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6129 dynabomb_size = player->dynabomb_size;
6130 dynabomb_xl = player->dynabomb_xl;
6131 player->dynabombs_left++;
6134 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6136 for (i = 0; i < NUM_DIRECTIONS; i++)
6138 for (j = 1; j <= dynabomb_size; j++)
6140 int x = ex + j * xy[i][0];
6141 int y = ey + j * xy[i][1];
6144 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6147 element = Feld[x][y];
6149 /* do not restart explosions of fields with active bombs */
6150 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6153 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6155 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6156 !IS_DIGGABLE(element) && !dynabomb_xl)
6162 void Bang(int x, int y)
6164 int element = MovingOrBlocked2Element(x, y);
6165 int explosion_type = EX_TYPE_NORMAL;
6167 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6169 struct PlayerInfo *player = PLAYERINFO(x, y);
6171 #if USE_FIX_CE_ACTION_WITH_PLAYER
6172 element = Feld[x][y] = player->initial_element;
6174 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6175 player->element_nr);
6178 if (level.use_explosion_element[player->index_nr])
6180 int explosion_element = level.explosion_element[player->index_nr];
6182 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6183 explosion_type = EX_TYPE_CROSS;
6184 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6185 explosion_type = EX_TYPE_CENTER;
6193 case EL_BD_BUTTERFLY:
6196 case EL_DARK_YAMYAM:
6200 RaiseScoreElement(element);
6203 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6204 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6205 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6206 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6207 case EL_DYNABOMB_INCREASE_NUMBER:
6208 case EL_DYNABOMB_INCREASE_SIZE:
6209 case EL_DYNABOMB_INCREASE_POWER:
6210 explosion_type = EX_TYPE_DYNA;
6213 case EL_DC_LANDMINE:
6215 case EL_EM_EXIT_OPEN:
6216 case EL_EM_STEEL_EXIT_OPEN:
6218 explosion_type = EX_TYPE_CENTER;
6223 case EL_LAMP_ACTIVE:
6224 case EL_AMOEBA_TO_DIAMOND:
6225 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6226 explosion_type = EX_TYPE_CENTER;
6230 if (element_info[element].explosion_type == EXPLODES_CROSS)
6231 explosion_type = EX_TYPE_CROSS;
6232 else if (element_info[element].explosion_type == EXPLODES_1X1)
6233 explosion_type = EX_TYPE_CENTER;
6237 if (explosion_type == EX_TYPE_DYNA)
6240 Explode(x, y, EX_PHASE_START, explosion_type);
6242 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6245 void SplashAcid(int x, int y)
6247 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6248 (!IN_LEV_FIELD(x - 1, y - 2) ||
6249 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6250 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6252 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6253 (!IN_LEV_FIELD(x + 1, y - 2) ||
6254 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6255 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6257 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6260 static void InitBeltMovement()
6262 static int belt_base_element[4] =
6264 EL_CONVEYOR_BELT_1_LEFT,
6265 EL_CONVEYOR_BELT_2_LEFT,
6266 EL_CONVEYOR_BELT_3_LEFT,
6267 EL_CONVEYOR_BELT_4_LEFT
6269 static int belt_base_active_element[4] =
6271 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6272 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6273 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6274 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6279 /* set frame order for belt animation graphic according to belt direction */
6280 for (i = 0; i < NUM_BELTS; i++)
6284 for (j = 0; j < NUM_BELT_PARTS; j++)
6286 int element = belt_base_active_element[belt_nr] + j;
6287 int graphic_1 = el2img(element);
6288 int graphic_2 = el2panelimg(element);
6290 if (game.belt_dir[i] == MV_LEFT)
6292 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6293 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6297 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6298 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6303 SCAN_PLAYFIELD(x, y)
6305 int element = Feld[x][y];
6307 for (i = 0; i < NUM_BELTS; i++)
6309 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6311 int e_belt_nr = getBeltNrFromBeltElement(element);
6314 if (e_belt_nr == belt_nr)
6316 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6318 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6325 static void ToggleBeltSwitch(int x, int y)
6327 static int belt_base_element[4] =
6329 EL_CONVEYOR_BELT_1_LEFT,
6330 EL_CONVEYOR_BELT_2_LEFT,
6331 EL_CONVEYOR_BELT_3_LEFT,
6332 EL_CONVEYOR_BELT_4_LEFT
6334 static int belt_base_active_element[4] =
6336 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6337 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6338 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6339 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6341 static int belt_base_switch_element[4] =
6343 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6344 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6345 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6346 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6348 static int belt_move_dir[4] =
6356 int element = Feld[x][y];
6357 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6358 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6359 int belt_dir = belt_move_dir[belt_dir_nr];
6362 if (!IS_BELT_SWITCH(element))
6365 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6366 game.belt_dir[belt_nr] = belt_dir;
6368 if (belt_dir_nr == 3)
6371 /* set frame order for belt animation graphic according to belt direction */
6372 for (i = 0; i < NUM_BELT_PARTS; i++)
6374 int element = belt_base_active_element[belt_nr] + i;
6375 int graphic_1 = el2img(element);
6376 int graphic_2 = el2panelimg(element);
6378 if (belt_dir == MV_LEFT)
6380 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6381 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6385 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6386 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6390 SCAN_PLAYFIELD(xx, yy)
6392 int element = Feld[xx][yy];
6394 if (IS_BELT_SWITCH(element))
6396 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6398 if (e_belt_nr == belt_nr)
6400 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6401 TEST_DrawLevelField(xx, yy);
6404 else if (IS_BELT(element) && belt_dir != MV_NONE)
6406 int e_belt_nr = getBeltNrFromBeltElement(element);
6408 if (e_belt_nr == belt_nr)
6410 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6412 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6413 TEST_DrawLevelField(xx, yy);
6416 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6418 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6420 if (e_belt_nr == belt_nr)
6422 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6424 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6425 TEST_DrawLevelField(xx, yy);
6431 static void ToggleSwitchgateSwitch(int x, int y)
6435 game.switchgate_pos = !game.switchgate_pos;
6437 SCAN_PLAYFIELD(xx, yy)
6439 int element = Feld[xx][yy];
6441 #if !USE_BOTH_SWITCHGATE_SWITCHES
6442 if (element == EL_SWITCHGATE_SWITCH_UP ||
6443 element == EL_SWITCHGATE_SWITCH_DOWN)
6445 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6446 TEST_DrawLevelField(xx, yy);
6448 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6449 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6451 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6452 TEST_DrawLevelField(xx, yy);
6455 if (element == EL_SWITCHGATE_SWITCH_UP)
6457 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6458 TEST_DrawLevelField(xx, yy);
6460 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6462 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6463 TEST_DrawLevelField(xx, yy);
6465 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6467 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6468 TEST_DrawLevelField(xx, yy);
6470 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6472 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6473 TEST_DrawLevelField(xx, yy);
6476 else if (element == EL_SWITCHGATE_OPEN ||
6477 element == EL_SWITCHGATE_OPENING)
6479 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6481 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6483 else if (element == EL_SWITCHGATE_CLOSED ||
6484 element == EL_SWITCHGATE_CLOSING)
6486 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6488 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6493 static int getInvisibleActiveFromInvisibleElement(int element)
6495 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6496 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6497 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6501 static int getInvisibleFromInvisibleActiveElement(int element)
6503 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6504 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6505 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6509 static void RedrawAllLightSwitchesAndInvisibleElements()
6513 SCAN_PLAYFIELD(x, y)
6515 int element = Feld[x][y];
6517 if (element == EL_LIGHT_SWITCH &&
6518 game.light_time_left > 0)
6520 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6521 TEST_DrawLevelField(x, y);
6523 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6524 game.light_time_left == 0)
6526 Feld[x][y] = EL_LIGHT_SWITCH;
6527 TEST_DrawLevelField(x, y);
6529 else if (element == EL_EMC_DRIPPER &&
6530 game.light_time_left > 0)
6532 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6533 TEST_DrawLevelField(x, y);
6535 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6536 game.light_time_left == 0)
6538 Feld[x][y] = EL_EMC_DRIPPER;
6539 TEST_DrawLevelField(x, y);
6541 else if (element == EL_INVISIBLE_STEELWALL ||
6542 element == EL_INVISIBLE_WALL ||
6543 element == EL_INVISIBLE_SAND)
6545 if (game.light_time_left > 0)
6546 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6548 TEST_DrawLevelField(x, y);
6550 /* uncrumble neighbour fields, if needed */
6551 if (element == EL_INVISIBLE_SAND)
6552 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6554 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6555 element == EL_INVISIBLE_WALL_ACTIVE ||
6556 element == EL_INVISIBLE_SAND_ACTIVE)
6558 if (game.light_time_left == 0)
6559 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6561 TEST_DrawLevelField(x, y);
6563 /* re-crumble neighbour fields, if needed */
6564 if (element == EL_INVISIBLE_SAND)
6565 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6570 static void RedrawAllInvisibleElementsForLenses()
6574 SCAN_PLAYFIELD(x, y)
6576 int element = Feld[x][y];
6578 if (element == EL_EMC_DRIPPER &&
6579 game.lenses_time_left > 0)
6581 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6582 TEST_DrawLevelField(x, y);
6584 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6585 game.lenses_time_left == 0)
6587 Feld[x][y] = EL_EMC_DRIPPER;
6588 TEST_DrawLevelField(x, y);
6590 else if (element == EL_INVISIBLE_STEELWALL ||
6591 element == EL_INVISIBLE_WALL ||
6592 element == EL_INVISIBLE_SAND)
6594 if (game.lenses_time_left > 0)
6595 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6597 TEST_DrawLevelField(x, y);
6599 /* uncrumble neighbour fields, if needed */
6600 if (element == EL_INVISIBLE_SAND)
6601 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6603 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6604 element == EL_INVISIBLE_WALL_ACTIVE ||
6605 element == EL_INVISIBLE_SAND_ACTIVE)
6607 if (game.lenses_time_left == 0)
6608 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6610 TEST_DrawLevelField(x, y);
6612 /* re-crumble neighbour fields, if needed */
6613 if (element == EL_INVISIBLE_SAND)
6614 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6619 static void RedrawAllInvisibleElementsForMagnifier()
6623 SCAN_PLAYFIELD(x, y)
6625 int element = Feld[x][y];
6627 if (element == EL_EMC_FAKE_GRASS &&
6628 game.magnify_time_left > 0)
6630 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6631 TEST_DrawLevelField(x, y);
6633 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6634 game.magnify_time_left == 0)
6636 Feld[x][y] = EL_EMC_FAKE_GRASS;
6637 TEST_DrawLevelField(x, y);
6639 else if (IS_GATE_GRAY(element) &&
6640 game.magnify_time_left > 0)
6642 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6643 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6644 IS_EM_GATE_GRAY(element) ?
6645 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6646 IS_EMC_GATE_GRAY(element) ?
6647 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6648 IS_DC_GATE_GRAY(element) ?
6649 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6651 TEST_DrawLevelField(x, y);
6653 else if (IS_GATE_GRAY_ACTIVE(element) &&
6654 game.magnify_time_left == 0)
6656 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6657 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6658 IS_EM_GATE_GRAY_ACTIVE(element) ?
6659 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6660 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6661 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6662 IS_DC_GATE_GRAY_ACTIVE(element) ?
6663 EL_DC_GATE_WHITE_GRAY :
6665 TEST_DrawLevelField(x, y);
6670 static void ToggleLightSwitch(int x, int y)
6672 int element = Feld[x][y];
6674 game.light_time_left =
6675 (element == EL_LIGHT_SWITCH ?
6676 level.time_light * FRAMES_PER_SECOND : 0);
6678 RedrawAllLightSwitchesAndInvisibleElements();
6681 static void ActivateTimegateSwitch(int x, int y)
6685 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6687 SCAN_PLAYFIELD(xx, yy)
6689 int element = Feld[xx][yy];
6691 if (element == EL_TIMEGATE_CLOSED ||
6692 element == EL_TIMEGATE_CLOSING)
6694 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6695 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6699 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6701 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6702 TEST_DrawLevelField(xx, yy);
6709 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6710 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6712 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6716 void Impact(int x, int y)
6718 boolean last_line = (y == lev_fieldy - 1);
6719 boolean object_hit = FALSE;
6720 boolean impact = (last_line || object_hit);
6721 int element = Feld[x][y];
6722 int smashed = EL_STEELWALL;
6724 if (!last_line) /* check if element below was hit */
6726 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6729 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6730 MovDir[x][y + 1] != MV_DOWN ||
6731 MovPos[x][y + 1] <= TILEY / 2));
6733 /* do not smash moving elements that left the smashed field in time */
6734 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6735 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6738 #if USE_QUICKSAND_IMPACT_BUGFIX
6739 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6741 RemoveMovingField(x, y + 1);
6742 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6743 Feld[x][y + 2] = EL_ROCK;
6744 TEST_DrawLevelField(x, y + 2);
6749 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6751 RemoveMovingField(x, y + 1);
6752 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6753 Feld[x][y + 2] = EL_ROCK;
6754 TEST_DrawLevelField(x, y + 2);
6761 smashed = MovingOrBlocked2Element(x, y + 1);
6763 impact = (last_line || object_hit);
6766 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6768 SplashAcid(x, y + 1);
6772 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6773 /* only reset graphic animation if graphic really changes after impact */
6775 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6777 ResetGfxAnimation(x, y);
6778 TEST_DrawLevelField(x, y);
6781 if (impact && CAN_EXPLODE_IMPACT(element))
6786 else if (impact && element == EL_PEARL &&
6787 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6789 ResetGfxAnimation(x, y);
6791 Feld[x][y] = EL_PEARL_BREAKING;
6792 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6795 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6797 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6802 if (impact && element == EL_AMOEBA_DROP)
6804 if (object_hit && IS_PLAYER(x, y + 1))
6805 KillPlayerUnlessEnemyProtected(x, y + 1);
6806 else if (object_hit && smashed == EL_PENGUIN)
6810 Feld[x][y] = EL_AMOEBA_GROWING;
6811 Store[x][y] = EL_AMOEBA_WET;
6813 ResetRandomAnimationValue(x, y);
6818 if (object_hit) /* check which object was hit */
6820 if ((CAN_PASS_MAGIC_WALL(element) &&
6821 (smashed == EL_MAGIC_WALL ||
6822 smashed == EL_BD_MAGIC_WALL)) ||
6823 (CAN_PASS_DC_MAGIC_WALL(element) &&
6824 smashed == EL_DC_MAGIC_WALL))
6827 int activated_magic_wall =
6828 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6829 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6830 EL_DC_MAGIC_WALL_ACTIVE);
6832 /* activate magic wall / mill */
6833 SCAN_PLAYFIELD(xx, yy)
6835 if (Feld[xx][yy] == smashed)
6836 Feld[xx][yy] = activated_magic_wall;
6839 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6840 game.magic_wall_active = TRUE;
6842 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6843 SND_MAGIC_WALL_ACTIVATING :
6844 smashed == EL_BD_MAGIC_WALL ?
6845 SND_BD_MAGIC_WALL_ACTIVATING :
6846 SND_DC_MAGIC_WALL_ACTIVATING));
6849 if (IS_PLAYER(x, y + 1))
6851 if (CAN_SMASH_PLAYER(element))
6853 KillPlayerUnlessEnemyProtected(x, y + 1);
6857 else if (smashed == EL_PENGUIN)
6859 if (CAN_SMASH_PLAYER(element))
6865 else if (element == EL_BD_DIAMOND)
6867 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6873 else if (((element == EL_SP_INFOTRON ||
6874 element == EL_SP_ZONK) &&
6875 (smashed == EL_SP_SNIKSNAK ||
6876 smashed == EL_SP_ELECTRON ||
6877 smashed == EL_SP_DISK_ORANGE)) ||
6878 (element == EL_SP_INFOTRON &&
6879 smashed == EL_SP_DISK_YELLOW))
6884 else if (CAN_SMASH_EVERYTHING(element))
6886 if (IS_CLASSIC_ENEMY(smashed) ||
6887 CAN_EXPLODE_SMASHED(smashed))
6892 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6894 if (smashed == EL_LAMP ||
6895 smashed == EL_LAMP_ACTIVE)
6900 else if (smashed == EL_NUT)
6902 Feld[x][y + 1] = EL_NUT_BREAKING;
6903 PlayLevelSound(x, y, SND_NUT_BREAKING);
6904 RaiseScoreElement(EL_NUT);
6907 else if (smashed == EL_PEARL)
6909 ResetGfxAnimation(x, y);
6911 Feld[x][y + 1] = EL_PEARL_BREAKING;
6912 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6915 else if (smashed == EL_DIAMOND)
6917 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6918 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6921 else if (IS_BELT_SWITCH(smashed))
6923 ToggleBeltSwitch(x, y + 1);
6925 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6926 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6927 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6928 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6930 ToggleSwitchgateSwitch(x, y + 1);
6932 else if (smashed == EL_LIGHT_SWITCH ||
6933 smashed == EL_LIGHT_SWITCH_ACTIVE)
6935 ToggleLightSwitch(x, y + 1);
6940 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6943 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6945 CheckElementChangeBySide(x, y + 1, smashed, element,
6946 CE_SWITCHED, CH_SIDE_TOP);
6947 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6953 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6958 /* play sound of magic wall / mill */
6960 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6961 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6962 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6964 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6965 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6966 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6967 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6968 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6969 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6974 /* play sound of object that hits the ground */
6975 if (last_line || object_hit)
6976 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6979 inline static void TurnRoundExt(int x, int y)
6991 { 0, 0 }, { 0, 0 }, { 0, 0 },
6996 int left, right, back;
7000 { MV_DOWN, MV_UP, MV_RIGHT },
7001 { MV_UP, MV_DOWN, MV_LEFT },
7003 { MV_LEFT, MV_RIGHT, MV_DOWN },
7007 { MV_RIGHT, MV_LEFT, MV_UP }
7010 int element = Feld[x][y];
7011 int move_pattern = element_info[element].move_pattern;
7013 int old_move_dir = MovDir[x][y];
7014 int left_dir = turn[old_move_dir].left;
7015 int right_dir = turn[old_move_dir].right;
7016 int back_dir = turn[old_move_dir].back;
7018 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7019 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7020 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7021 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7023 int left_x = x + left_dx, left_y = y + left_dy;
7024 int right_x = x + right_dx, right_y = y + right_dy;
7025 int move_x = x + move_dx, move_y = y + move_dy;
7029 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7031 TestIfBadThingTouchesOtherBadThing(x, y);
7033 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7034 MovDir[x][y] = right_dir;
7035 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7036 MovDir[x][y] = left_dir;
7038 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7040 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7043 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7045 TestIfBadThingTouchesOtherBadThing(x, y);
7047 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7048 MovDir[x][y] = left_dir;
7049 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7050 MovDir[x][y] = right_dir;
7052 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7054 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7057 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7059 TestIfBadThingTouchesOtherBadThing(x, y);
7061 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7062 MovDir[x][y] = left_dir;
7063 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7064 MovDir[x][y] = right_dir;
7066 if (MovDir[x][y] != old_move_dir)
7069 else if (element == EL_YAMYAM)
7071 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7072 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7074 if (can_turn_left && can_turn_right)
7075 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7076 else if (can_turn_left)
7077 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7078 else if (can_turn_right)
7079 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7081 MovDir[x][y] = back_dir;
7083 MovDelay[x][y] = 16 + 16 * RND(3);
7085 else if (element == EL_DARK_YAMYAM)
7087 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7089 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7092 if (can_turn_left && can_turn_right)
7093 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7094 else if (can_turn_left)
7095 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7096 else if (can_turn_right)
7097 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7099 MovDir[x][y] = back_dir;
7101 MovDelay[x][y] = 16 + 16 * RND(3);
7103 else if (element == EL_PACMAN)
7105 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7106 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7108 if (can_turn_left && can_turn_right)
7109 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7110 else if (can_turn_left)
7111 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7112 else if (can_turn_right)
7113 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7115 MovDir[x][y] = back_dir;
7117 MovDelay[x][y] = 6 + RND(40);
7119 else if (element == EL_PIG)
7121 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7122 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7123 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7124 boolean should_turn_left, should_turn_right, should_move_on;
7126 int rnd = RND(rnd_value);
7128 should_turn_left = (can_turn_left &&
7130 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7131 y + back_dy + left_dy)));
7132 should_turn_right = (can_turn_right &&
7134 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7135 y + back_dy + right_dy)));
7136 should_move_on = (can_move_on &&
7139 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7140 y + move_dy + left_dy) ||
7141 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7142 y + move_dy + right_dy)));
7144 if (should_turn_left || should_turn_right || should_move_on)
7146 if (should_turn_left && should_turn_right && should_move_on)
7147 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7148 rnd < 2 * rnd_value / 3 ? right_dir :
7150 else if (should_turn_left && should_turn_right)
7151 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7152 else if (should_turn_left && should_move_on)
7153 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7154 else if (should_turn_right && should_move_on)
7155 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7156 else if (should_turn_left)
7157 MovDir[x][y] = left_dir;
7158 else if (should_turn_right)
7159 MovDir[x][y] = right_dir;
7160 else if (should_move_on)
7161 MovDir[x][y] = old_move_dir;
7163 else if (can_move_on && rnd > rnd_value / 8)
7164 MovDir[x][y] = old_move_dir;
7165 else if (can_turn_left && can_turn_right)
7166 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7167 else if (can_turn_left && rnd > rnd_value / 8)
7168 MovDir[x][y] = left_dir;
7169 else if (can_turn_right && rnd > rnd_value/8)
7170 MovDir[x][y] = right_dir;
7172 MovDir[x][y] = back_dir;
7174 xx = x + move_xy[MovDir[x][y]].dx;
7175 yy = y + move_xy[MovDir[x][y]].dy;
7177 if (!IN_LEV_FIELD(xx, yy) ||
7178 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7179 MovDir[x][y] = old_move_dir;
7183 else if (element == EL_DRAGON)
7185 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7186 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7187 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7189 int rnd = RND(rnd_value);
7191 if (can_move_on && rnd > rnd_value / 8)
7192 MovDir[x][y] = old_move_dir;
7193 else if (can_turn_left && can_turn_right)
7194 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7195 else if (can_turn_left && rnd > rnd_value / 8)
7196 MovDir[x][y] = left_dir;
7197 else if (can_turn_right && rnd > rnd_value / 8)
7198 MovDir[x][y] = right_dir;
7200 MovDir[x][y] = back_dir;
7202 xx = x + move_xy[MovDir[x][y]].dx;
7203 yy = y + move_xy[MovDir[x][y]].dy;
7205 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7206 MovDir[x][y] = old_move_dir;
7210 else if (element == EL_MOLE)
7212 boolean can_move_on =
7213 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7214 IS_AMOEBOID(Feld[move_x][move_y]) ||
7215 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7218 boolean can_turn_left =
7219 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7220 IS_AMOEBOID(Feld[left_x][left_y])));
7222 boolean can_turn_right =
7223 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7224 IS_AMOEBOID(Feld[right_x][right_y])));
7226 if (can_turn_left && can_turn_right)
7227 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7228 else if (can_turn_left)
7229 MovDir[x][y] = left_dir;
7231 MovDir[x][y] = right_dir;
7234 if (MovDir[x][y] != old_move_dir)
7237 else if (element == EL_BALLOON)
7239 MovDir[x][y] = game.wind_direction;
7242 else if (element == EL_SPRING)
7244 #if USE_NEW_SPRING_BUMPER
7245 if (MovDir[x][y] & MV_HORIZONTAL)
7247 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7248 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7250 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7251 ResetGfxAnimation(move_x, move_y);
7252 TEST_DrawLevelField(move_x, move_y);
7254 MovDir[x][y] = back_dir;
7256 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7257 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7258 MovDir[x][y] = MV_NONE;
7261 if (MovDir[x][y] & MV_HORIZONTAL &&
7262 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7263 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7264 MovDir[x][y] = MV_NONE;
7269 else if (element == EL_ROBOT ||
7270 element == EL_SATELLITE ||
7271 element == EL_PENGUIN ||
7272 element == EL_EMC_ANDROID)
7274 int attr_x = -1, attr_y = -1;
7285 for (i = 0; i < MAX_PLAYERS; i++)
7287 struct PlayerInfo *player = &stored_player[i];
7288 int jx = player->jx, jy = player->jy;
7290 if (!player->active)
7294 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7302 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7303 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7304 game.engine_version < VERSION_IDENT(3,1,0,0)))
7310 if (element == EL_PENGUIN)
7313 static int xy[4][2] =
7321 for (i = 0; i < NUM_DIRECTIONS; i++)
7323 int ex = x + xy[i][0];
7324 int ey = y + xy[i][1];
7326 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7327 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7328 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7329 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7338 MovDir[x][y] = MV_NONE;
7340 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7341 else if (attr_x > x)
7342 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7344 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7345 else if (attr_y > y)
7346 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7348 if (element == EL_ROBOT)
7352 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7353 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7354 Moving2Blocked(x, y, &newx, &newy);
7356 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7357 MovDelay[x][y] = 8 + 8 * !RND(3);
7359 MovDelay[x][y] = 16;
7361 else if (element == EL_PENGUIN)
7367 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7369 boolean first_horiz = RND(2);
7370 int new_move_dir = MovDir[x][y];
7373 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7374 Moving2Blocked(x, y, &newx, &newy);
7376 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7380 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7381 Moving2Blocked(x, y, &newx, &newy);
7383 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7386 MovDir[x][y] = old_move_dir;
7390 else if (element == EL_SATELLITE)
7396 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7398 boolean first_horiz = RND(2);
7399 int new_move_dir = MovDir[x][y];
7402 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7403 Moving2Blocked(x, y, &newx, &newy);
7405 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7409 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7410 Moving2Blocked(x, y, &newx, &newy);
7412 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7415 MovDir[x][y] = old_move_dir;
7419 else if (element == EL_EMC_ANDROID)
7421 static int check_pos[16] =
7423 -1, /* 0 => (invalid) */
7424 7, /* 1 => MV_LEFT */
7425 3, /* 2 => MV_RIGHT */
7426 -1, /* 3 => (invalid) */
7428 0, /* 5 => MV_LEFT | MV_UP */
7429 2, /* 6 => MV_RIGHT | MV_UP */
7430 -1, /* 7 => (invalid) */
7431 5, /* 8 => MV_DOWN */
7432 6, /* 9 => MV_LEFT | MV_DOWN */
7433 4, /* 10 => MV_RIGHT | MV_DOWN */
7434 -1, /* 11 => (invalid) */
7435 -1, /* 12 => (invalid) */
7436 -1, /* 13 => (invalid) */
7437 -1, /* 14 => (invalid) */
7438 -1, /* 15 => (invalid) */
7446 { -1, -1, MV_LEFT | MV_UP },
7448 { +1, -1, MV_RIGHT | MV_UP },
7449 { +1, 0, MV_RIGHT },
7450 { +1, +1, MV_RIGHT | MV_DOWN },
7452 { -1, +1, MV_LEFT | MV_DOWN },
7455 int start_pos, check_order;
7456 boolean can_clone = FALSE;
7459 /* check if there is any free field around current position */
7460 for (i = 0; i < 8; i++)
7462 int newx = x + check_xy[i].dx;
7463 int newy = y + check_xy[i].dy;
7465 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7473 if (can_clone) /* randomly find an element to clone */
7477 start_pos = check_pos[RND(8)];
7478 check_order = (RND(2) ? -1 : +1);
7480 for (i = 0; i < 8; i++)
7482 int pos_raw = start_pos + i * check_order;
7483 int pos = (pos_raw + 8) % 8;
7484 int newx = x + check_xy[pos].dx;
7485 int newy = y + check_xy[pos].dy;
7487 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7489 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7490 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7492 Store[x][y] = Feld[newx][newy];
7501 if (can_clone) /* randomly find a direction to move */
7505 start_pos = check_pos[RND(8)];
7506 check_order = (RND(2) ? -1 : +1);
7508 for (i = 0; i < 8; i++)
7510 int pos_raw = start_pos + i * check_order;
7511 int pos = (pos_raw + 8) % 8;
7512 int newx = x + check_xy[pos].dx;
7513 int newy = y + check_xy[pos].dy;
7514 int new_move_dir = check_xy[pos].dir;
7516 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7518 MovDir[x][y] = new_move_dir;
7519 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7528 if (can_clone) /* cloning and moving successful */
7531 /* cannot clone -- try to move towards player */
7533 start_pos = check_pos[MovDir[x][y] & 0x0f];
7534 check_order = (RND(2) ? -1 : +1);
7536 for (i = 0; i < 3; i++)
7538 /* first check start_pos, then previous/next or (next/previous) pos */
7539 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7540 int pos = (pos_raw + 8) % 8;
7541 int newx = x + check_xy[pos].dx;
7542 int newy = y + check_xy[pos].dy;
7543 int new_move_dir = check_xy[pos].dir;
7545 if (IS_PLAYER(newx, newy))
7548 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7550 MovDir[x][y] = new_move_dir;
7551 MovDelay[x][y] = level.android_move_time * 8 + 1;
7558 else if (move_pattern == MV_TURNING_LEFT ||
7559 move_pattern == MV_TURNING_RIGHT ||
7560 move_pattern == MV_TURNING_LEFT_RIGHT ||
7561 move_pattern == MV_TURNING_RIGHT_LEFT ||
7562 move_pattern == MV_TURNING_RANDOM ||
7563 move_pattern == MV_ALL_DIRECTIONS)
7565 boolean can_turn_left =
7566 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7567 boolean can_turn_right =
7568 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7570 if (element_info[element].move_stepsize == 0) /* "not moving" */
7573 if (move_pattern == MV_TURNING_LEFT)
7574 MovDir[x][y] = left_dir;
7575 else if (move_pattern == MV_TURNING_RIGHT)
7576 MovDir[x][y] = right_dir;
7577 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7578 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7579 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7580 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7581 else if (move_pattern == MV_TURNING_RANDOM)
7582 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7583 can_turn_right && !can_turn_left ? right_dir :
7584 RND(2) ? left_dir : right_dir);
7585 else if (can_turn_left && can_turn_right)
7586 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7587 else if (can_turn_left)
7588 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7589 else if (can_turn_right)
7590 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7592 MovDir[x][y] = back_dir;
7594 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7596 else if (move_pattern == MV_HORIZONTAL ||
7597 move_pattern == MV_VERTICAL)
7599 if (move_pattern & old_move_dir)
7600 MovDir[x][y] = back_dir;
7601 else if (move_pattern == MV_HORIZONTAL)
7602 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7603 else if (move_pattern == MV_VERTICAL)
7604 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7606 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7608 else if (move_pattern & MV_ANY_DIRECTION)
7610 MovDir[x][y] = move_pattern;
7611 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7613 else if (move_pattern & MV_WIND_DIRECTION)
7615 MovDir[x][y] = game.wind_direction;
7616 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7618 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7620 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7621 MovDir[x][y] = left_dir;
7622 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7623 MovDir[x][y] = right_dir;
7625 if (MovDir[x][y] != old_move_dir)
7626 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7628 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7630 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7631 MovDir[x][y] = right_dir;
7632 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7633 MovDir[x][y] = left_dir;
7635 if (MovDir[x][y] != old_move_dir)
7636 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7638 else if (move_pattern == MV_TOWARDS_PLAYER ||
7639 move_pattern == MV_AWAY_FROM_PLAYER)
7641 int attr_x = -1, attr_y = -1;
7643 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7654 for (i = 0; i < MAX_PLAYERS; i++)
7656 struct PlayerInfo *player = &stored_player[i];
7657 int jx = player->jx, jy = player->jy;
7659 if (!player->active)
7663 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7671 MovDir[x][y] = MV_NONE;
7673 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7674 else if (attr_x > x)
7675 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7677 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7678 else if (attr_y > y)
7679 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7681 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7683 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7685 boolean first_horiz = RND(2);
7686 int new_move_dir = MovDir[x][y];
7688 if (element_info[element].move_stepsize == 0) /* "not moving" */
7690 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7691 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7697 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7698 Moving2Blocked(x, y, &newx, &newy);
7700 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7704 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7705 Moving2Blocked(x, y, &newx, &newy);
7707 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7710 MovDir[x][y] = old_move_dir;
7713 else if (move_pattern == MV_WHEN_PUSHED ||
7714 move_pattern == MV_WHEN_DROPPED)
7716 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7717 MovDir[x][y] = MV_NONE;
7721 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7723 static int test_xy[7][2] =
7733 static int test_dir[7] =
7743 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7744 int move_preference = -1000000; /* start with very low preference */
7745 int new_move_dir = MV_NONE;
7746 int start_test = RND(4);
7749 for (i = 0; i < NUM_DIRECTIONS; i++)
7751 int move_dir = test_dir[start_test + i];
7752 int move_dir_preference;
7754 xx = x + test_xy[start_test + i][0];
7755 yy = y + test_xy[start_test + i][1];
7757 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7758 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7760 new_move_dir = move_dir;
7765 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7768 move_dir_preference = -1 * RunnerVisit[xx][yy];
7769 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7770 move_dir_preference = PlayerVisit[xx][yy];
7772 if (move_dir_preference > move_preference)
7774 /* prefer field that has not been visited for the longest time */
7775 move_preference = move_dir_preference;
7776 new_move_dir = move_dir;
7778 else if (move_dir_preference == move_preference &&
7779 move_dir == old_move_dir)
7781 /* prefer last direction when all directions are preferred equally */
7782 move_preference = move_dir_preference;
7783 new_move_dir = move_dir;
7787 MovDir[x][y] = new_move_dir;
7788 if (old_move_dir != new_move_dir)
7789 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7793 static void TurnRound(int x, int y)
7795 int direction = MovDir[x][y];
7799 GfxDir[x][y] = MovDir[x][y];
7801 if (direction != MovDir[x][y])
7805 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7807 ResetGfxFrame(x, y, FALSE);
7810 static boolean JustBeingPushed(int x, int y)
7814 for (i = 0; i < MAX_PLAYERS; i++)
7816 struct PlayerInfo *player = &stored_player[i];
7818 if (player->active && player->is_pushing && player->MovPos)
7820 int next_jx = player->jx + (player->jx - player->last_jx);
7821 int next_jy = player->jy + (player->jy - player->last_jy);
7823 if (x == next_jx && y == next_jy)
7831 void StartMoving(int x, int y)
7833 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7834 int element = Feld[x][y];
7839 if (MovDelay[x][y] == 0)
7840 GfxAction[x][y] = ACTION_DEFAULT;
7842 if (CAN_FALL(element) && y < lev_fieldy - 1)
7844 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7845 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7846 if (JustBeingPushed(x, y))
7849 if (element == EL_QUICKSAND_FULL)
7851 if (IS_FREE(x, y + 1))
7853 InitMovingField(x, y, MV_DOWN);
7854 started_moving = TRUE;
7856 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7857 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7858 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7859 Store[x][y] = EL_ROCK;
7861 Store[x][y] = EL_ROCK;
7864 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7866 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7868 if (!MovDelay[x][y])
7870 MovDelay[x][y] = TILEY + 1;
7872 ResetGfxAnimation(x, y);
7873 ResetGfxAnimation(x, y + 1);
7878 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7879 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7886 Feld[x][y] = EL_QUICKSAND_EMPTY;
7887 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7888 Store[x][y + 1] = Store[x][y];
7891 PlayLevelSoundAction(x, y, ACTION_FILLING);
7893 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7895 if (!MovDelay[x][y])
7897 MovDelay[x][y] = TILEY + 1;
7899 ResetGfxAnimation(x, y);
7900 ResetGfxAnimation(x, y + 1);
7905 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7906 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7913 Feld[x][y] = EL_QUICKSAND_EMPTY;
7914 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7915 Store[x][y + 1] = Store[x][y];
7918 PlayLevelSoundAction(x, y, ACTION_FILLING);
7921 else if (element == EL_QUICKSAND_FAST_FULL)
7923 if (IS_FREE(x, y + 1))
7925 InitMovingField(x, y, MV_DOWN);
7926 started_moving = TRUE;
7928 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7929 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7930 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7931 Store[x][y] = EL_ROCK;
7933 Store[x][y] = EL_ROCK;
7936 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7938 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7940 if (!MovDelay[x][y])
7942 MovDelay[x][y] = TILEY + 1;
7944 ResetGfxAnimation(x, y);
7945 ResetGfxAnimation(x, y + 1);
7950 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7951 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7958 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7959 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7960 Store[x][y + 1] = Store[x][y];
7963 PlayLevelSoundAction(x, y, ACTION_FILLING);
7965 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7967 if (!MovDelay[x][y])
7969 MovDelay[x][y] = TILEY + 1;
7971 ResetGfxAnimation(x, y);
7972 ResetGfxAnimation(x, y + 1);
7977 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7978 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7985 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7986 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7987 Store[x][y + 1] = Store[x][y];
7990 PlayLevelSoundAction(x, y, ACTION_FILLING);
7993 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7994 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7996 InitMovingField(x, y, MV_DOWN);
7997 started_moving = TRUE;
7999 Feld[x][y] = EL_QUICKSAND_FILLING;
8000 Store[x][y] = element;
8002 PlayLevelSoundAction(x, y, ACTION_FILLING);
8004 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8005 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8007 InitMovingField(x, y, MV_DOWN);
8008 started_moving = TRUE;
8010 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8011 Store[x][y] = element;
8013 PlayLevelSoundAction(x, y, ACTION_FILLING);
8015 else if (element == EL_MAGIC_WALL_FULL)
8017 if (IS_FREE(x, y + 1))
8019 InitMovingField(x, y, MV_DOWN);
8020 started_moving = TRUE;
8022 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8023 Store[x][y] = EL_CHANGED(Store[x][y]);
8025 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8027 if (!MovDelay[x][y])
8028 MovDelay[x][y] = TILEY/4 + 1;
8037 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8038 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8039 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8043 else if (element == EL_BD_MAGIC_WALL_FULL)
8045 if (IS_FREE(x, y + 1))
8047 InitMovingField(x, y, MV_DOWN);
8048 started_moving = TRUE;
8050 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8051 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8053 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8055 if (!MovDelay[x][y])
8056 MovDelay[x][y] = TILEY/4 + 1;
8065 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8066 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8067 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8071 else if (element == EL_DC_MAGIC_WALL_FULL)
8073 if (IS_FREE(x, y + 1))
8075 InitMovingField(x, y, MV_DOWN);
8076 started_moving = TRUE;
8078 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8079 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8081 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8083 if (!MovDelay[x][y])
8084 MovDelay[x][y] = TILEY/4 + 1;
8093 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8094 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8095 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8099 else if ((CAN_PASS_MAGIC_WALL(element) &&
8100 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8101 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8102 (CAN_PASS_DC_MAGIC_WALL(element) &&
8103 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8106 InitMovingField(x, y, MV_DOWN);
8107 started_moving = TRUE;
8110 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8111 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8112 EL_DC_MAGIC_WALL_FILLING);
8113 Store[x][y] = element;
8115 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8117 SplashAcid(x, y + 1);
8119 InitMovingField(x, y, MV_DOWN);
8120 started_moving = TRUE;
8122 Store[x][y] = EL_ACID;
8125 #if USE_FIX_IMPACT_COLLISION
8126 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8127 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8129 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8130 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8132 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8133 CAN_FALL(element) && WasJustFalling[x][y] &&
8134 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8136 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8137 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8138 (Feld[x][y + 1] == EL_BLOCKED)))
8140 /* this is needed for a special case not covered by calling "Impact()"
8141 from "ContinueMoving()": if an element moves to a tile directly below
8142 another element which was just falling on that tile (which was empty
8143 in the previous frame), the falling element above would just stop
8144 instead of smashing the element below (in previous version, the above
8145 element was just checked for "moving" instead of "falling", resulting
8146 in incorrect smashes caused by horizontal movement of the above
8147 element; also, the case of the player being the element to smash was
8148 simply not covered here... :-/ ) */
8150 CheckCollision[x][y] = 0;
8151 CheckImpact[x][y] = 0;
8155 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8157 if (MovDir[x][y] == MV_NONE)
8159 InitMovingField(x, y, MV_DOWN);
8160 started_moving = TRUE;
8163 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8165 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8166 MovDir[x][y] = MV_DOWN;
8168 InitMovingField(x, y, MV_DOWN);
8169 started_moving = TRUE;
8171 else if (element == EL_AMOEBA_DROP)
8173 Feld[x][y] = EL_AMOEBA_GROWING;
8174 Store[x][y] = EL_AMOEBA_WET;
8176 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8177 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8178 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8179 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8181 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8182 (IS_FREE(x - 1, y + 1) ||
8183 Feld[x - 1][y + 1] == EL_ACID));
8184 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8185 (IS_FREE(x + 1, y + 1) ||
8186 Feld[x + 1][y + 1] == EL_ACID));
8187 boolean can_fall_any = (can_fall_left || can_fall_right);
8188 boolean can_fall_both = (can_fall_left && can_fall_right);
8189 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8191 #if USE_NEW_ALL_SLIPPERY
8192 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8194 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8195 can_fall_right = FALSE;
8196 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8197 can_fall_left = FALSE;
8198 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8199 can_fall_right = FALSE;
8200 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8201 can_fall_left = FALSE;
8203 can_fall_any = (can_fall_left || can_fall_right);
8204 can_fall_both = FALSE;
8207 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8209 if (slippery_type == SLIPPERY_ONLY_LEFT)
8210 can_fall_right = FALSE;
8211 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8212 can_fall_left = FALSE;
8213 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8214 can_fall_right = FALSE;
8215 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8216 can_fall_left = FALSE;
8218 can_fall_any = (can_fall_left || can_fall_right);
8219 can_fall_both = (can_fall_left && can_fall_right);
8223 #if USE_NEW_ALL_SLIPPERY
8225 #if USE_NEW_SP_SLIPPERY
8226 /* !!! better use the same properties as for custom elements here !!! */
8227 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8228 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8230 can_fall_right = FALSE; /* slip down on left side */
8231 can_fall_both = FALSE;
8236 #if USE_NEW_ALL_SLIPPERY
8239 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8240 can_fall_right = FALSE; /* slip down on left side */
8242 can_fall_left = !(can_fall_right = RND(2));
8244 can_fall_both = FALSE;
8249 if (game.emulation == EMU_BOULDERDASH ||
8250 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8251 can_fall_right = FALSE; /* slip down on left side */
8253 can_fall_left = !(can_fall_right = RND(2));
8255 can_fall_both = FALSE;
8261 /* if not determined otherwise, prefer left side for slipping down */
8262 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8263 started_moving = TRUE;
8267 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8269 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8272 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8273 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8274 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8275 int belt_dir = game.belt_dir[belt_nr];
8277 if ((belt_dir == MV_LEFT && left_is_free) ||
8278 (belt_dir == MV_RIGHT && right_is_free))
8280 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8282 InitMovingField(x, y, belt_dir);
8283 started_moving = TRUE;
8285 Pushed[x][y] = TRUE;
8286 Pushed[nextx][y] = TRUE;
8288 GfxAction[x][y] = ACTION_DEFAULT;
8292 MovDir[x][y] = 0; /* if element was moving, stop it */
8297 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8299 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8301 if (CAN_MOVE(element) && !started_moving)
8304 int move_pattern = element_info[element].move_pattern;
8309 if (MovDir[x][y] == MV_NONE)
8311 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8312 x, y, element, element_info[element].token_name);
8313 printf("StartMoving(): This should never happen!\n");
8318 Moving2Blocked(x, y, &newx, &newy);
8320 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8323 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8324 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8326 WasJustMoving[x][y] = 0;
8327 CheckCollision[x][y] = 0;
8329 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8331 if (Feld[x][y] != element) /* element has changed */
8335 if (!MovDelay[x][y]) /* start new movement phase */
8337 /* all objects that can change their move direction after each step
8338 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8340 if (element != EL_YAMYAM &&
8341 element != EL_DARK_YAMYAM &&
8342 element != EL_PACMAN &&
8343 !(move_pattern & MV_ANY_DIRECTION) &&
8344 move_pattern != MV_TURNING_LEFT &&
8345 move_pattern != MV_TURNING_RIGHT &&
8346 move_pattern != MV_TURNING_LEFT_RIGHT &&
8347 move_pattern != MV_TURNING_RIGHT_LEFT &&
8348 move_pattern != MV_TURNING_RANDOM)
8352 if (MovDelay[x][y] && (element == EL_BUG ||
8353 element == EL_SPACESHIP ||
8354 element == EL_SP_SNIKSNAK ||
8355 element == EL_SP_ELECTRON ||
8356 element == EL_MOLE))
8357 TEST_DrawLevelField(x, y);
8361 if (MovDelay[x][y]) /* wait some time before next movement */
8365 if (element == EL_ROBOT ||
8366 element == EL_YAMYAM ||
8367 element == EL_DARK_YAMYAM)
8369 DrawLevelElementAnimationIfNeeded(x, y, element);
8370 PlayLevelSoundAction(x, y, ACTION_WAITING);
8372 else if (element == EL_SP_ELECTRON)
8373 DrawLevelElementAnimationIfNeeded(x, y, element);
8374 else if (element == EL_DRAGON)
8377 int dir = MovDir[x][y];
8378 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8379 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8380 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8381 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8382 dir == MV_UP ? IMG_FLAMES_1_UP :
8383 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8384 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8386 GfxAction[x][y] = ACTION_ATTACKING;
8388 if (IS_PLAYER(x, y))
8389 DrawPlayerField(x, y);
8391 TEST_DrawLevelField(x, y);
8393 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8395 for (i = 1; i <= 3; i++)
8397 int xx = x + i * dx;
8398 int yy = y + i * dy;
8399 int sx = SCREENX(xx);
8400 int sy = SCREENY(yy);
8401 int flame_graphic = graphic + (i - 1);
8403 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8408 int flamed = MovingOrBlocked2Element(xx, yy);
8412 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8414 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8415 RemoveMovingField(xx, yy);
8417 RemoveField(xx, yy);
8419 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8422 RemoveMovingField(xx, yy);
8425 ChangeDelay[xx][yy] = 0;
8427 Feld[xx][yy] = EL_FLAMES;
8429 if (IN_SCR_FIELD(sx, sy))
8431 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8432 DrawGraphic(sx, sy, flame_graphic, frame);
8437 if (Feld[xx][yy] == EL_FLAMES)
8438 Feld[xx][yy] = EL_EMPTY;
8439 TEST_DrawLevelField(xx, yy);
8444 if (MovDelay[x][y]) /* element still has to wait some time */
8446 PlayLevelSoundAction(x, y, ACTION_WAITING);
8452 /* now make next step */
8454 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8456 if (DONT_COLLIDE_WITH(element) &&
8457 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8458 !PLAYER_ENEMY_PROTECTED(newx, newy))
8460 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8465 else if (CAN_MOVE_INTO_ACID(element) &&
8466 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8467 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8468 (MovDir[x][y] == MV_DOWN ||
8469 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8471 SplashAcid(newx, newy);
8472 Store[x][y] = EL_ACID;
8474 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8476 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8477 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8478 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8479 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8482 TEST_DrawLevelField(x, y);
8484 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8485 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8486 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8488 local_player->friends_still_needed--;
8489 if (!local_player->friends_still_needed &&
8490 !local_player->GameOver && AllPlayersGone)
8491 PlayerWins(local_player);
8495 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8497 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8498 TEST_DrawLevelField(newx, newy);
8500 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8502 else if (!IS_FREE(newx, newy))
8504 GfxAction[x][y] = ACTION_WAITING;
8506 if (IS_PLAYER(x, y))
8507 DrawPlayerField(x, y);
8509 TEST_DrawLevelField(x, y);
8514 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8516 if (IS_FOOD_PIG(Feld[newx][newy]))
8518 if (IS_MOVING(newx, newy))
8519 RemoveMovingField(newx, newy);
8522 Feld[newx][newy] = EL_EMPTY;
8523 TEST_DrawLevelField(newx, newy);
8526 PlayLevelSound(x, y, SND_PIG_DIGGING);
8528 else if (!IS_FREE(newx, newy))
8530 if (IS_PLAYER(x, y))
8531 DrawPlayerField(x, y);
8533 TEST_DrawLevelField(x, y);
8538 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8540 if (Store[x][y] != EL_EMPTY)
8542 boolean can_clone = FALSE;
8545 /* check if element to clone is still there */
8546 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8548 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8556 /* cannot clone or target field not free anymore -- do not clone */
8557 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8558 Store[x][y] = EL_EMPTY;
8561 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8563 if (IS_MV_DIAGONAL(MovDir[x][y]))
8565 int diagonal_move_dir = MovDir[x][y];
8566 int stored = Store[x][y];
8567 int change_delay = 8;
8570 /* android is moving diagonally */
8572 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8574 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8575 GfxElement[x][y] = EL_EMC_ANDROID;
8576 GfxAction[x][y] = ACTION_SHRINKING;
8577 GfxDir[x][y] = diagonal_move_dir;
8578 ChangeDelay[x][y] = change_delay;
8580 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8583 DrawLevelGraphicAnimation(x, y, graphic);
8584 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8586 if (Feld[newx][newy] == EL_ACID)
8588 SplashAcid(newx, newy);
8593 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8595 Store[newx][newy] = EL_EMC_ANDROID;
8596 GfxElement[newx][newy] = EL_EMC_ANDROID;
8597 GfxAction[newx][newy] = ACTION_GROWING;
8598 GfxDir[newx][newy] = diagonal_move_dir;
8599 ChangeDelay[newx][newy] = change_delay;
8601 graphic = el_act_dir2img(GfxElement[newx][newy],
8602 GfxAction[newx][newy], GfxDir[newx][newy]);
8604 DrawLevelGraphicAnimation(newx, newy, graphic);
8605 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8611 Feld[newx][newy] = EL_EMPTY;
8612 TEST_DrawLevelField(newx, newy);
8614 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8617 else if (!IS_FREE(newx, newy))
8620 if (IS_PLAYER(x, y))
8621 DrawPlayerField(x, y);
8623 TEST_DrawLevelField(x, y);
8629 else if (IS_CUSTOM_ELEMENT(element) &&
8630 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8633 if (!DigFieldByCE(newx, newy, element))
8636 int new_element = Feld[newx][newy];
8638 if (!IS_FREE(newx, newy))
8640 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8641 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8644 /* no element can dig solid indestructible elements */
8645 if (IS_INDESTRUCTIBLE(new_element) &&
8646 !IS_DIGGABLE(new_element) &&
8647 !IS_COLLECTIBLE(new_element))
8650 if (AmoebaNr[newx][newy] &&
8651 (new_element == EL_AMOEBA_FULL ||
8652 new_element == EL_BD_AMOEBA ||
8653 new_element == EL_AMOEBA_GROWING))
8655 AmoebaCnt[AmoebaNr[newx][newy]]--;
8656 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8659 if (IS_MOVING(newx, newy))
8660 RemoveMovingField(newx, newy);
8663 RemoveField(newx, newy);
8664 TEST_DrawLevelField(newx, newy);
8667 /* if digged element was about to explode, prevent the explosion */
8668 ExplodeField[newx][newy] = EX_TYPE_NONE;
8670 PlayLevelSoundAction(x, y, action);
8673 Store[newx][newy] = EL_EMPTY;
8676 /* this makes it possible to leave the removed element again */
8677 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8678 Store[newx][newy] = new_element;
8680 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8682 int move_leave_element = element_info[element].move_leave_element;
8684 /* this makes it possible to leave the removed element again */
8685 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8686 new_element : move_leave_element);
8692 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8694 RunnerVisit[x][y] = FrameCounter;
8695 PlayerVisit[x][y] /= 8; /* expire player visit path */
8698 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8700 if (!IS_FREE(newx, newy))
8702 if (IS_PLAYER(x, y))
8703 DrawPlayerField(x, y);
8705 TEST_DrawLevelField(x, y);
8711 boolean wanna_flame = !RND(10);
8712 int dx = newx - x, dy = newy - y;
8713 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8714 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8715 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8716 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8717 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8718 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8721 IS_CLASSIC_ENEMY(element1) ||
8722 IS_CLASSIC_ENEMY(element2)) &&
8723 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8724 element1 != EL_FLAMES && element2 != EL_FLAMES)
8726 ResetGfxAnimation(x, y);
8727 GfxAction[x][y] = ACTION_ATTACKING;
8729 if (IS_PLAYER(x, y))
8730 DrawPlayerField(x, y);
8732 TEST_DrawLevelField(x, y);
8734 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8736 MovDelay[x][y] = 50;
8740 RemoveField(newx, newy);
8742 Feld[newx][newy] = EL_FLAMES;
8743 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8746 RemoveField(newx1, newy1);
8748 Feld[newx1][newy1] = EL_FLAMES;
8750 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8753 RemoveField(newx2, newy2);
8755 Feld[newx2][newy2] = EL_FLAMES;
8762 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8763 Feld[newx][newy] == EL_DIAMOND)
8765 if (IS_MOVING(newx, newy))
8766 RemoveMovingField(newx, newy);
8769 Feld[newx][newy] = EL_EMPTY;
8770 TEST_DrawLevelField(newx, newy);
8773 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8775 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8776 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8778 if (AmoebaNr[newx][newy])
8780 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8781 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8782 Feld[newx][newy] == EL_BD_AMOEBA)
8783 AmoebaCnt[AmoebaNr[newx][newy]]--;
8788 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8790 RemoveMovingField(newx, newy);
8793 if (IS_MOVING(newx, newy))
8795 RemoveMovingField(newx, newy);
8800 Feld[newx][newy] = EL_EMPTY;
8801 TEST_DrawLevelField(newx, newy);
8804 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8806 else if ((element == EL_PACMAN || element == EL_MOLE)
8807 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8809 if (AmoebaNr[newx][newy])
8811 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8812 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8813 Feld[newx][newy] == EL_BD_AMOEBA)
8814 AmoebaCnt[AmoebaNr[newx][newy]]--;
8817 if (element == EL_MOLE)
8819 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8820 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8822 ResetGfxAnimation(x, y);
8823 GfxAction[x][y] = ACTION_DIGGING;
8824 TEST_DrawLevelField(x, y);
8826 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8828 return; /* wait for shrinking amoeba */
8830 else /* element == EL_PACMAN */
8832 Feld[newx][newy] = EL_EMPTY;
8833 TEST_DrawLevelField(newx, newy);
8834 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8837 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8838 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8839 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8841 /* wait for shrinking amoeba to completely disappear */
8844 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8846 /* object was running against a wall */
8851 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8852 if (move_pattern & MV_ANY_DIRECTION &&
8853 move_pattern == MovDir[x][y])
8855 int blocking_element =
8856 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8858 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8861 element = Feld[x][y]; /* element might have changed */
8865 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8866 DrawLevelElementAnimation(x, y, element);
8868 if (DONT_TOUCH(element))
8869 TestIfBadThingTouchesPlayer(x, y);
8874 InitMovingField(x, y, MovDir[x][y]);
8876 PlayLevelSoundAction(x, y, ACTION_MOVING);
8880 ContinueMoving(x, y);
8883 void ContinueMoving(int x, int y)
8885 int element = Feld[x][y];
8886 struct ElementInfo *ei = &element_info[element];
8887 int direction = MovDir[x][y];
8888 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8889 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8890 int newx = x + dx, newy = y + dy;
8891 int stored = Store[x][y];
8892 int stored_new = Store[newx][newy];
8893 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8894 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8895 boolean last_line = (newy == lev_fieldy - 1);
8897 MovPos[x][y] += getElementMoveStepsize(x, y);
8899 if (pushed_by_player) /* special case: moving object pushed by player */
8900 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8902 if (ABS(MovPos[x][y]) < TILEX)
8905 int ee = Feld[x][y];
8906 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8907 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8909 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8910 x, y, ABS(MovPos[x][y]),
8912 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8915 TEST_DrawLevelField(x, y);
8917 return; /* element is still moving */
8920 /* element reached destination field */
8922 Feld[x][y] = EL_EMPTY;
8923 Feld[newx][newy] = element;
8924 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8926 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8928 element = Feld[newx][newy] = EL_ACID;
8930 else if (element == EL_MOLE)
8932 Feld[x][y] = EL_SAND;
8934 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8936 else if (element == EL_QUICKSAND_FILLING)
8938 element = Feld[newx][newy] = get_next_element(element);
8939 Store[newx][newy] = Store[x][y];
8941 else if (element == EL_QUICKSAND_EMPTYING)
8943 Feld[x][y] = get_next_element(element);
8944 element = Feld[newx][newy] = Store[x][y];
8946 else if (element == EL_QUICKSAND_FAST_FILLING)
8948 element = Feld[newx][newy] = get_next_element(element);
8949 Store[newx][newy] = Store[x][y];
8951 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8953 Feld[x][y] = get_next_element(element);
8954 element = Feld[newx][newy] = Store[x][y];
8956 else if (element == EL_MAGIC_WALL_FILLING)
8958 element = Feld[newx][newy] = get_next_element(element);
8959 if (!game.magic_wall_active)
8960 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8961 Store[newx][newy] = Store[x][y];
8963 else if (element == EL_MAGIC_WALL_EMPTYING)
8965 Feld[x][y] = get_next_element(element);
8966 if (!game.magic_wall_active)
8967 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8968 element = Feld[newx][newy] = Store[x][y];
8970 #if USE_NEW_CUSTOM_VALUE
8971 InitField(newx, newy, FALSE);
8974 else if (element == EL_BD_MAGIC_WALL_FILLING)
8976 element = Feld[newx][newy] = get_next_element(element);
8977 if (!game.magic_wall_active)
8978 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8979 Store[newx][newy] = Store[x][y];
8981 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8983 Feld[x][y] = get_next_element(element);
8984 if (!game.magic_wall_active)
8985 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8986 element = Feld[newx][newy] = Store[x][y];
8988 #if USE_NEW_CUSTOM_VALUE
8989 InitField(newx, newy, FALSE);
8992 else if (element == EL_DC_MAGIC_WALL_FILLING)
8994 element = Feld[newx][newy] = get_next_element(element);
8995 if (!game.magic_wall_active)
8996 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8997 Store[newx][newy] = Store[x][y];
8999 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9001 Feld[x][y] = get_next_element(element);
9002 if (!game.magic_wall_active)
9003 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9004 element = Feld[newx][newy] = Store[x][y];
9006 #if USE_NEW_CUSTOM_VALUE
9007 InitField(newx, newy, FALSE);
9010 else if (element == EL_AMOEBA_DROPPING)
9012 Feld[x][y] = get_next_element(element);
9013 element = Feld[newx][newy] = Store[x][y];
9015 else if (element == EL_SOKOBAN_OBJECT)
9018 Feld[x][y] = Back[x][y];
9020 if (Back[newx][newy])
9021 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9023 Back[x][y] = Back[newx][newy] = 0;
9026 Store[x][y] = EL_EMPTY;
9031 MovDelay[newx][newy] = 0;
9033 if (CAN_CHANGE_OR_HAS_ACTION(element))
9035 /* copy element change control values to new field */
9036 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9037 ChangePage[newx][newy] = ChangePage[x][y];
9038 ChangeCount[newx][newy] = ChangeCount[x][y];
9039 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9042 #if USE_NEW_CUSTOM_VALUE
9043 CustomValue[newx][newy] = CustomValue[x][y];
9046 ChangeDelay[x][y] = 0;
9047 ChangePage[x][y] = -1;
9048 ChangeCount[x][y] = 0;
9049 ChangeEvent[x][y] = -1;
9051 #if USE_NEW_CUSTOM_VALUE
9052 CustomValue[x][y] = 0;
9055 /* copy animation control values to new field */
9056 GfxFrame[newx][newy] = GfxFrame[x][y];
9057 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9058 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9059 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9061 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9063 /* some elements can leave other elements behind after moving */
9065 if (ei->move_leave_element != EL_EMPTY &&
9066 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9067 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9069 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9070 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9071 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9074 int move_leave_element = ei->move_leave_element;
9078 /* this makes it possible to leave the removed element again */
9079 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9080 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9082 /* this makes it possible to leave the removed element again */
9083 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9084 move_leave_element = stored;
9087 /* this makes it possible to leave the removed element again */
9088 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9089 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9090 move_leave_element = stored;
9093 Feld[x][y] = move_leave_element;
9095 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9096 MovDir[x][y] = direction;
9098 InitField(x, y, FALSE);
9100 if (GFX_CRUMBLED(Feld[x][y]))
9101 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9103 if (ELEM_IS_PLAYER(move_leave_element))
9104 RelocatePlayer(x, y, move_leave_element);
9107 /* do this after checking for left-behind element */
9108 ResetGfxAnimation(x, y); /* reset animation values for old field */
9110 if (!CAN_MOVE(element) ||
9111 (CAN_FALL(element) && direction == MV_DOWN &&
9112 (element == EL_SPRING ||
9113 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9114 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9115 GfxDir[x][y] = MovDir[newx][newy] = 0;
9117 TEST_DrawLevelField(x, y);
9118 TEST_DrawLevelField(newx, newy);
9120 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9122 /* prevent pushed element from moving on in pushed direction */
9123 if (pushed_by_player && CAN_MOVE(element) &&
9124 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9125 !(element_info[element].move_pattern & direction))
9126 TurnRound(newx, newy);
9128 /* prevent elements on conveyor belt from moving on in last direction */
9129 if (pushed_by_conveyor && CAN_FALL(element) &&
9130 direction & MV_HORIZONTAL)
9131 MovDir[newx][newy] = 0;
9133 if (!pushed_by_player)
9135 int nextx = newx + dx, nexty = newy + dy;
9136 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9138 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9140 if (CAN_FALL(element) && direction == MV_DOWN)
9141 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9143 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9144 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9146 #if USE_FIX_IMPACT_COLLISION
9147 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9148 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9152 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9154 TestIfBadThingTouchesPlayer(newx, newy);
9155 TestIfBadThingTouchesFriend(newx, newy);
9157 if (!IS_CUSTOM_ELEMENT(element))
9158 TestIfBadThingTouchesOtherBadThing(newx, newy);
9160 else if (element == EL_PENGUIN)
9161 TestIfFriendTouchesBadThing(newx, newy);
9163 if (DONT_GET_HIT_BY(element))
9165 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9168 /* give the player one last chance (one more frame) to move away */
9169 if (CAN_FALL(element) && direction == MV_DOWN &&
9170 (last_line || (!IS_FREE(x, newy + 1) &&
9171 (!IS_PLAYER(x, newy + 1) ||
9172 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9175 if (pushed_by_player && !game.use_change_when_pushing_bug)
9177 int push_side = MV_DIR_OPPOSITE(direction);
9178 struct PlayerInfo *player = PLAYERINFO(x, y);
9180 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9181 player->index_bit, push_side);
9182 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9183 player->index_bit, push_side);
9186 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9187 MovDelay[newx][newy] = 1;
9189 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9191 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9194 if (ChangePage[newx][newy] != -1) /* delayed change */
9196 int page = ChangePage[newx][newy];
9197 struct ElementChangeInfo *change = &ei->change_page[page];
9199 ChangePage[newx][newy] = -1;
9201 if (change->can_change)
9203 if (ChangeElement(newx, newy, element, page))
9205 if (change->post_change_function)
9206 change->post_change_function(newx, newy);
9210 if (change->has_action)
9211 ExecuteCustomElementAction(newx, newy, element, page);
9215 TestIfElementHitsCustomElement(newx, newy, direction);
9216 TestIfPlayerTouchesCustomElement(newx, newy);
9217 TestIfElementTouchesCustomElement(newx, newy);
9219 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9220 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9221 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9222 MV_DIR_OPPOSITE(direction));
9225 int AmoebeNachbarNr(int ax, int ay)
9228 int element = Feld[ax][ay];
9230 static int xy[4][2] =
9238 for (i = 0; i < NUM_DIRECTIONS; i++)
9240 int x = ax + xy[i][0];
9241 int y = ay + xy[i][1];
9243 if (!IN_LEV_FIELD(x, y))
9246 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9247 group_nr = AmoebaNr[x][y];
9253 void AmoebenVereinigen(int ax, int ay)
9255 int i, x, y, xx, yy;
9256 int new_group_nr = AmoebaNr[ax][ay];
9257 static int xy[4][2] =
9265 if (new_group_nr == 0)
9268 for (i = 0; i < NUM_DIRECTIONS; i++)
9273 if (!IN_LEV_FIELD(x, y))
9276 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9277 Feld[x][y] == EL_BD_AMOEBA ||
9278 Feld[x][y] == EL_AMOEBA_DEAD) &&
9279 AmoebaNr[x][y] != new_group_nr)
9281 int old_group_nr = AmoebaNr[x][y];
9283 if (old_group_nr == 0)
9286 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9287 AmoebaCnt[old_group_nr] = 0;
9288 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9289 AmoebaCnt2[old_group_nr] = 0;
9291 SCAN_PLAYFIELD(xx, yy)
9293 if (AmoebaNr[xx][yy] == old_group_nr)
9294 AmoebaNr[xx][yy] = new_group_nr;
9300 void AmoebeUmwandeln(int ax, int ay)
9304 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9306 int group_nr = AmoebaNr[ax][ay];
9311 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9312 printf("AmoebeUmwandeln(): This should never happen!\n");
9317 SCAN_PLAYFIELD(x, y)
9319 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9322 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9326 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9327 SND_AMOEBA_TURNING_TO_GEM :
9328 SND_AMOEBA_TURNING_TO_ROCK));
9333 static int xy[4][2] =
9341 for (i = 0; i < NUM_DIRECTIONS; i++)
9346 if (!IN_LEV_FIELD(x, y))
9349 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9351 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9352 SND_AMOEBA_TURNING_TO_GEM :
9353 SND_AMOEBA_TURNING_TO_ROCK));
9360 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9363 int group_nr = AmoebaNr[ax][ay];
9364 boolean done = FALSE;
9369 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9370 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9375 SCAN_PLAYFIELD(x, y)
9377 if (AmoebaNr[x][y] == group_nr &&
9378 (Feld[x][y] == EL_AMOEBA_DEAD ||
9379 Feld[x][y] == EL_BD_AMOEBA ||
9380 Feld[x][y] == EL_AMOEBA_GROWING))
9383 Feld[x][y] = new_element;
9384 InitField(x, y, FALSE);
9385 TEST_DrawLevelField(x, y);
9391 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9392 SND_BD_AMOEBA_TURNING_TO_ROCK :
9393 SND_BD_AMOEBA_TURNING_TO_GEM));
9396 void AmoebeWaechst(int x, int y)
9398 static unsigned long sound_delay = 0;
9399 static unsigned long sound_delay_value = 0;
9401 if (!MovDelay[x][y]) /* start new growing cycle */
9405 if (DelayReached(&sound_delay, sound_delay_value))
9407 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9408 sound_delay_value = 30;
9412 if (MovDelay[x][y]) /* wait some time before growing bigger */
9415 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9417 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9418 6 - MovDelay[x][y]);
9420 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9423 if (!MovDelay[x][y])
9425 Feld[x][y] = Store[x][y];
9427 TEST_DrawLevelField(x, y);
9432 void AmoebaDisappearing(int x, int y)
9434 static unsigned long sound_delay = 0;
9435 static unsigned long sound_delay_value = 0;
9437 if (!MovDelay[x][y]) /* start new shrinking cycle */
9441 if (DelayReached(&sound_delay, sound_delay_value))
9442 sound_delay_value = 30;
9445 if (MovDelay[x][y]) /* wait some time before shrinking */
9448 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9450 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9451 6 - MovDelay[x][y]);
9453 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9456 if (!MovDelay[x][y])
9458 Feld[x][y] = EL_EMPTY;
9459 TEST_DrawLevelField(x, y);
9461 /* don't let mole enter this field in this cycle;
9462 (give priority to objects falling to this field from above) */
9468 void AmoebeAbleger(int ax, int ay)
9471 int element = Feld[ax][ay];
9472 int graphic = el2img(element);
9473 int newax = ax, neway = ay;
9474 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9475 static int xy[4][2] =
9483 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9485 Feld[ax][ay] = EL_AMOEBA_DEAD;
9486 TEST_DrawLevelField(ax, ay);
9490 if (IS_ANIMATED(graphic))
9491 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9493 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9494 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9496 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9499 if (MovDelay[ax][ay])
9503 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9506 int x = ax + xy[start][0];
9507 int y = ay + xy[start][1];
9509 if (!IN_LEV_FIELD(x, y))
9512 if (IS_FREE(x, y) ||
9513 CAN_GROW_INTO(Feld[x][y]) ||
9514 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9515 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9521 if (newax == ax && neway == ay)
9524 else /* normal or "filled" (BD style) amoeba */
9527 boolean waiting_for_player = FALSE;
9529 for (i = 0; i < NUM_DIRECTIONS; i++)
9531 int j = (start + i) % 4;
9532 int x = ax + xy[j][0];
9533 int y = ay + xy[j][1];
9535 if (!IN_LEV_FIELD(x, y))
9538 if (IS_FREE(x, y) ||
9539 CAN_GROW_INTO(Feld[x][y]) ||
9540 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9541 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9547 else if (IS_PLAYER(x, y))
9548 waiting_for_player = TRUE;
9551 if (newax == ax && neway == ay) /* amoeba cannot grow */
9553 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9555 Feld[ax][ay] = EL_AMOEBA_DEAD;
9556 TEST_DrawLevelField(ax, ay);
9557 AmoebaCnt[AmoebaNr[ax][ay]]--;
9559 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9561 if (element == EL_AMOEBA_FULL)
9562 AmoebeUmwandeln(ax, ay);
9563 else if (element == EL_BD_AMOEBA)
9564 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9569 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9571 /* amoeba gets larger by growing in some direction */
9573 int new_group_nr = AmoebaNr[ax][ay];
9576 if (new_group_nr == 0)
9578 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9579 printf("AmoebeAbleger(): This should never happen!\n");
9584 AmoebaNr[newax][neway] = new_group_nr;
9585 AmoebaCnt[new_group_nr]++;
9586 AmoebaCnt2[new_group_nr]++;
9588 /* if amoeba touches other amoeba(s) after growing, unify them */
9589 AmoebenVereinigen(newax, neway);
9591 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9593 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9599 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9600 (neway == lev_fieldy - 1 && newax != ax))
9602 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9603 Store[newax][neway] = element;
9605 else if (neway == ay || element == EL_EMC_DRIPPER)
9607 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9609 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9613 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9614 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9615 Store[ax][ay] = EL_AMOEBA_DROP;
9616 ContinueMoving(ax, ay);
9620 TEST_DrawLevelField(newax, neway);
9623 void Life(int ax, int ay)
9627 int element = Feld[ax][ay];
9628 int graphic = el2img(element);
9629 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9631 boolean changed = FALSE;
9633 if (IS_ANIMATED(graphic))
9634 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9639 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9640 MovDelay[ax][ay] = life_time;
9642 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9645 if (MovDelay[ax][ay])
9649 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9651 int xx = ax+x1, yy = ay+y1;
9654 if (!IN_LEV_FIELD(xx, yy))
9657 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9659 int x = xx+x2, y = yy+y2;
9661 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9664 if (((Feld[x][y] == element ||
9665 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9667 (IS_FREE(x, y) && Stop[x][y]))
9671 if (xx == ax && yy == ay) /* field in the middle */
9673 if (nachbarn < life_parameter[0] ||
9674 nachbarn > life_parameter[1])
9676 Feld[xx][yy] = EL_EMPTY;
9678 TEST_DrawLevelField(xx, yy);
9679 Stop[xx][yy] = TRUE;
9683 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9684 { /* free border field */
9685 if (nachbarn >= life_parameter[2] &&
9686 nachbarn <= life_parameter[3])
9688 Feld[xx][yy] = element;
9689 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9691 TEST_DrawLevelField(xx, yy);
9692 Stop[xx][yy] = TRUE;
9699 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9700 SND_GAME_OF_LIFE_GROWING);
9703 static void InitRobotWheel(int x, int y)
9705 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9708 static void RunRobotWheel(int x, int y)
9710 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9713 static void StopRobotWheel(int x, int y)
9715 if (ZX == x && ZY == y)
9719 game.robot_wheel_active = FALSE;
9723 static void InitTimegateWheel(int x, int y)
9725 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9728 static void RunTimegateWheel(int x, int y)
9730 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9733 static void InitMagicBallDelay(int x, int y)
9736 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9738 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9742 static void ActivateMagicBall(int bx, int by)
9746 if (level.ball_random)
9748 int pos_border = RND(8); /* select one of the eight border elements */
9749 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9750 int xx = pos_content % 3;
9751 int yy = pos_content / 3;
9756 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9757 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9761 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9763 int xx = x - bx + 1;
9764 int yy = y - by + 1;
9766 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9767 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9771 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9774 void CheckExit(int x, int y)
9776 if (local_player->gems_still_needed > 0 ||
9777 local_player->sokobanfields_still_needed > 0 ||
9778 local_player->lights_still_needed > 0)
9780 int element = Feld[x][y];
9781 int graphic = el2img(element);
9783 if (IS_ANIMATED(graphic))
9784 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9789 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9792 Feld[x][y] = EL_EXIT_OPENING;
9794 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9797 void CheckExitEM(int x, int y)
9799 if (local_player->gems_still_needed > 0 ||
9800 local_player->sokobanfields_still_needed > 0 ||
9801 local_player->lights_still_needed > 0)
9803 int element = Feld[x][y];
9804 int graphic = el2img(element);
9806 if (IS_ANIMATED(graphic))
9807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9812 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9815 Feld[x][y] = EL_EM_EXIT_OPENING;
9817 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9820 void CheckExitSteel(int x, int y)
9822 if (local_player->gems_still_needed > 0 ||
9823 local_player->sokobanfields_still_needed > 0 ||
9824 local_player->lights_still_needed > 0)
9826 int element = Feld[x][y];
9827 int graphic = el2img(element);
9829 if (IS_ANIMATED(graphic))
9830 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9835 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9838 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9840 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9843 void CheckExitSteelEM(int x, int y)
9845 if (local_player->gems_still_needed > 0 ||
9846 local_player->sokobanfields_still_needed > 0 ||
9847 local_player->lights_still_needed > 0)
9849 int element = Feld[x][y];
9850 int graphic = el2img(element);
9852 if (IS_ANIMATED(graphic))
9853 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9858 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9861 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9863 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9866 void CheckExitSP(int x, int y)
9868 if (local_player->gems_still_needed > 0)
9870 int element = Feld[x][y];
9871 int graphic = el2img(element);
9873 if (IS_ANIMATED(graphic))
9874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9879 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9882 Feld[x][y] = EL_SP_EXIT_OPENING;
9884 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9887 static void CloseAllOpenTimegates()
9891 SCAN_PLAYFIELD(x, y)
9893 int element = Feld[x][y];
9895 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9897 Feld[x][y] = EL_TIMEGATE_CLOSING;
9899 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9904 void DrawTwinkleOnField(int x, int y)
9906 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9909 if (Feld[x][y] == EL_BD_DIAMOND)
9912 if (MovDelay[x][y] == 0) /* next animation frame */
9913 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9915 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9919 DrawLevelElementAnimation(x, y, Feld[x][y]);
9921 if (MovDelay[x][y] != 0)
9923 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9924 10 - MovDelay[x][y]);
9926 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9931 void MauerWaechst(int x, int y)
9935 if (!MovDelay[x][y]) /* next animation frame */
9936 MovDelay[x][y] = 3 * delay;
9938 if (MovDelay[x][y]) /* wait some time before next frame */
9942 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9944 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9945 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9947 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9950 if (!MovDelay[x][y])
9952 if (MovDir[x][y] == MV_LEFT)
9954 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9955 TEST_DrawLevelField(x - 1, y);
9957 else if (MovDir[x][y] == MV_RIGHT)
9959 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9960 TEST_DrawLevelField(x + 1, y);
9962 else if (MovDir[x][y] == MV_UP)
9964 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9965 TEST_DrawLevelField(x, y - 1);
9969 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9970 TEST_DrawLevelField(x, y + 1);
9973 Feld[x][y] = Store[x][y];
9975 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9976 TEST_DrawLevelField(x, y);
9981 void MauerAbleger(int ax, int ay)
9983 int element = Feld[ax][ay];
9984 int graphic = el2img(element);
9985 boolean oben_frei = FALSE, unten_frei = FALSE;
9986 boolean links_frei = FALSE, rechts_frei = FALSE;
9987 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9988 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9989 boolean new_wall = FALSE;
9991 if (IS_ANIMATED(graphic))
9992 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9994 if (!MovDelay[ax][ay]) /* start building new wall */
9995 MovDelay[ax][ay] = 6;
9997 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10000 if (MovDelay[ax][ay])
10004 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10006 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10008 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10010 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10011 rechts_frei = TRUE;
10013 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10014 element == EL_EXPANDABLE_WALL_ANY)
10018 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10019 Store[ax][ay-1] = element;
10020 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10021 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10022 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10023 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10028 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10029 Store[ax][ay+1] = element;
10030 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10031 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10032 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10033 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10038 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10039 element == EL_EXPANDABLE_WALL_ANY ||
10040 element == EL_EXPANDABLE_WALL ||
10041 element == EL_BD_EXPANDABLE_WALL)
10045 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10046 Store[ax-1][ay] = element;
10047 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10048 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10049 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10050 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10056 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10057 Store[ax+1][ay] = element;
10058 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10059 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10060 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10061 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10066 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10067 TEST_DrawLevelField(ax, ay);
10069 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10070 oben_massiv = TRUE;
10071 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10072 unten_massiv = TRUE;
10073 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10074 links_massiv = TRUE;
10075 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10076 rechts_massiv = TRUE;
10078 if (((oben_massiv && unten_massiv) ||
10079 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10080 element == EL_EXPANDABLE_WALL) &&
10081 ((links_massiv && rechts_massiv) ||
10082 element == EL_EXPANDABLE_WALL_VERTICAL))
10083 Feld[ax][ay] = EL_WALL;
10086 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10089 void MauerAblegerStahl(int ax, int ay)
10091 int element = Feld[ax][ay];
10092 int graphic = el2img(element);
10093 boolean oben_frei = FALSE, unten_frei = FALSE;
10094 boolean links_frei = FALSE, rechts_frei = FALSE;
10095 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10096 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10097 boolean new_wall = FALSE;
10099 if (IS_ANIMATED(graphic))
10100 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10102 if (!MovDelay[ax][ay]) /* start building new wall */
10103 MovDelay[ax][ay] = 6;
10105 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10107 MovDelay[ax][ay]--;
10108 if (MovDelay[ax][ay])
10112 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10114 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10116 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10118 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10119 rechts_frei = TRUE;
10121 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10122 element == EL_EXPANDABLE_STEELWALL_ANY)
10126 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10127 Store[ax][ay-1] = element;
10128 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10129 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10130 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10131 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10136 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10137 Store[ax][ay+1] = element;
10138 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10139 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10140 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10141 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10146 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10147 element == EL_EXPANDABLE_STEELWALL_ANY)
10151 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10152 Store[ax-1][ay] = element;
10153 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10154 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10155 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10156 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10162 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10163 Store[ax+1][ay] = element;
10164 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10165 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10166 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10167 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10172 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10173 oben_massiv = TRUE;
10174 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10175 unten_massiv = TRUE;
10176 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10177 links_massiv = TRUE;
10178 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10179 rechts_massiv = TRUE;
10181 if (((oben_massiv && unten_massiv) ||
10182 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10183 ((links_massiv && rechts_massiv) ||
10184 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10185 Feld[ax][ay] = EL_STEELWALL;
10188 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10191 void CheckForDragon(int x, int y)
10194 boolean dragon_found = FALSE;
10195 static int xy[4][2] =
10203 for (i = 0; i < NUM_DIRECTIONS; i++)
10205 for (j = 0; j < 4; j++)
10207 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10209 if (IN_LEV_FIELD(xx, yy) &&
10210 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10212 if (Feld[xx][yy] == EL_DRAGON)
10213 dragon_found = TRUE;
10222 for (i = 0; i < NUM_DIRECTIONS; i++)
10224 for (j = 0; j < 3; j++)
10226 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10228 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10230 Feld[xx][yy] = EL_EMPTY;
10231 TEST_DrawLevelField(xx, yy);
10240 static void InitBuggyBase(int x, int y)
10242 int element = Feld[x][y];
10243 int activating_delay = FRAMES_PER_SECOND / 4;
10245 ChangeDelay[x][y] =
10246 (element == EL_SP_BUGGY_BASE ?
10247 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10248 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10250 element == EL_SP_BUGGY_BASE_ACTIVE ?
10251 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10254 static void WarnBuggyBase(int x, int y)
10257 static int xy[4][2] =
10265 for (i = 0; i < NUM_DIRECTIONS; i++)
10267 int xx = x + xy[i][0];
10268 int yy = y + xy[i][1];
10270 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10272 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10279 static void InitTrap(int x, int y)
10281 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10284 static void ActivateTrap(int x, int y)
10286 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10289 static void ChangeActiveTrap(int x, int y)
10291 int graphic = IMG_TRAP_ACTIVE;
10293 /* if new animation frame was drawn, correct crumbled sand border */
10294 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10295 TEST_DrawLevelFieldCrumbledSand(x, y);
10298 static int getSpecialActionElement(int element, int number, int base_element)
10300 return (element != EL_EMPTY ? element :
10301 number != -1 ? base_element + number - 1 :
10305 static int getModifiedActionNumber(int value_old, int operator, int operand,
10306 int value_min, int value_max)
10308 int value_new = (operator == CA_MODE_SET ? operand :
10309 operator == CA_MODE_ADD ? value_old + operand :
10310 operator == CA_MODE_SUBTRACT ? value_old - operand :
10311 operator == CA_MODE_MULTIPLY ? value_old * operand :
10312 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10313 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10316 return (value_new < value_min ? value_min :
10317 value_new > value_max ? value_max :
10321 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10323 struct ElementInfo *ei = &element_info[element];
10324 struct ElementChangeInfo *change = &ei->change_page[page];
10325 int target_element = change->target_element;
10326 int action_type = change->action_type;
10327 int action_mode = change->action_mode;
10328 int action_arg = change->action_arg;
10329 int action_element = change->action_element;
10332 if (!change->has_action)
10335 /* ---------- determine action paramater values -------------------------- */
10337 int level_time_value =
10338 (level.time > 0 ? TimeLeft :
10341 int action_arg_element_raw =
10342 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10343 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10344 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10345 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10346 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10347 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10348 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10350 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10353 if (action_arg_element_raw == EL_GROUP_START)
10354 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10357 int action_arg_direction =
10358 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10359 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10360 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10361 change->actual_trigger_side :
10362 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10363 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10366 int action_arg_number_min =
10367 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10370 int action_arg_number_max =
10371 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10372 action_type == CA_SET_LEVEL_GEMS ? 999 :
10373 action_type == CA_SET_LEVEL_TIME ? 9999 :
10374 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10375 action_type == CA_SET_CE_VALUE ? 9999 :
10376 action_type == CA_SET_CE_SCORE ? 9999 :
10379 int action_arg_number_reset =
10380 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10381 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10382 action_type == CA_SET_LEVEL_TIME ? level.time :
10383 action_type == CA_SET_LEVEL_SCORE ? 0 :
10384 #if USE_NEW_CUSTOM_VALUE
10385 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10387 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10389 action_type == CA_SET_CE_SCORE ? 0 :
10392 int action_arg_number =
10393 (action_arg <= CA_ARG_MAX ? action_arg :
10394 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10395 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10396 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10397 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10398 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10399 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10400 #if USE_NEW_CUSTOM_VALUE
10401 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10403 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10405 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10406 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10407 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10408 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10409 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10410 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10411 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10412 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10413 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10414 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10415 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10416 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10417 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10418 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10421 int action_arg_number_old =
10422 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10423 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10424 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10425 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10426 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10429 int action_arg_number_new =
10430 getModifiedActionNumber(action_arg_number_old,
10431 action_mode, action_arg_number,
10432 action_arg_number_min, action_arg_number_max);
10435 int trigger_player_bits =
10436 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10437 change->actual_trigger_player_bits : change->trigger_player);
10439 int trigger_player_bits =
10440 (change->actual_trigger_player >= EL_PLAYER_1 &&
10441 change->actual_trigger_player <= EL_PLAYER_4 ?
10442 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10446 int action_arg_player_bits =
10447 (action_arg >= CA_ARG_PLAYER_1 &&
10448 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10449 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10450 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10453 /* ---------- execute action -------------------------------------------- */
10455 switch (action_type)
10462 /* ---------- level actions ------------------------------------------- */
10464 case CA_RESTART_LEVEL:
10466 game.restart_level = TRUE;
10471 case CA_SHOW_ENVELOPE:
10473 int element = getSpecialActionElement(action_arg_element,
10474 action_arg_number, EL_ENVELOPE_1);
10476 if (IS_ENVELOPE(element))
10477 local_player->show_envelope = element;
10482 case CA_SET_LEVEL_TIME:
10484 if (level.time > 0) /* only modify limited time value */
10486 TimeLeft = action_arg_number_new;
10489 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10491 DisplayGameControlValues();
10493 DrawGameValue_Time(TimeLeft);
10496 if (!TimeLeft && setup.time_limit)
10497 for (i = 0; i < MAX_PLAYERS; i++)
10498 KillPlayer(&stored_player[i]);
10504 case CA_SET_LEVEL_SCORE:
10506 local_player->score = action_arg_number_new;
10509 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10511 DisplayGameControlValues();
10513 DrawGameValue_Score(local_player->score);
10519 case CA_SET_LEVEL_GEMS:
10521 local_player->gems_still_needed = action_arg_number_new;
10524 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10526 DisplayGameControlValues();
10528 DrawGameValue_Emeralds(local_player->gems_still_needed);
10534 #if !USE_PLAYER_GRAVITY
10535 case CA_SET_LEVEL_GRAVITY:
10537 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10538 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10539 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10545 case CA_SET_LEVEL_WIND:
10547 game.wind_direction = action_arg_direction;
10552 case CA_SET_LEVEL_RANDOM_SEED:
10555 /* ensure that setting a new random seed while playing is predictable */
10556 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10558 InitRND(action_arg_number_new);
10562 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10570 for (i = 0; i < 9; i++)
10571 printf("%d, ", RND(2));
10579 /* ---------- player actions ------------------------------------------ */
10581 case CA_MOVE_PLAYER:
10583 /* automatically move to the next field in specified direction */
10584 for (i = 0; i < MAX_PLAYERS; i++)
10585 if (trigger_player_bits & (1 << i))
10586 stored_player[i].programmed_action = action_arg_direction;
10591 case CA_EXIT_PLAYER:
10593 for (i = 0; i < MAX_PLAYERS; i++)
10594 if (action_arg_player_bits & (1 << i))
10595 PlayerWins(&stored_player[i]);
10600 case CA_KILL_PLAYER:
10602 for (i = 0; i < MAX_PLAYERS; i++)
10603 if (action_arg_player_bits & (1 << i))
10604 KillPlayer(&stored_player[i]);
10609 case CA_SET_PLAYER_KEYS:
10611 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10612 int element = getSpecialActionElement(action_arg_element,
10613 action_arg_number, EL_KEY_1);
10615 if (IS_KEY(element))
10617 for (i = 0; i < MAX_PLAYERS; i++)
10619 if (trigger_player_bits & (1 << i))
10621 stored_player[i].key[KEY_NR(element)] = key_state;
10623 DrawGameDoorValues();
10631 case CA_SET_PLAYER_SPEED:
10634 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10637 for (i = 0; i < MAX_PLAYERS; i++)
10639 if (trigger_player_bits & (1 << i))
10641 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10643 if (action_arg == CA_ARG_SPEED_FASTER &&
10644 stored_player[i].cannot_move)
10646 action_arg_number = STEPSIZE_VERY_SLOW;
10648 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10649 action_arg == CA_ARG_SPEED_FASTER)
10651 action_arg_number = 2;
10652 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10655 else if (action_arg == CA_ARG_NUMBER_RESET)
10657 action_arg_number = level.initial_player_stepsize[i];
10661 getModifiedActionNumber(move_stepsize,
10664 action_arg_number_min,
10665 action_arg_number_max);
10667 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10674 case CA_SET_PLAYER_SHIELD:
10676 for (i = 0; i < MAX_PLAYERS; i++)
10678 if (trigger_player_bits & (1 << i))
10680 if (action_arg == CA_ARG_SHIELD_OFF)
10682 stored_player[i].shield_normal_time_left = 0;
10683 stored_player[i].shield_deadly_time_left = 0;
10685 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10687 stored_player[i].shield_normal_time_left = 999999;
10689 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10691 stored_player[i].shield_normal_time_left = 999999;
10692 stored_player[i].shield_deadly_time_left = 999999;
10700 #if USE_PLAYER_GRAVITY
10701 case CA_SET_PLAYER_GRAVITY:
10703 for (i = 0; i < MAX_PLAYERS; i++)
10705 if (trigger_player_bits & (1 << i))
10707 stored_player[i].gravity =
10708 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10709 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10710 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10711 stored_player[i].gravity);
10719 case CA_SET_PLAYER_ARTWORK:
10721 for (i = 0; i < MAX_PLAYERS; i++)
10723 if (trigger_player_bits & (1 << i))
10725 int artwork_element = action_arg_element;
10727 if (action_arg == CA_ARG_ELEMENT_RESET)
10729 (level.use_artwork_element[i] ? level.artwork_element[i] :
10730 stored_player[i].element_nr);
10732 #if USE_GFX_RESET_PLAYER_ARTWORK
10733 if (stored_player[i].artwork_element != artwork_element)
10734 stored_player[i].Frame = 0;
10737 stored_player[i].artwork_element = artwork_element;
10739 SetPlayerWaiting(&stored_player[i], FALSE);
10741 /* set number of special actions for bored and sleeping animation */
10742 stored_player[i].num_special_action_bored =
10743 get_num_special_action(artwork_element,
10744 ACTION_BORING_1, ACTION_BORING_LAST);
10745 stored_player[i].num_special_action_sleeping =
10746 get_num_special_action(artwork_element,
10747 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10754 case CA_SET_PLAYER_INVENTORY:
10756 for (i = 0; i < MAX_PLAYERS; i++)
10758 struct PlayerInfo *player = &stored_player[i];
10761 if (trigger_player_bits & (1 << i))
10763 int inventory_element = action_arg_element;
10765 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10766 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10767 action_arg == CA_ARG_ELEMENT_ACTION)
10769 int element = inventory_element;
10770 int collect_count = element_info[element].collect_count_initial;
10772 if (!IS_CUSTOM_ELEMENT(element))
10775 if (collect_count == 0)
10776 player->inventory_infinite_element = element;
10778 for (k = 0; k < collect_count; k++)
10779 if (player->inventory_size < MAX_INVENTORY_SIZE)
10780 player->inventory_element[player->inventory_size++] =
10783 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10784 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10785 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10787 if (player->inventory_infinite_element != EL_UNDEFINED &&
10788 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10789 action_arg_element_raw))
10790 player->inventory_infinite_element = EL_UNDEFINED;
10792 for (k = 0, j = 0; j < player->inventory_size; j++)
10794 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10795 action_arg_element_raw))
10796 player->inventory_element[k++] = player->inventory_element[j];
10799 player->inventory_size = k;
10801 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10803 if (player->inventory_size > 0)
10805 for (j = 0; j < player->inventory_size - 1; j++)
10806 player->inventory_element[j] = player->inventory_element[j + 1];
10808 player->inventory_size--;
10811 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10813 if (player->inventory_size > 0)
10814 player->inventory_size--;
10816 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10818 player->inventory_infinite_element = EL_UNDEFINED;
10819 player->inventory_size = 0;
10821 else if (action_arg == CA_ARG_INVENTORY_RESET)
10823 player->inventory_infinite_element = EL_UNDEFINED;
10824 player->inventory_size = 0;
10826 if (level.use_initial_inventory[i])
10828 for (j = 0; j < level.initial_inventory_size[i]; j++)
10830 int element = level.initial_inventory_content[i][j];
10831 int collect_count = element_info[element].collect_count_initial;
10833 if (!IS_CUSTOM_ELEMENT(element))
10836 if (collect_count == 0)
10837 player->inventory_infinite_element = element;
10839 for (k = 0; k < collect_count; k++)
10840 if (player->inventory_size < MAX_INVENTORY_SIZE)
10841 player->inventory_element[player->inventory_size++] =
10852 /* ---------- CE actions ---------------------------------------------- */
10854 case CA_SET_CE_VALUE:
10856 #if USE_NEW_CUSTOM_VALUE
10857 int last_ce_value = CustomValue[x][y];
10859 CustomValue[x][y] = action_arg_number_new;
10861 if (CustomValue[x][y] != last_ce_value)
10863 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10864 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10866 if (CustomValue[x][y] == 0)
10868 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10869 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10877 case CA_SET_CE_SCORE:
10879 #if USE_NEW_CUSTOM_VALUE
10880 int last_ce_score = ei->collect_score;
10882 ei->collect_score = action_arg_number_new;
10884 if (ei->collect_score != last_ce_score)
10886 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10887 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10889 if (ei->collect_score == 0)
10893 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10894 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10897 This is a very special case that seems to be a mixture between
10898 CheckElementChange() and CheckTriggeredElementChange(): while
10899 the first one only affects single elements that are triggered
10900 directly, the second one affects multiple elements in the playfield
10901 that are triggered indirectly by another element. This is a third
10902 case: Changing the CE score always affects multiple identical CEs,
10903 so every affected CE must be checked, not only the single CE for
10904 which the CE score was changed in the first place (as every instance
10905 of that CE shares the same CE score, and therefore also can change)!
10907 SCAN_PLAYFIELD(xx, yy)
10909 if (Feld[xx][yy] == element)
10910 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10911 CE_SCORE_GETS_ZERO);
10920 case CA_SET_CE_ARTWORK:
10922 int artwork_element = action_arg_element;
10923 boolean reset_frame = FALSE;
10926 if (action_arg == CA_ARG_ELEMENT_RESET)
10927 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10930 if (ei->gfx_element != artwork_element)
10931 reset_frame = TRUE;
10933 ei->gfx_element = artwork_element;
10935 SCAN_PLAYFIELD(xx, yy)
10937 if (Feld[xx][yy] == element)
10941 ResetGfxAnimation(xx, yy);
10942 ResetRandomAnimationValue(xx, yy);
10945 TEST_DrawLevelField(xx, yy);
10952 /* ---------- engine actions ------------------------------------------ */
10954 case CA_SET_ENGINE_SCAN_MODE:
10956 InitPlayfieldScanMode(action_arg);
10966 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10968 int old_element = Feld[x][y];
10969 int new_element = GetElementFromGroupElement(element);
10970 int previous_move_direction = MovDir[x][y];
10971 #if USE_NEW_CUSTOM_VALUE
10972 int last_ce_value = CustomValue[x][y];
10974 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10975 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10976 boolean add_player_onto_element = (new_element_is_player &&
10977 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10978 /* this breaks SnakeBite when a snake is
10979 halfway through a door that closes */
10980 /* NOW FIXED AT LEVEL INIT IN files.c */
10981 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10983 IS_WALKABLE(old_element));
10986 /* check if element under the player changes from accessible to unaccessible
10987 (needed for special case of dropping element which then changes) */
10988 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10989 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10997 if (!add_player_onto_element)
10999 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11000 RemoveMovingField(x, y);
11004 Feld[x][y] = new_element;
11006 #if !USE_GFX_RESET_GFX_ANIMATION
11007 ResetGfxAnimation(x, y);
11008 ResetRandomAnimationValue(x, y);
11011 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11012 MovDir[x][y] = previous_move_direction;
11014 #if USE_NEW_CUSTOM_VALUE
11015 if (element_info[new_element].use_last_ce_value)
11016 CustomValue[x][y] = last_ce_value;
11019 InitField_WithBug1(x, y, FALSE);
11021 new_element = Feld[x][y]; /* element may have changed */
11023 #if USE_GFX_RESET_GFX_ANIMATION
11024 ResetGfxAnimation(x, y);
11025 ResetRandomAnimationValue(x, y);
11028 TEST_DrawLevelField(x, y);
11030 if (GFX_CRUMBLED(new_element))
11031 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11035 /* check if element under the player changes from accessible to unaccessible
11036 (needed for special case of dropping element which then changes) */
11037 /* (must be checked after creating new element for walkable group elements) */
11038 #if USE_FIX_KILLED_BY_NON_WALKABLE
11039 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11040 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11047 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11048 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11057 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11058 if (new_element_is_player)
11059 RelocatePlayer(x, y, new_element);
11062 ChangeCount[x][y]++; /* count number of changes in the same frame */
11064 TestIfBadThingTouchesPlayer(x, y);
11065 TestIfPlayerTouchesCustomElement(x, y);
11066 TestIfElementTouchesCustomElement(x, y);
11069 static void CreateField(int x, int y, int element)
11071 CreateFieldExt(x, y, element, FALSE);
11074 static void CreateElementFromChange(int x, int y, int element)
11076 element = GET_VALID_RUNTIME_ELEMENT(element);
11078 #if USE_STOP_CHANGED_ELEMENTS
11079 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11081 int old_element = Feld[x][y];
11083 /* prevent changed element from moving in same engine frame
11084 unless both old and new element can either fall or move */
11085 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11086 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11091 CreateFieldExt(x, y, element, TRUE);
11094 static boolean ChangeElement(int x, int y, int element, int page)
11096 struct ElementInfo *ei = &element_info[element];
11097 struct ElementChangeInfo *change = &ei->change_page[page];
11098 int ce_value = CustomValue[x][y];
11099 int ce_score = ei->collect_score;
11100 int target_element;
11101 int old_element = Feld[x][y];
11103 /* always use default change event to prevent running into a loop */
11104 if (ChangeEvent[x][y] == -1)
11105 ChangeEvent[x][y] = CE_DELAY;
11107 if (ChangeEvent[x][y] == CE_DELAY)
11109 /* reset actual trigger element, trigger player and action element */
11110 change->actual_trigger_element = EL_EMPTY;
11111 change->actual_trigger_player = EL_EMPTY;
11112 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11113 change->actual_trigger_side = CH_SIDE_NONE;
11114 change->actual_trigger_ce_value = 0;
11115 change->actual_trigger_ce_score = 0;
11118 /* do not change elements more than a specified maximum number of changes */
11119 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11122 ChangeCount[x][y]++; /* count number of changes in the same frame */
11124 if (change->explode)
11131 if (change->use_target_content)
11133 boolean complete_replace = TRUE;
11134 boolean can_replace[3][3];
11137 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11140 boolean is_walkable;
11141 boolean is_diggable;
11142 boolean is_collectible;
11143 boolean is_removable;
11144 boolean is_destructible;
11145 int ex = x + xx - 1;
11146 int ey = y + yy - 1;
11147 int content_element = change->target_content.e[xx][yy];
11150 can_replace[xx][yy] = TRUE;
11152 if (ex == x && ey == y) /* do not check changing element itself */
11155 if (content_element == EL_EMPTY_SPACE)
11157 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11162 if (!IN_LEV_FIELD(ex, ey))
11164 can_replace[xx][yy] = FALSE;
11165 complete_replace = FALSE;
11172 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11173 e = MovingOrBlocked2Element(ex, ey);
11175 is_empty = (IS_FREE(ex, ey) ||
11176 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11178 is_walkable = (is_empty || IS_WALKABLE(e));
11179 is_diggable = (is_empty || IS_DIGGABLE(e));
11180 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11181 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11182 is_removable = (is_diggable || is_collectible);
11184 can_replace[xx][yy] =
11185 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11186 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11187 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11188 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11189 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11190 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11191 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11193 if (!can_replace[xx][yy])
11194 complete_replace = FALSE;
11197 if (!change->only_if_complete || complete_replace)
11199 boolean something_has_changed = FALSE;
11201 if (change->only_if_complete && change->use_random_replace &&
11202 RND(100) < change->random_percentage)
11205 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11207 int ex = x + xx - 1;
11208 int ey = y + yy - 1;
11209 int content_element;
11211 if (can_replace[xx][yy] && (!change->use_random_replace ||
11212 RND(100) < change->random_percentage))
11214 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11215 RemoveMovingField(ex, ey);
11217 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11219 content_element = change->target_content.e[xx][yy];
11220 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11221 ce_value, ce_score);
11223 CreateElementFromChange(ex, ey, target_element);
11225 something_has_changed = TRUE;
11227 /* for symmetry reasons, freeze newly created border elements */
11228 if (ex != x || ey != y)
11229 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11233 if (something_has_changed)
11235 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11236 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11242 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11243 ce_value, ce_score);
11245 if (element == EL_DIAGONAL_GROWING ||
11246 element == EL_DIAGONAL_SHRINKING)
11248 target_element = Store[x][y];
11250 Store[x][y] = EL_EMPTY;
11253 CreateElementFromChange(x, y, target_element);
11255 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11256 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11259 /* this uses direct change before indirect change */
11260 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11265 #if USE_NEW_DELAYED_ACTION
11267 static void HandleElementChange(int x, int y, int page)
11269 int element = MovingOrBlocked2Element(x, y);
11270 struct ElementInfo *ei = &element_info[element];
11271 struct ElementChangeInfo *change = &ei->change_page[page];
11272 boolean handle_action_before_change = FALSE;
11275 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11276 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11279 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11280 x, y, element, element_info[element].token_name);
11281 printf("HandleElementChange(): This should never happen!\n");
11286 /* this can happen with classic bombs on walkable, changing elements */
11287 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11290 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11291 ChangeDelay[x][y] = 0;
11297 if (ChangeDelay[x][y] == 0) /* initialize element change */
11299 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11301 if (change->can_change)
11304 /* !!! not clear why graphic animation should be reset at all here !!! */
11305 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11306 #if USE_GFX_RESET_WHEN_NOT_MOVING
11307 /* when a custom element is about to change (for example by change delay),
11308 do not reset graphic animation when the custom element is moving */
11309 if (!IS_MOVING(x, y))
11312 ResetGfxAnimation(x, y);
11313 ResetRandomAnimationValue(x, y);
11317 if (change->pre_change_function)
11318 change->pre_change_function(x, y);
11322 ChangeDelay[x][y]--;
11324 if (ChangeDelay[x][y] != 0) /* continue element change */
11326 if (change->can_change)
11328 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11330 if (IS_ANIMATED(graphic))
11331 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11333 if (change->change_function)
11334 change->change_function(x, y);
11337 else /* finish element change */
11339 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11341 page = ChangePage[x][y];
11342 ChangePage[x][y] = -1;
11344 change = &ei->change_page[page];
11347 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11349 ChangeDelay[x][y] = 1; /* try change after next move step */
11350 ChangePage[x][y] = page; /* remember page to use for change */
11356 /* special case: set new level random seed before changing element */
11357 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11358 handle_action_before_change = TRUE;
11360 if (change->has_action && handle_action_before_change)
11361 ExecuteCustomElementAction(x, y, element, page);
11364 if (change->can_change)
11366 if (ChangeElement(x, y, element, page))
11368 if (change->post_change_function)
11369 change->post_change_function(x, y);
11373 if (change->has_action && !handle_action_before_change)
11374 ExecuteCustomElementAction(x, y, element, page);
11380 static void HandleElementChange(int x, int y, int page)
11382 int element = MovingOrBlocked2Element(x, y);
11383 struct ElementInfo *ei = &element_info[element];
11384 struct ElementChangeInfo *change = &ei->change_page[page];
11387 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11390 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11391 x, y, element, element_info[element].token_name);
11392 printf("HandleElementChange(): This should never happen!\n");
11397 /* this can happen with classic bombs on walkable, changing elements */
11398 if (!CAN_CHANGE(element))
11401 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11402 ChangeDelay[x][y] = 0;
11408 if (ChangeDelay[x][y] == 0) /* initialize element change */
11410 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11412 ResetGfxAnimation(x, y);
11413 ResetRandomAnimationValue(x, y);
11415 if (change->pre_change_function)
11416 change->pre_change_function(x, y);
11419 ChangeDelay[x][y]--;
11421 if (ChangeDelay[x][y] != 0) /* continue element change */
11423 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11425 if (IS_ANIMATED(graphic))
11426 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11428 if (change->change_function)
11429 change->change_function(x, y);
11431 else /* finish element change */
11433 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11435 page = ChangePage[x][y];
11436 ChangePage[x][y] = -1;
11438 change = &ei->change_page[page];
11441 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11443 ChangeDelay[x][y] = 1; /* try change after next move step */
11444 ChangePage[x][y] = page; /* remember page to use for change */
11449 if (ChangeElement(x, y, element, page))
11451 if (change->post_change_function)
11452 change->post_change_function(x, y);
11459 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11460 int trigger_element,
11462 int trigger_player,
11466 boolean change_done_any = FALSE;
11467 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11470 if (!(trigger_events[trigger_element][trigger_event]))
11474 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11475 trigger_event, recursion_loop_depth, recursion_loop_detected,
11476 recursion_loop_element, EL_NAME(recursion_loop_element));
11479 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11481 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11483 int element = EL_CUSTOM_START + i;
11484 boolean change_done = FALSE;
11487 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11488 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11491 for (p = 0; p < element_info[element].num_change_pages; p++)
11493 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11495 if (change->can_change_or_has_action &&
11496 change->has_event[trigger_event] &&
11497 change->trigger_side & trigger_side &&
11498 change->trigger_player & trigger_player &&
11499 change->trigger_page & trigger_page_bits &&
11500 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11502 change->actual_trigger_element = trigger_element;
11503 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11504 change->actual_trigger_player_bits = trigger_player;
11505 change->actual_trigger_side = trigger_side;
11506 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11507 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11510 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11511 element, EL_NAME(element), p);
11514 if ((change->can_change && !change_done) || change->has_action)
11518 SCAN_PLAYFIELD(x, y)
11520 if (Feld[x][y] == element)
11522 if (change->can_change && !change_done)
11524 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11525 /* if element already changed in this frame, not only prevent
11526 another element change (checked in ChangeElement()), but
11527 also prevent additional element actions for this element */
11529 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11530 !level.use_action_after_change_bug)
11535 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11536 element, EL_NAME(element), p);
11539 ChangeDelay[x][y] = 1;
11540 ChangeEvent[x][y] = trigger_event;
11542 HandleElementChange(x, y, p);
11544 #if USE_NEW_DELAYED_ACTION
11545 else if (change->has_action)
11547 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11548 /* if element already changed in this frame, not only prevent
11549 another element change (checked in ChangeElement()), but
11550 also prevent additional element actions for this element */
11552 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11553 !level.use_action_after_change_bug)
11559 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11560 element, EL_NAME(element), p);
11563 ExecuteCustomElementAction(x, y, element, p);
11564 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11567 if (change->has_action)
11569 ExecuteCustomElementAction(x, y, element, p);
11570 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11576 if (change->can_change)
11578 change_done = TRUE;
11579 change_done_any = TRUE;
11582 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11583 element, EL_NAME(element), p);
11592 RECURSION_LOOP_DETECTION_END();
11594 return change_done_any;
11597 static boolean CheckElementChangeExt(int x, int y,
11599 int trigger_element,
11601 int trigger_player,
11604 boolean change_done = FALSE;
11607 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11608 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11611 if (Feld[x][y] == EL_BLOCKED)
11613 Blocked2Moving(x, y, &x, &y);
11614 element = Feld[x][y];
11618 /* check if element has already changed */
11619 if (Feld[x][y] != element)
11622 /* check if element has already changed or is about to change after moving */
11623 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11624 Feld[x][y] != element) ||
11626 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11627 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11628 ChangePage[x][y] != -1)))
11633 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11634 trigger_event, recursion_loop_depth, recursion_loop_detected,
11635 recursion_loop_element, EL_NAME(recursion_loop_element));
11638 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11641 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11644 for (p = 0; p < element_info[element].num_change_pages; p++)
11646 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11648 /* check trigger element for all events where the element that is checked
11649 for changing interacts with a directly adjacent element -- this is
11650 different to element changes that affect other elements to change on the
11651 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11652 boolean check_trigger_element =
11653 (trigger_event == CE_TOUCHING_X ||
11654 trigger_event == CE_HITTING_X ||
11655 trigger_event == CE_HIT_BY_X ||
11657 /* this one was forgotten until 3.2.3 */
11658 trigger_event == CE_DIGGING_X);
11661 if (change->can_change_or_has_action &&
11662 change->has_event[trigger_event] &&
11663 change->trigger_side & trigger_side &&
11664 change->trigger_player & trigger_player &&
11665 (!check_trigger_element ||
11666 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11668 change->actual_trigger_element = trigger_element;
11669 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11670 change->actual_trigger_player_bits = trigger_player;
11671 change->actual_trigger_side = trigger_side;
11672 change->actual_trigger_ce_value = CustomValue[x][y];
11673 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11675 /* special case: trigger element not at (x,y) position for some events */
11676 if (check_trigger_element)
11688 { 0, 0 }, { 0, 0 }, { 0, 0 },
11692 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11693 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11695 change->actual_trigger_ce_value = CustomValue[xx][yy];
11696 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11699 if (change->can_change && !change_done)
11701 ChangeDelay[x][y] = 1;
11702 ChangeEvent[x][y] = trigger_event;
11704 HandleElementChange(x, y, p);
11706 change_done = TRUE;
11708 #if USE_NEW_DELAYED_ACTION
11709 else if (change->has_action)
11711 ExecuteCustomElementAction(x, y, element, p);
11712 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11715 if (change->has_action)
11717 ExecuteCustomElementAction(x, y, element, p);
11718 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11724 RECURSION_LOOP_DETECTION_END();
11726 return change_done;
11729 static void PlayPlayerSound(struct PlayerInfo *player)
11731 int jx = player->jx, jy = player->jy;
11732 int sound_element = player->artwork_element;
11733 int last_action = player->last_action_waiting;
11734 int action = player->action_waiting;
11736 if (player->is_waiting)
11738 if (action != last_action)
11739 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11741 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11745 if (action != last_action)
11746 StopSound(element_info[sound_element].sound[last_action]);
11748 if (last_action == ACTION_SLEEPING)
11749 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11753 static void PlayAllPlayersSound()
11757 for (i = 0; i < MAX_PLAYERS; i++)
11758 if (stored_player[i].active)
11759 PlayPlayerSound(&stored_player[i]);
11762 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11764 boolean last_waiting = player->is_waiting;
11765 int move_dir = player->MovDir;
11767 player->dir_waiting = move_dir;
11768 player->last_action_waiting = player->action_waiting;
11772 if (!last_waiting) /* not waiting -> waiting */
11774 player->is_waiting = TRUE;
11776 player->frame_counter_bored =
11778 game.player_boring_delay_fixed +
11779 GetSimpleRandom(game.player_boring_delay_random);
11780 player->frame_counter_sleeping =
11782 game.player_sleeping_delay_fixed +
11783 GetSimpleRandom(game.player_sleeping_delay_random);
11785 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11788 if (game.player_sleeping_delay_fixed +
11789 game.player_sleeping_delay_random > 0 &&
11790 player->anim_delay_counter == 0 &&
11791 player->post_delay_counter == 0 &&
11792 FrameCounter >= player->frame_counter_sleeping)
11793 player->is_sleeping = TRUE;
11794 else if (game.player_boring_delay_fixed +
11795 game.player_boring_delay_random > 0 &&
11796 FrameCounter >= player->frame_counter_bored)
11797 player->is_bored = TRUE;
11799 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11800 player->is_bored ? ACTION_BORING :
11803 if (player->is_sleeping && player->use_murphy)
11805 /* special case for sleeping Murphy when leaning against non-free tile */
11807 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11808 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11809 !IS_MOVING(player->jx - 1, player->jy)))
11810 move_dir = MV_LEFT;
11811 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11812 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11813 !IS_MOVING(player->jx + 1, player->jy)))
11814 move_dir = MV_RIGHT;
11816 player->is_sleeping = FALSE;
11818 player->dir_waiting = move_dir;
11821 if (player->is_sleeping)
11823 if (player->num_special_action_sleeping > 0)
11825 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11827 int last_special_action = player->special_action_sleeping;
11828 int num_special_action = player->num_special_action_sleeping;
11829 int special_action =
11830 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11831 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11832 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11833 last_special_action + 1 : ACTION_SLEEPING);
11834 int special_graphic =
11835 el_act_dir2img(player->artwork_element, special_action, move_dir);
11837 player->anim_delay_counter =
11838 graphic_info[special_graphic].anim_delay_fixed +
11839 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11840 player->post_delay_counter =
11841 graphic_info[special_graphic].post_delay_fixed +
11842 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11844 player->special_action_sleeping = special_action;
11847 if (player->anim_delay_counter > 0)
11849 player->action_waiting = player->special_action_sleeping;
11850 player->anim_delay_counter--;
11852 else if (player->post_delay_counter > 0)
11854 player->post_delay_counter--;
11858 else if (player->is_bored)
11860 if (player->num_special_action_bored > 0)
11862 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11864 int special_action =
11865 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11866 int special_graphic =
11867 el_act_dir2img(player->artwork_element, special_action, move_dir);
11869 player->anim_delay_counter =
11870 graphic_info[special_graphic].anim_delay_fixed +
11871 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11872 player->post_delay_counter =
11873 graphic_info[special_graphic].post_delay_fixed +
11874 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11876 player->special_action_bored = special_action;
11879 if (player->anim_delay_counter > 0)
11881 player->action_waiting = player->special_action_bored;
11882 player->anim_delay_counter--;
11884 else if (player->post_delay_counter > 0)
11886 player->post_delay_counter--;
11891 else if (last_waiting) /* waiting -> not waiting */
11893 player->is_waiting = FALSE;
11894 player->is_bored = FALSE;
11895 player->is_sleeping = FALSE;
11897 player->frame_counter_bored = -1;
11898 player->frame_counter_sleeping = -1;
11900 player->anim_delay_counter = 0;
11901 player->post_delay_counter = 0;
11903 player->dir_waiting = player->MovDir;
11904 player->action_waiting = ACTION_DEFAULT;
11906 player->special_action_bored = ACTION_DEFAULT;
11907 player->special_action_sleeping = ACTION_DEFAULT;
11911 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11913 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11914 int left = player_action & JOY_LEFT;
11915 int right = player_action & JOY_RIGHT;
11916 int up = player_action & JOY_UP;
11917 int down = player_action & JOY_DOWN;
11918 int button1 = player_action & JOY_BUTTON_1;
11919 int button2 = player_action & JOY_BUTTON_2;
11920 int dx = (left ? -1 : right ? 1 : 0);
11921 int dy = (up ? -1 : down ? 1 : 0);
11923 if (!player->active || tape.pausing)
11929 snapped = SnapField(player, dx, dy);
11933 dropped = DropElement(player);
11935 moved = MovePlayer(player, dx, dy);
11938 if (tape.single_step && tape.recording && !tape.pausing)
11940 if (button1 || (dropped && !moved))
11942 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11943 SnapField(player, 0, 0); /* stop snapping */
11947 SetPlayerWaiting(player, FALSE);
11949 return player_action;
11953 /* no actions for this player (no input at player's configured device) */
11955 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11956 SnapField(player, 0, 0);
11957 CheckGravityMovementWhenNotMoving(player);
11959 if (player->MovPos == 0)
11960 SetPlayerWaiting(player, TRUE);
11962 if (player->MovPos == 0) /* needed for tape.playing */
11963 player->is_moving = FALSE;
11965 player->is_dropping = FALSE;
11966 player->is_dropping_pressed = FALSE;
11967 player->drop_pressed_delay = 0;
11973 static void CheckLevelTime()
11977 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11979 if (level.native_em_level->lev->home == 0) /* all players at home */
11981 PlayerWins(local_player);
11983 AllPlayersGone = TRUE;
11985 level.native_em_level->lev->home = -1;
11988 if (level.native_em_level->ply[0]->alive == 0 &&
11989 level.native_em_level->ply[1]->alive == 0 &&
11990 level.native_em_level->ply[2]->alive == 0 &&
11991 level.native_em_level->ply[3]->alive == 0) /* all dead */
11992 AllPlayersGone = TRUE;
11995 if (TimeFrames >= FRAMES_PER_SECOND)
12000 for (i = 0; i < MAX_PLAYERS; i++)
12002 struct PlayerInfo *player = &stored_player[i];
12004 if (SHIELD_ON(player))
12006 player->shield_normal_time_left--;
12008 if (player->shield_deadly_time_left > 0)
12009 player->shield_deadly_time_left--;
12013 if (!local_player->LevelSolved && !level.use_step_counter)
12021 if (TimeLeft <= 10 && setup.time_limit)
12022 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12025 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12027 DisplayGameControlValues();
12029 DrawGameValue_Time(TimeLeft);
12032 if (!TimeLeft && setup.time_limit)
12034 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12035 level.native_em_level->lev->killed_out_of_time = TRUE;
12037 for (i = 0; i < MAX_PLAYERS; i++)
12038 KillPlayer(&stored_player[i]);
12042 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12044 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12046 DisplayGameControlValues();
12049 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12050 DrawGameValue_Time(TimePlayed);
12053 level.native_em_level->lev->time =
12054 (level.time == 0 ? TimePlayed : TimeLeft);
12057 if (tape.recording || tape.playing)
12058 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12062 UpdateAndDisplayGameControlValues();
12064 UpdateGameDoorValues();
12065 DrawGameDoorValues();
12069 void AdvanceFrameAndPlayerCounters(int player_nr)
12073 /* advance frame counters (global frame counter and time frame counter) */
12077 /* advance player counters (counters for move delay, move animation etc.) */
12078 for (i = 0; i < MAX_PLAYERS; i++)
12080 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12081 int move_delay_value = stored_player[i].move_delay_value;
12082 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12084 if (!advance_player_counters) /* not all players may be affected */
12087 #if USE_NEW_PLAYER_ANIM
12088 if (move_frames == 0) /* less than one move per game frame */
12090 int stepsize = TILEX / move_delay_value;
12091 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12092 int count = (stored_player[i].is_moving ?
12093 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12095 if (count % delay == 0)
12100 stored_player[i].Frame += move_frames;
12102 if (stored_player[i].MovPos != 0)
12103 stored_player[i].StepFrame += move_frames;
12105 if (stored_player[i].move_delay > 0)
12106 stored_player[i].move_delay--;
12108 /* due to bugs in previous versions, counter must count up, not down */
12109 if (stored_player[i].push_delay != -1)
12110 stored_player[i].push_delay++;
12112 if (stored_player[i].drop_delay > 0)
12113 stored_player[i].drop_delay--;
12115 if (stored_player[i].is_dropping_pressed)
12116 stored_player[i].drop_pressed_delay++;
12120 void StartGameActions(boolean init_network_game, boolean record_tape,
12123 unsigned long new_random_seed = InitRND(random_seed);
12126 TapeStartRecording(new_random_seed);
12128 #if defined(NETWORK_AVALIABLE)
12129 if (init_network_game)
12131 SendToServer_StartPlaying();
12142 static unsigned long game_frame_delay = 0;
12143 unsigned long game_frame_delay_value;
12144 byte *recorded_player_action;
12145 byte summarized_player_action = 0;
12146 byte tape_action[MAX_PLAYERS];
12149 /* detect endless loops, caused by custom element programming */
12150 if (recursion_loop_detected && recursion_loop_depth == 0)
12152 char *message = getStringCat3("Internal Error ! Element ",
12153 EL_NAME(recursion_loop_element),
12154 " caused endless loop ! Quit the game ?");
12156 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12157 EL_NAME(recursion_loop_element));
12159 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12161 recursion_loop_detected = FALSE; /* if game should be continued */
12168 if (game.restart_level)
12169 StartGameActions(options.network, setup.autorecord, level.random_seed);
12171 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12173 if (level.native_em_level->lev->home == 0) /* all players at home */
12175 PlayerWins(local_player);
12177 AllPlayersGone = TRUE;
12179 level.native_em_level->lev->home = -1;
12182 if (level.native_em_level->ply[0]->alive == 0 &&
12183 level.native_em_level->ply[1]->alive == 0 &&
12184 level.native_em_level->ply[2]->alive == 0 &&
12185 level.native_em_level->ply[3]->alive == 0) /* all dead */
12186 AllPlayersGone = TRUE;
12189 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12192 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12195 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12198 game_frame_delay_value =
12199 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12201 if (tape.playing && tape.warp_forward && !tape.pausing)
12202 game_frame_delay_value = 0;
12204 /* ---------- main game synchronization point ---------- */
12206 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12208 if (network_playing && !network_player_action_received)
12210 /* try to get network player actions in time */
12212 #if defined(NETWORK_AVALIABLE)
12213 /* last chance to get network player actions without main loop delay */
12214 HandleNetworking();
12217 /* game was quit by network peer */
12218 if (game_status != GAME_MODE_PLAYING)
12221 if (!network_player_action_received)
12222 return; /* failed to get network player actions in time */
12224 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12230 /* at this point we know that we really continue executing the game */
12232 network_player_action_received = FALSE;
12234 /* when playing tape, read previously recorded player input from tape data */
12235 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12238 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12243 if (tape.set_centered_player)
12245 game.centered_player_nr_next = tape.centered_player_nr_next;
12246 game.set_centered_player = TRUE;
12249 #if 0 /* USE_NEW_PLAYER_ASSIGNMENTS */
12250 for (i = 0; i < MAX_PLAYERS; i++)
12252 summarized_player_action |= stored_player[i].mapped_action;
12254 if (!network_playing)
12255 stored_player[i].effective_action = stored_player[i].mapped_action;
12258 for (i = 0; i < MAX_PLAYERS; i++)
12260 summarized_player_action |= stored_player[i].action;
12262 if (!network_playing)
12263 stored_player[i].effective_action = stored_player[i].action;
12267 #if defined(NETWORK_AVALIABLE)
12268 if (network_playing)
12269 SendToServer_MovePlayer(summarized_player_action);
12272 if (!options.network && !setup.team_mode)
12273 local_player->effective_action = summarized_player_action;
12275 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12277 for (i = 0; i < MAX_PLAYERS; i++)
12278 stored_player[i].effective_action =
12279 (i == game.centered_player_nr ? summarized_player_action : 0);
12282 #if 0 /* USE_NEW_PLAYER_ASSIGNMENTS */
12283 if (recorded_player_action != NULL)
12284 for (i = 0; i < MAX_PLAYERS; i++)
12285 stored_player[i].effective_action =
12286 recorded_player_action[map_player_action[i]];
12288 if (recorded_player_action != NULL)
12289 for (i = 0; i < MAX_PLAYERS; i++)
12290 stored_player[i].effective_action =
12291 recorded_player_action[i];
12294 for (i = 0; i < MAX_PLAYERS; i++)
12296 #if 0 /* USE_NEW_PLAYER_ASSIGNMENTS */
12297 tape_action[i] = stored_player[i].action;
12299 tape_action[i] = stored_player[i].effective_action;
12302 /* (this can only happen in the R'n'D game engine) */
12303 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12304 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12307 /* only record actions from input devices, but not programmed actions */
12308 if (tape.recording)
12309 TapeRecordAction(tape_action);
12311 #if USE_NEW_PLAYER_ASSIGNMENTS
12315 for (i = 0; i < MAX_PLAYERS; i++)
12316 stored_player[i].mapped_action = stored_player[map_player_action[i]].action;
12321 byte unmapped_action[MAX_PLAYERS];
12323 for (i = 0; i < MAX_PLAYERS; i++)
12324 unmapped_action[i] = stored_player[i].effective_action;
12326 for (i = 0; i < MAX_PLAYERS; i++)
12327 stored_player[i].effective_action = unmapped_action[map_player_action[i]];
12331 for (i = 0; i < MAX_PLAYERS; i++)
12332 printf("::: %d: %d [%d]\n", i, stored_player[i].effective_action,
12333 map_player_action[i]);
12338 for (i = 0; i < MAX_PLAYERS; i++)
12339 stored_player[i].mapped_action = stored_player[i].action;
12342 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12344 GameActions_EM_Main();
12352 void GameActions_EM_Main()
12354 byte effective_action[MAX_PLAYERS];
12355 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12358 for (i = 0; i < MAX_PLAYERS; i++)
12359 effective_action[i] = stored_player[i].effective_action;
12361 GameActions_EM(effective_action, warp_mode);
12365 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12368 void GameActions_RND()
12370 int magic_wall_x = 0, magic_wall_y = 0;
12371 int i, x, y, element, graphic;
12373 InitPlayfieldScanModeVars();
12375 #if USE_ONE_MORE_CHANGE_PER_FRAME
12376 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12378 SCAN_PLAYFIELD(x, y)
12380 ChangeCount[x][y] = 0;
12381 ChangeEvent[x][y] = -1;
12386 if (game.set_centered_player)
12388 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12390 /* switching to "all players" only possible if all players fit to screen */
12391 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12393 game.centered_player_nr_next = game.centered_player_nr;
12394 game.set_centered_player = FALSE;
12397 /* do not switch focus to non-existing (or non-active) player */
12398 if (game.centered_player_nr_next >= 0 &&
12399 !stored_player[game.centered_player_nr_next].active)
12401 game.centered_player_nr_next = game.centered_player_nr;
12402 game.set_centered_player = FALSE;
12406 if (game.set_centered_player &&
12407 ScreenMovPos == 0) /* screen currently aligned at tile position */
12411 if (game.centered_player_nr_next == -1)
12413 setScreenCenteredToAllPlayers(&sx, &sy);
12417 sx = stored_player[game.centered_player_nr_next].jx;
12418 sy = stored_player[game.centered_player_nr_next].jy;
12421 game.centered_player_nr = game.centered_player_nr_next;
12422 game.set_centered_player = FALSE;
12424 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12425 DrawGameDoorValues();
12428 for (i = 0; i < MAX_PLAYERS; i++)
12430 int actual_player_action = stored_player[i].effective_action;
12433 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12434 - rnd_equinox_tetrachloride 048
12435 - rnd_equinox_tetrachloride_ii 096
12436 - rnd_emanuel_schmieg 002
12437 - doctor_sloan_ww 001, 020
12439 if (stored_player[i].MovPos == 0)
12440 CheckGravityMovement(&stored_player[i]);
12443 /* overwrite programmed action with tape action */
12444 if (stored_player[i].programmed_action)
12445 actual_player_action = stored_player[i].programmed_action;
12447 PlayerActions(&stored_player[i], actual_player_action);
12449 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12452 ScrollScreen(NULL, SCROLL_GO_ON);
12454 /* for backwards compatibility, the following code emulates a fixed bug that
12455 occured when pushing elements (causing elements that just made their last
12456 pushing step to already (if possible) make their first falling step in the
12457 same game frame, which is bad); this code is also needed to use the famous
12458 "spring push bug" which is used in older levels and might be wanted to be
12459 used also in newer levels, but in this case the buggy pushing code is only
12460 affecting the "spring" element and no other elements */
12462 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12464 for (i = 0; i < MAX_PLAYERS; i++)
12466 struct PlayerInfo *player = &stored_player[i];
12467 int x = player->jx;
12468 int y = player->jy;
12470 if (player->active && player->is_pushing && player->is_moving &&
12472 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12473 Feld[x][y] == EL_SPRING))
12475 ContinueMoving(x, y);
12477 /* continue moving after pushing (this is actually a bug) */
12478 if (!IS_MOVING(x, y))
12479 Stop[x][y] = FALSE;
12485 debug_print_timestamp(0, "start main loop profiling");
12488 SCAN_PLAYFIELD(x, y)
12490 ChangeCount[x][y] = 0;
12491 ChangeEvent[x][y] = -1;
12493 /* this must be handled before main playfield loop */
12494 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12497 if (MovDelay[x][y] <= 0)
12501 #if USE_NEW_SNAP_DELAY
12502 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12505 if (MovDelay[x][y] <= 0)
12508 TEST_DrawLevelField(x, y);
12510 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12516 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12518 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12519 printf("GameActions(): This should never happen!\n");
12521 ChangePage[x][y] = -1;
12525 Stop[x][y] = FALSE;
12526 if (WasJustMoving[x][y] > 0)
12527 WasJustMoving[x][y]--;
12528 if (WasJustFalling[x][y] > 0)
12529 WasJustFalling[x][y]--;
12530 if (CheckCollision[x][y] > 0)
12531 CheckCollision[x][y]--;
12532 if (CheckImpact[x][y] > 0)
12533 CheckImpact[x][y]--;
12537 /* reset finished pushing action (not done in ContinueMoving() to allow
12538 continuous pushing animation for elements with zero push delay) */
12539 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12541 ResetGfxAnimation(x, y);
12542 TEST_DrawLevelField(x, y);
12546 if (IS_BLOCKED(x, y))
12550 Blocked2Moving(x, y, &oldx, &oldy);
12551 if (!IS_MOVING(oldx, oldy))
12553 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12554 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12555 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12556 printf("GameActions(): This should never happen!\n");
12563 debug_print_timestamp(0, "- time for pre-main loop:");
12566 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12567 SCAN_PLAYFIELD(x, y)
12569 element = Feld[x][y];
12570 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12575 int element2 = element;
12576 int graphic2 = graphic;
12578 int element2 = Feld[x][y];
12579 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12581 int last_gfx_frame = GfxFrame[x][y];
12583 if (graphic_info[graphic2].anim_global_sync)
12584 GfxFrame[x][y] = FrameCounter;
12585 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12586 GfxFrame[x][y] = CustomValue[x][y];
12587 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12588 GfxFrame[x][y] = element_info[element2].collect_score;
12589 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12590 GfxFrame[x][y] = ChangeDelay[x][y];
12592 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12593 DrawLevelGraphicAnimation(x, y, graphic2);
12596 ResetGfxFrame(x, y, TRUE);
12600 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12601 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12602 ResetRandomAnimationValue(x, y);
12606 SetRandomAnimationValue(x, y);
12610 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12613 #endif // -------------------- !!! TEST ONLY !!! --------------------
12616 debug_print_timestamp(0, "- time for TEST loop: -->");
12619 SCAN_PLAYFIELD(x, y)
12621 element = Feld[x][y];
12622 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12624 ResetGfxFrame(x, y, TRUE);
12626 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12627 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12628 ResetRandomAnimationValue(x, y);
12630 SetRandomAnimationValue(x, y);
12632 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12634 if (IS_INACTIVE(element))
12636 if (IS_ANIMATED(graphic))
12637 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12642 /* this may take place after moving, so 'element' may have changed */
12643 if (IS_CHANGING(x, y) &&
12644 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12646 int page = element_info[element].event_page_nr[CE_DELAY];
12649 HandleElementChange(x, y, page);
12651 if (CAN_CHANGE(element))
12652 HandleElementChange(x, y, page);
12654 if (HAS_ACTION(element))
12655 ExecuteCustomElementAction(x, y, element, page);
12658 element = Feld[x][y];
12659 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12662 #if 0 // ---------------------------------------------------------------------
12664 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12668 element = Feld[x][y];
12669 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12671 if (IS_ANIMATED(graphic) &&
12672 !IS_MOVING(x, y) &&
12674 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12676 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12677 TEST_DrawTwinkleOnField(x, y);
12679 else if (IS_MOVING(x, y))
12680 ContinueMoving(x, y);
12687 case EL_EM_EXIT_OPEN:
12688 case EL_SP_EXIT_OPEN:
12689 case EL_STEEL_EXIT_OPEN:
12690 case EL_EM_STEEL_EXIT_OPEN:
12691 case EL_SP_TERMINAL:
12692 case EL_SP_TERMINAL_ACTIVE:
12693 case EL_EXTRA_TIME:
12694 case EL_SHIELD_NORMAL:
12695 case EL_SHIELD_DEADLY:
12696 if (IS_ANIMATED(graphic))
12697 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12700 case EL_DYNAMITE_ACTIVE:
12701 case EL_EM_DYNAMITE_ACTIVE:
12702 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12703 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12704 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12705 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12706 case EL_SP_DISK_RED_ACTIVE:
12707 CheckDynamite(x, y);
12710 case EL_AMOEBA_GROWING:
12711 AmoebeWaechst(x, y);
12714 case EL_AMOEBA_SHRINKING:
12715 AmoebaDisappearing(x, y);
12718 #if !USE_NEW_AMOEBA_CODE
12719 case EL_AMOEBA_WET:
12720 case EL_AMOEBA_DRY:
12721 case EL_AMOEBA_FULL:
12723 case EL_EMC_DRIPPER:
12724 AmoebeAbleger(x, y);
12728 case EL_GAME_OF_LIFE:
12733 case EL_EXIT_CLOSED:
12737 case EL_EM_EXIT_CLOSED:
12741 case EL_STEEL_EXIT_CLOSED:
12742 CheckExitSteel(x, y);
12745 case EL_EM_STEEL_EXIT_CLOSED:
12746 CheckExitSteelEM(x, y);
12749 case EL_SP_EXIT_CLOSED:
12753 case EL_EXPANDABLE_WALL_GROWING:
12754 case EL_EXPANDABLE_STEELWALL_GROWING:
12755 MauerWaechst(x, y);
12758 case EL_EXPANDABLE_WALL:
12759 case EL_EXPANDABLE_WALL_HORIZONTAL:
12760 case EL_EXPANDABLE_WALL_VERTICAL:
12761 case EL_EXPANDABLE_WALL_ANY:
12762 case EL_BD_EXPANDABLE_WALL:
12763 MauerAbleger(x, y);
12766 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12767 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12768 case EL_EXPANDABLE_STEELWALL_ANY:
12769 MauerAblegerStahl(x, y);
12773 CheckForDragon(x, y);
12779 case EL_ELEMENT_SNAPPING:
12780 case EL_DIAGONAL_SHRINKING:
12781 case EL_DIAGONAL_GROWING:
12784 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12786 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12791 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12792 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12797 #else // ---------------------------------------------------------------------
12799 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12803 element = Feld[x][y];
12804 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12806 if (IS_ANIMATED(graphic) &&
12807 !IS_MOVING(x, y) &&
12809 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12811 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12812 TEST_DrawTwinkleOnField(x, y);
12814 else if ((element == EL_ACID ||
12815 element == EL_EXIT_OPEN ||
12816 element == EL_EM_EXIT_OPEN ||
12817 element == EL_SP_EXIT_OPEN ||
12818 element == EL_STEEL_EXIT_OPEN ||
12819 element == EL_EM_STEEL_EXIT_OPEN ||
12820 element == EL_SP_TERMINAL ||
12821 element == EL_SP_TERMINAL_ACTIVE ||
12822 element == EL_EXTRA_TIME ||
12823 element == EL_SHIELD_NORMAL ||
12824 element == EL_SHIELD_DEADLY) &&
12825 IS_ANIMATED(graphic))
12826 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12827 else if (IS_MOVING(x, y))
12828 ContinueMoving(x, y);
12829 else if (IS_ACTIVE_BOMB(element))
12830 CheckDynamite(x, y);
12831 else if (element == EL_AMOEBA_GROWING)
12832 AmoebeWaechst(x, y);
12833 else if (element == EL_AMOEBA_SHRINKING)
12834 AmoebaDisappearing(x, y);
12836 #if !USE_NEW_AMOEBA_CODE
12837 else if (IS_AMOEBALIVE(element))
12838 AmoebeAbleger(x, y);
12841 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12843 else if (element == EL_EXIT_CLOSED)
12845 else if (element == EL_EM_EXIT_CLOSED)
12847 else if (element == EL_STEEL_EXIT_CLOSED)
12848 CheckExitSteel(x, y);
12849 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12850 CheckExitSteelEM(x, y);
12851 else if (element == EL_SP_EXIT_CLOSED)
12853 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12854 element == EL_EXPANDABLE_STEELWALL_GROWING)
12855 MauerWaechst(x, y);
12856 else if (element == EL_EXPANDABLE_WALL ||
12857 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12858 element == EL_EXPANDABLE_WALL_VERTICAL ||
12859 element == EL_EXPANDABLE_WALL_ANY ||
12860 element == EL_BD_EXPANDABLE_WALL)
12861 MauerAbleger(x, y);
12862 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12863 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12864 element == EL_EXPANDABLE_STEELWALL_ANY)
12865 MauerAblegerStahl(x, y);
12866 else if (element == EL_FLAMES)
12867 CheckForDragon(x, y);
12868 else if (element == EL_EXPLOSION)
12869 ; /* drawing of correct explosion animation is handled separately */
12870 else if (element == EL_ELEMENT_SNAPPING ||
12871 element == EL_DIAGONAL_SHRINKING ||
12872 element == EL_DIAGONAL_GROWING)
12874 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12876 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12878 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12879 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12881 #endif // ---------------------------------------------------------------------
12883 if (IS_BELT_ACTIVE(element))
12884 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12886 if (game.magic_wall_active)
12888 int jx = local_player->jx, jy = local_player->jy;
12890 /* play the element sound at the position nearest to the player */
12891 if ((element == EL_MAGIC_WALL_FULL ||
12892 element == EL_MAGIC_WALL_ACTIVE ||
12893 element == EL_MAGIC_WALL_EMPTYING ||
12894 element == EL_BD_MAGIC_WALL_FULL ||
12895 element == EL_BD_MAGIC_WALL_ACTIVE ||
12896 element == EL_BD_MAGIC_WALL_EMPTYING ||
12897 element == EL_DC_MAGIC_WALL_FULL ||
12898 element == EL_DC_MAGIC_WALL_ACTIVE ||
12899 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12900 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12909 debug_print_timestamp(0, "- time for MAIN loop: -->");
12912 #if USE_NEW_AMOEBA_CODE
12913 /* new experimental amoeba growth stuff */
12914 if (!(FrameCounter % 8))
12916 static unsigned long random = 1684108901;
12918 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12920 x = RND(lev_fieldx);
12921 y = RND(lev_fieldy);
12922 element = Feld[x][y];
12924 if (!IS_PLAYER(x,y) &&
12925 (element == EL_EMPTY ||
12926 CAN_GROW_INTO(element) ||
12927 element == EL_QUICKSAND_EMPTY ||
12928 element == EL_QUICKSAND_FAST_EMPTY ||
12929 element == EL_ACID_SPLASH_LEFT ||
12930 element == EL_ACID_SPLASH_RIGHT))
12932 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12933 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12934 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12935 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12936 Feld[x][y] = EL_AMOEBA_DROP;
12939 random = random * 129 + 1;
12945 if (game.explosions_delayed)
12948 game.explosions_delayed = FALSE;
12950 SCAN_PLAYFIELD(x, y)
12952 element = Feld[x][y];
12954 if (ExplodeField[x][y])
12955 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12956 else if (element == EL_EXPLOSION)
12957 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12959 ExplodeField[x][y] = EX_TYPE_NONE;
12962 game.explosions_delayed = TRUE;
12965 if (game.magic_wall_active)
12967 if (!(game.magic_wall_time_left % 4))
12969 int element = Feld[magic_wall_x][magic_wall_y];
12971 if (element == EL_BD_MAGIC_WALL_FULL ||
12972 element == EL_BD_MAGIC_WALL_ACTIVE ||
12973 element == EL_BD_MAGIC_WALL_EMPTYING)
12974 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12975 else if (element == EL_DC_MAGIC_WALL_FULL ||
12976 element == EL_DC_MAGIC_WALL_ACTIVE ||
12977 element == EL_DC_MAGIC_WALL_EMPTYING)
12978 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12980 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12983 if (game.magic_wall_time_left > 0)
12985 game.magic_wall_time_left--;
12987 if (!game.magic_wall_time_left)
12989 SCAN_PLAYFIELD(x, y)
12991 element = Feld[x][y];
12993 if (element == EL_MAGIC_WALL_ACTIVE ||
12994 element == EL_MAGIC_WALL_FULL)
12996 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12997 TEST_DrawLevelField(x, y);
12999 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13000 element == EL_BD_MAGIC_WALL_FULL)
13002 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13003 TEST_DrawLevelField(x, y);
13005 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13006 element == EL_DC_MAGIC_WALL_FULL)
13008 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13009 TEST_DrawLevelField(x, y);
13013 game.magic_wall_active = FALSE;
13018 if (game.light_time_left > 0)
13020 game.light_time_left--;
13022 if (game.light_time_left == 0)
13023 RedrawAllLightSwitchesAndInvisibleElements();
13026 if (game.timegate_time_left > 0)
13028 game.timegate_time_left--;
13030 if (game.timegate_time_left == 0)
13031 CloseAllOpenTimegates();
13034 if (game.lenses_time_left > 0)
13036 game.lenses_time_left--;
13038 if (game.lenses_time_left == 0)
13039 RedrawAllInvisibleElementsForLenses();
13042 if (game.magnify_time_left > 0)
13044 game.magnify_time_left--;
13046 if (game.magnify_time_left == 0)
13047 RedrawAllInvisibleElementsForMagnifier();
13050 for (i = 0; i < MAX_PLAYERS; i++)
13052 struct PlayerInfo *player = &stored_player[i];
13054 if (SHIELD_ON(player))
13056 if (player->shield_deadly_time_left)
13057 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13058 else if (player->shield_normal_time_left)
13059 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13063 #if USE_DELAYED_GFX_REDRAW
13064 SCAN_PLAYFIELD(x, y)
13067 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13069 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13070 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13073 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13074 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13076 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13077 DrawLevelField(x, y);
13079 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13080 DrawLevelFieldCrumbledSand(x, y);
13082 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13083 DrawLevelFieldCrumbledSandNeighbours(x, y);
13085 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13086 DrawTwinkleOnField(x, y);
13089 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13096 PlayAllPlayersSound();
13098 if (options.debug) /* calculate frames per second */
13100 static unsigned long fps_counter = 0;
13101 static int fps_frames = 0;
13102 unsigned long fps_delay_ms = Counter() - fps_counter;
13106 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13108 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13111 fps_counter = Counter();
13114 redraw_mask |= REDRAW_FPS;
13117 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13119 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13121 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13123 local_player->show_envelope = 0;
13127 debug_print_timestamp(0, "stop main loop profiling ");
13128 printf("----------------------------------------------------------\n");
13131 /* use random number generator in every frame to make it less predictable */
13132 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13136 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13138 int min_x = x, min_y = y, max_x = x, max_y = y;
13141 for (i = 0; i < MAX_PLAYERS; i++)
13143 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13145 if (!stored_player[i].active || &stored_player[i] == player)
13148 min_x = MIN(min_x, jx);
13149 min_y = MIN(min_y, jy);
13150 max_x = MAX(max_x, jx);
13151 max_y = MAX(max_y, jy);
13154 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13157 static boolean AllPlayersInVisibleScreen()
13161 for (i = 0; i < MAX_PLAYERS; i++)
13163 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13165 if (!stored_player[i].active)
13168 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13175 void ScrollLevel(int dx, int dy)
13178 /* (directly solved in BlitBitmap() now) */
13179 static Bitmap *bitmap_db_field2 = NULL;
13180 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13187 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13188 /* only horizontal XOR vertical scroll direction allowed */
13189 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13194 /* (directly solved in BlitBitmap() now) */
13195 if (bitmap_db_field2 == NULL)
13196 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13198 /* needed when blitting directly to same bitmap -- should not be needed with
13199 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13200 BlitBitmap(drawto_field, bitmap_db_field2,
13201 FX + TILEX * (dx == -1) - softscroll_offset,
13202 FY + TILEY * (dy == -1) - softscroll_offset,
13203 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13204 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13205 FX + TILEX * (dx == 1) - softscroll_offset,
13206 FY + TILEY * (dy == 1) - softscroll_offset);
13207 BlitBitmap(bitmap_db_field2, drawto_field,
13208 FX + TILEX * (dx == 1) - softscroll_offset,
13209 FY + TILEY * (dy == 1) - softscroll_offset,
13210 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13211 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13212 FX + TILEX * (dx == 1) - softscroll_offset,
13213 FY + TILEY * (dy == 1) - softscroll_offset);
13218 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13219 int xsize = (BX2 - BX1 + 1);
13220 int ysize = (BY2 - BY1 + 1);
13221 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13222 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13223 int step = (start < end ? +1 : -1);
13225 for (i = start; i != end; i += step)
13227 BlitBitmap(drawto_field, drawto_field,
13228 FX + TILEX * (dx != 0 ? i + step : 0),
13229 FY + TILEY * (dy != 0 ? i + step : 0),
13230 TILEX * (dx != 0 ? 1 : xsize),
13231 TILEY * (dy != 0 ? 1 : ysize),
13232 FX + TILEX * (dx != 0 ? i : 0),
13233 FY + TILEY * (dy != 0 ? i : 0));
13238 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13240 BlitBitmap(drawto_field, drawto_field,
13241 FX + TILEX * (dx == -1) - softscroll_offset,
13242 FY + TILEY * (dy == -1) - softscroll_offset,
13243 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13244 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13245 FX + TILEX * (dx == 1) - softscroll_offset,
13246 FY + TILEY * (dy == 1) - softscroll_offset);
13252 x = (dx == 1 ? BX1 : BX2);
13253 for (y = BY1; y <= BY2; y++)
13254 DrawScreenField(x, y);
13259 y = (dy == 1 ? BY1 : BY2);
13260 for (x = BX1; x <= BX2; x++)
13261 DrawScreenField(x, y);
13264 redraw_mask |= REDRAW_FIELD;
13267 static boolean canFallDown(struct PlayerInfo *player)
13269 int jx = player->jx, jy = player->jy;
13271 return (IN_LEV_FIELD(jx, jy + 1) &&
13272 (IS_FREE(jx, jy + 1) ||
13273 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13274 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13275 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13278 static boolean canPassField(int x, int y, int move_dir)
13280 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13281 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13282 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13283 int nextx = x + dx;
13284 int nexty = y + dy;
13285 int element = Feld[x][y];
13287 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13288 !CAN_MOVE(element) &&
13289 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13290 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13291 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13294 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13296 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13297 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13298 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13302 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13303 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13304 (IS_DIGGABLE(Feld[newx][newy]) ||
13305 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13306 canPassField(newx, newy, move_dir)));
13309 static void CheckGravityMovement(struct PlayerInfo *player)
13311 #if USE_PLAYER_GRAVITY
13312 if (player->gravity && !player->programmed_action)
13314 if (game.gravity && !player->programmed_action)
13317 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13318 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13319 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13320 int jx = player->jx, jy = player->jy;
13321 boolean player_is_moving_to_valid_field =
13322 (!player_is_snapping &&
13323 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13324 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13325 boolean player_can_fall_down = canFallDown(player);
13327 if (player_can_fall_down &&
13328 !player_is_moving_to_valid_field)
13329 player->programmed_action = MV_DOWN;
13333 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13335 return CheckGravityMovement(player);
13337 #if USE_PLAYER_GRAVITY
13338 if (player->gravity && !player->programmed_action)
13340 if (game.gravity && !player->programmed_action)
13343 int jx = player->jx, jy = player->jy;
13344 boolean field_under_player_is_free =
13345 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13346 boolean player_is_standing_on_valid_field =
13347 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13348 (IS_WALKABLE(Feld[jx][jy]) &&
13349 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13351 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13352 player->programmed_action = MV_DOWN;
13357 MovePlayerOneStep()
13358 -----------------------------------------------------------------------------
13359 dx, dy: direction (non-diagonal) to try to move the player to
13360 real_dx, real_dy: direction as read from input device (can be diagonal)
13363 boolean MovePlayerOneStep(struct PlayerInfo *player,
13364 int dx, int dy, int real_dx, int real_dy)
13366 int jx = player->jx, jy = player->jy;
13367 int new_jx = jx + dx, new_jy = jy + dy;
13368 #if !USE_FIXED_DONT_RUN_INTO
13372 boolean player_can_move = !player->cannot_move;
13374 if (!player->active || (!dx && !dy))
13375 return MP_NO_ACTION;
13377 player->MovDir = (dx < 0 ? MV_LEFT :
13378 dx > 0 ? MV_RIGHT :
13380 dy > 0 ? MV_DOWN : MV_NONE);
13382 if (!IN_LEV_FIELD(new_jx, new_jy))
13383 return MP_NO_ACTION;
13385 if (!player_can_move)
13387 if (player->MovPos == 0)
13389 player->is_moving = FALSE;
13390 player->is_digging = FALSE;
13391 player->is_collecting = FALSE;
13392 player->is_snapping = FALSE;
13393 player->is_pushing = FALSE;
13398 if (!options.network && game.centered_player_nr == -1 &&
13399 !AllPlayersInSight(player, new_jx, new_jy))
13400 return MP_NO_ACTION;
13402 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13403 return MP_NO_ACTION;
13406 #if !USE_FIXED_DONT_RUN_INTO
13407 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13409 /* (moved to DigField()) */
13410 if (player_can_move && DONT_RUN_INTO(element))
13412 if (element == EL_ACID && dx == 0 && dy == 1)
13414 SplashAcid(new_jx, new_jy);
13415 Feld[jx][jy] = EL_PLAYER_1;
13416 InitMovingField(jx, jy, MV_DOWN);
13417 Store[jx][jy] = EL_ACID;
13418 ContinueMoving(jx, jy);
13419 BuryPlayer(player);
13422 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13428 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13429 if (can_move != MP_MOVING)
13432 /* check if DigField() has caused relocation of the player */
13433 if (player->jx != jx || player->jy != jy)
13434 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13436 StorePlayer[jx][jy] = 0;
13437 player->last_jx = jx;
13438 player->last_jy = jy;
13439 player->jx = new_jx;
13440 player->jy = new_jy;
13441 StorePlayer[new_jx][new_jy] = player->element_nr;
13443 if (player->move_delay_value_next != -1)
13445 player->move_delay_value = player->move_delay_value_next;
13446 player->move_delay_value_next = -1;
13450 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13452 player->step_counter++;
13454 PlayerVisit[jx][jy] = FrameCounter;
13456 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13457 player->is_moving = TRUE;
13461 /* should better be called in MovePlayer(), but this breaks some tapes */
13462 ScrollPlayer(player, SCROLL_INIT);
13468 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13470 int jx = player->jx, jy = player->jy;
13471 int old_jx = jx, old_jy = jy;
13472 int moved = MP_NO_ACTION;
13474 if (!player->active)
13479 if (player->MovPos == 0)
13481 player->is_moving = FALSE;
13482 player->is_digging = FALSE;
13483 player->is_collecting = FALSE;
13484 player->is_snapping = FALSE;
13485 player->is_pushing = FALSE;
13491 if (player->move_delay > 0)
13494 player->move_delay = -1; /* set to "uninitialized" value */
13496 /* store if player is automatically moved to next field */
13497 player->is_auto_moving = (player->programmed_action != MV_NONE);
13499 /* remove the last programmed player action */
13500 player->programmed_action = 0;
13502 if (player->MovPos)
13504 /* should only happen if pre-1.2 tape recordings are played */
13505 /* this is only for backward compatibility */
13507 int original_move_delay_value = player->move_delay_value;
13510 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13514 /* scroll remaining steps with finest movement resolution */
13515 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13517 while (player->MovPos)
13519 ScrollPlayer(player, SCROLL_GO_ON);
13520 ScrollScreen(NULL, SCROLL_GO_ON);
13522 AdvanceFrameAndPlayerCounters(player->index_nr);
13528 player->move_delay_value = original_move_delay_value;
13531 player->is_active = FALSE;
13533 if (player->last_move_dir & MV_HORIZONTAL)
13535 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13536 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13540 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13541 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13544 #if USE_FIXED_BORDER_RUNNING_GFX
13545 if (!moved && !player->is_active)
13547 player->is_moving = FALSE;
13548 player->is_digging = FALSE;
13549 player->is_collecting = FALSE;
13550 player->is_snapping = FALSE;
13551 player->is_pushing = FALSE;
13559 if (moved & MP_MOVING && !ScreenMovPos &&
13560 (player->index_nr == game.centered_player_nr ||
13561 game.centered_player_nr == -1))
13563 if (moved & MP_MOVING && !ScreenMovPos &&
13564 (player == local_player || !options.network))
13567 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13568 int offset = game.scroll_delay_value;
13570 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13572 /* actual player has left the screen -- scroll in that direction */
13573 if (jx != old_jx) /* player has moved horizontally */
13574 scroll_x += (jx - old_jx);
13575 else /* player has moved vertically */
13576 scroll_y += (jy - old_jy);
13580 if (jx != old_jx) /* player has moved horizontally */
13582 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13583 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13584 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13586 /* don't scroll over playfield boundaries */
13587 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13588 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13590 /* don't scroll more than one field at a time */
13591 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13593 /* don't scroll against the player's moving direction */
13594 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13595 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13596 scroll_x = old_scroll_x;
13598 else /* player has moved vertically */
13600 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13601 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13602 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13604 /* don't scroll over playfield boundaries */
13605 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13606 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13608 /* don't scroll more than one field at a time */
13609 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13611 /* don't scroll against the player's moving direction */
13612 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13613 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13614 scroll_y = old_scroll_y;
13618 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13621 if (!options.network && game.centered_player_nr == -1 &&
13622 !AllPlayersInVisibleScreen())
13624 scroll_x = old_scroll_x;
13625 scroll_y = old_scroll_y;
13629 if (!options.network && !AllPlayersInVisibleScreen())
13631 scroll_x = old_scroll_x;
13632 scroll_y = old_scroll_y;
13637 ScrollScreen(player, SCROLL_INIT);
13638 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13643 player->StepFrame = 0;
13645 if (moved & MP_MOVING)
13647 if (old_jx != jx && old_jy == jy)
13648 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13649 else if (old_jx == jx && old_jy != jy)
13650 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13652 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13654 player->last_move_dir = player->MovDir;
13655 player->is_moving = TRUE;
13656 player->is_snapping = FALSE;
13657 player->is_switching = FALSE;
13658 player->is_dropping = FALSE;
13659 player->is_dropping_pressed = FALSE;
13660 player->drop_pressed_delay = 0;
13663 /* should better be called here than above, but this breaks some tapes */
13664 ScrollPlayer(player, SCROLL_INIT);
13669 CheckGravityMovementWhenNotMoving(player);
13671 player->is_moving = FALSE;
13673 /* at this point, the player is allowed to move, but cannot move right now
13674 (e.g. because of something blocking the way) -- ensure that the player
13675 is also allowed to move in the next frame (in old versions before 3.1.1,
13676 the player was forced to wait again for eight frames before next try) */
13678 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13679 player->move_delay = 0; /* allow direct movement in the next frame */
13682 if (player->move_delay == -1) /* not yet initialized by DigField() */
13683 player->move_delay = player->move_delay_value;
13685 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13687 TestIfPlayerTouchesBadThing(jx, jy);
13688 TestIfPlayerTouchesCustomElement(jx, jy);
13691 if (!player->active)
13692 RemovePlayer(player);
13697 void ScrollPlayer(struct PlayerInfo *player, int mode)
13699 int jx = player->jx, jy = player->jy;
13700 int last_jx = player->last_jx, last_jy = player->last_jy;
13701 int move_stepsize = TILEX / player->move_delay_value;
13703 #if USE_NEW_PLAYER_SPEED
13704 if (!player->active)
13707 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13710 if (!player->active || player->MovPos == 0)
13714 if (mode == SCROLL_INIT)
13716 player->actual_frame_counter = FrameCounter;
13717 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13719 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13720 Feld[last_jx][last_jy] == EL_EMPTY)
13722 int last_field_block_delay = 0; /* start with no blocking at all */
13723 int block_delay_adjustment = player->block_delay_adjustment;
13725 /* if player blocks last field, add delay for exactly one move */
13726 if (player->block_last_field)
13728 last_field_block_delay += player->move_delay_value;
13730 /* when blocking enabled, prevent moving up despite gravity */
13731 #if USE_PLAYER_GRAVITY
13732 if (player->gravity && player->MovDir == MV_UP)
13733 block_delay_adjustment = -1;
13735 if (game.gravity && player->MovDir == MV_UP)
13736 block_delay_adjustment = -1;
13740 /* add block delay adjustment (also possible when not blocking) */
13741 last_field_block_delay += block_delay_adjustment;
13743 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13744 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13747 #if USE_NEW_PLAYER_SPEED
13748 if (player->MovPos != 0) /* player has not yet reached destination */
13754 else if (!FrameReached(&player->actual_frame_counter, 1))
13757 #if USE_NEW_PLAYER_SPEED
13758 if (player->MovPos != 0)
13760 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13761 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13763 /* before DrawPlayer() to draw correct player graphic for this case */
13764 if (player->MovPos == 0)
13765 CheckGravityMovement(player);
13768 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13769 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13771 /* before DrawPlayer() to draw correct player graphic for this case */
13772 if (player->MovPos == 0)
13773 CheckGravityMovement(player);
13776 if (player->MovPos == 0) /* player reached destination field */
13778 if (player->move_delay_reset_counter > 0)
13780 player->move_delay_reset_counter--;
13782 if (player->move_delay_reset_counter == 0)
13784 /* continue with normal speed after quickly moving through gate */
13785 HALVE_PLAYER_SPEED(player);
13787 /* be able to make the next move without delay */
13788 player->move_delay = 0;
13792 player->last_jx = jx;
13793 player->last_jy = jy;
13795 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13796 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13798 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13800 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13801 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13803 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13805 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13806 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13808 DrawPlayer(player); /* needed here only to cleanup last field */
13809 RemovePlayer(player);
13811 if (local_player->friends_still_needed == 0 ||
13812 IS_SP_ELEMENT(Feld[jx][jy]))
13813 PlayerWins(player);
13816 /* this breaks one level: "machine", level 000 */
13818 int move_direction = player->MovDir;
13819 int enter_side = MV_DIR_OPPOSITE(move_direction);
13820 int leave_side = move_direction;
13821 int old_jx = last_jx;
13822 int old_jy = last_jy;
13823 int old_element = Feld[old_jx][old_jy];
13824 int new_element = Feld[jx][jy];
13826 if (IS_CUSTOM_ELEMENT(old_element))
13827 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13829 player->index_bit, leave_side);
13831 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13832 CE_PLAYER_LEAVES_X,
13833 player->index_bit, leave_side);
13835 if (IS_CUSTOM_ELEMENT(new_element))
13836 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13837 player->index_bit, enter_side);
13839 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13840 CE_PLAYER_ENTERS_X,
13841 player->index_bit, enter_side);
13843 #if USE_FIX_CE_ACTION_WITH_PLAYER
13844 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13845 CE_MOVE_OF_X, move_direction);
13847 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13848 CE_MOVE_OF_X, move_direction);
13852 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13854 TestIfPlayerTouchesBadThing(jx, jy);
13855 TestIfPlayerTouchesCustomElement(jx, jy);
13857 /* needed because pushed element has not yet reached its destination,
13858 so it would trigger a change event at its previous field location */
13859 if (!player->is_pushing)
13860 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13862 if (!player->active)
13863 RemovePlayer(player);
13866 if (!local_player->LevelSolved && level.use_step_counter)
13876 if (TimeLeft <= 10 && setup.time_limit)
13877 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13880 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13882 DisplayGameControlValues();
13884 DrawGameValue_Time(TimeLeft);
13887 if (!TimeLeft && setup.time_limit)
13888 for (i = 0; i < MAX_PLAYERS; i++)
13889 KillPlayer(&stored_player[i]);
13892 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13894 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13896 DisplayGameControlValues();
13899 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13900 DrawGameValue_Time(TimePlayed);
13904 if (tape.single_step && tape.recording && !tape.pausing &&
13905 !player->programmed_action)
13906 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13910 void ScrollScreen(struct PlayerInfo *player, int mode)
13912 static unsigned long screen_frame_counter = 0;
13914 if (mode == SCROLL_INIT)
13916 /* set scrolling step size according to actual player's moving speed */
13917 ScrollStepSize = TILEX / player->move_delay_value;
13919 screen_frame_counter = FrameCounter;
13920 ScreenMovDir = player->MovDir;
13921 ScreenMovPos = player->MovPos;
13922 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13925 else if (!FrameReached(&screen_frame_counter, 1))
13930 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13931 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13932 redraw_mask |= REDRAW_FIELD;
13935 ScreenMovDir = MV_NONE;
13938 void TestIfPlayerTouchesCustomElement(int x, int y)
13940 static int xy[4][2] =
13947 static int trigger_sides[4][2] =
13949 /* center side border side */
13950 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13951 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13952 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13953 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13955 static int touch_dir[4] =
13957 MV_LEFT | MV_RIGHT,
13962 int center_element = Feld[x][y]; /* should always be non-moving! */
13965 for (i = 0; i < NUM_DIRECTIONS; i++)
13967 int xx = x + xy[i][0];
13968 int yy = y + xy[i][1];
13969 int center_side = trigger_sides[i][0];
13970 int border_side = trigger_sides[i][1];
13971 int border_element;
13973 if (!IN_LEV_FIELD(xx, yy))
13976 if (IS_PLAYER(x, y)) /* player found at center element */
13978 struct PlayerInfo *player = PLAYERINFO(x, y);
13980 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13981 border_element = Feld[xx][yy]; /* may be moving! */
13982 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13983 border_element = Feld[xx][yy];
13984 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13985 border_element = MovingOrBlocked2Element(xx, yy);
13987 continue; /* center and border element do not touch */
13989 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13990 player->index_bit, border_side);
13991 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13992 CE_PLAYER_TOUCHES_X,
13993 player->index_bit, border_side);
13995 #if USE_FIX_CE_ACTION_WITH_PLAYER
13997 /* use player element that is initially defined in the level playfield,
13998 not the player element that corresponds to the runtime player number
13999 (example: a level that contains EL_PLAYER_3 as the only player would
14000 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14001 int player_element = PLAYERINFO(x, y)->initial_element;
14003 CheckElementChangeBySide(xx, yy, border_element, player_element,
14004 CE_TOUCHING_X, border_side);
14008 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14010 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14012 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14014 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14015 continue; /* center and border element do not touch */
14018 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14019 player->index_bit, center_side);
14020 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14021 CE_PLAYER_TOUCHES_X,
14022 player->index_bit, center_side);
14024 #if USE_FIX_CE_ACTION_WITH_PLAYER
14026 /* use player element that is initially defined in the level playfield,
14027 not the player element that corresponds to the runtime player number
14028 (example: a level that contains EL_PLAYER_3 as the only player would
14029 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14030 int player_element = PLAYERINFO(xx, yy)->initial_element;
14032 CheckElementChangeBySide(x, y, center_element, player_element,
14033 CE_TOUCHING_X, center_side);
14042 #if USE_ELEMENT_TOUCHING_BUGFIX
14044 void TestIfElementTouchesCustomElement(int x, int y)
14046 static int xy[4][2] =
14053 static int trigger_sides[4][2] =
14055 /* center side border side */
14056 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14057 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14058 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14059 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14061 static int touch_dir[4] =
14063 MV_LEFT | MV_RIGHT,
14068 boolean change_center_element = FALSE;
14069 int center_element = Feld[x][y]; /* should always be non-moving! */
14070 int border_element_old[NUM_DIRECTIONS];
14073 for (i = 0; i < NUM_DIRECTIONS; i++)
14075 int xx = x + xy[i][0];
14076 int yy = y + xy[i][1];
14077 int border_element;
14079 border_element_old[i] = -1;
14081 if (!IN_LEV_FIELD(xx, yy))
14084 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14085 border_element = Feld[xx][yy]; /* may be moving! */
14086 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14087 border_element = Feld[xx][yy];
14088 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14089 border_element = MovingOrBlocked2Element(xx, yy);
14091 continue; /* center and border element do not touch */
14093 border_element_old[i] = border_element;
14096 for (i = 0; i < NUM_DIRECTIONS; i++)
14098 int xx = x + xy[i][0];
14099 int yy = y + xy[i][1];
14100 int center_side = trigger_sides[i][0];
14101 int border_element = border_element_old[i];
14103 if (border_element == -1)
14106 /* check for change of border element */
14107 CheckElementChangeBySide(xx, yy, border_element, center_element,
14108 CE_TOUCHING_X, center_side);
14110 /* (center element cannot be player, so we dont have to check this here) */
14113 for (i = 0; i < NUM_DIRECTIONS; i++)
14115 int xx = x + xy[i][0];
14116 int yy = y + xy[i][1];
14117 int border_side = trigger_sides[i][1];
14118 int border_element = border_element_old[i];
14120 if (border_element == -1)
14123 /* check for change of center element (but change it only once) */
14124 if (!change_center_element)
14125 change_center_element =
14126 CheckElementChangeBySide(x, y, center_element, border_element,
14127 CE_TOUCHING_X, border_side);
14129 #if USE_FIX_CE_ACTION_WITH_PLAYER
14130 if (IS_PLAYER(xx, yy))
14132 /* use player element that is initially defined in the level playfield,
14133 not the player element that corresponds to the runtime player number
14134 (example: a level that contains EL_PLAYER_3 as the only player would
14135 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14136 int player_element = PLAYERINFO(xx, yy)->initial_element;
14138 CheckElementChangeBySide(x, y, center_element, player_element,
14139 CE_TOUCHING_X, border_side);
14147 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14149 static int xy[4][2] =
14156 static int trigger_sides[4][2] =
14158 /* center side border side */
14159 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14160 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14161 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14162 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14164 static int touch_dir[4] =
14166 MV_LEFT | MV_RIGHT,
14171 boolean change_center_element = FALSE;
14172 int center_element = Feld[x][y]; /* should always be non-moving! */
14175 for (i = 0; i < NUM_DIRECTIONS; i++)
14177 int xx = x + xy[i][0];
14178 int yy = y + xy[i][1];
14179 int center_side = trigger_sides[i][0];
14180 int border_side = trigger_sides[i][1];
14181 int border_element;
14183 if (!IN_LEV_FIELD(xx, yy))
14186 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14187 border_element = Feld[xx][yy]; /* may be moving! */
14188 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14189 border_element = Feld[xx][yy];
14190 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14191 border_element = MovingOrBlocked2Element(xx, yy);
14193 continue; /* center and border element do not touch */
14195 /* check for change of center element (but change it only once) */
14196 if (!change_center_element)
14197 change_center_element =
14198 CheckElementChangeBySide(x, y, center_element, border_element,
14199 CE_TOUCHING_X, border_side);
14201 /* check for change of border element */
14202 CheckElementChangeBySide(xx, yy, border_element, center_element,
14203 CE_TOUCHING_X, center_side);
14209 void TestIfElementHitsCustomElement(int x, int y, int direction)
14211 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14212 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14213 int hitx = x + dx, hity = y + dy;
14214 int hitting_element = Feld[x][y];
14215 int touched_element;
14217 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14220 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14221 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14223 if (IN_LEV_FIELD(hitx, hity))
14225 int opposite_direction = MV_DIR_OPPOSITE(direction);
14226 int hitting_side = direction;
14227 int touched_side = opposite_direction;
14228 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14229 MovDir[hitx][hity] != direction ||
14230 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14236 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14237 CE_HITTING_X, touched_side);
14239 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14240 CE_HIT_BY_X, hitting_side);
14242 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14243 CE_HIT_BY_SOMETHING, opposite_direction);
14245 #if USE_FIX_CE_ACTION_WITH_PLAYER
14246 if (IS_PLAYER(hitx, hity))
14248 /* use player element that is initially defined in the level playfield,
14249 not the player element that corresponds to the runtime player number
14250 (example: a level that contains EL_PLAYER_3 as the only player would
14251 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14252 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14254 CheckElementChangeBySide(x, y, hitting_element, player_element,
14255 CE_HITTING_X, touched_side);
14261 /* "hitting something" is also true when hitting the playfield border */
14262 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14263 CE_HITTING_SOMETHING, direction);
14267 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14269 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14270 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14271 int hitx = x + dx, hity = y + dy;
14272 int hitting_element = Feld[x][y];
14273 int touched_element;
14275 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14276 !IS_FREE(hitx, hity) &&
14277 (!IS_MOVING(hitx, hity) ||
14278 MovDir[hitx][hity] != direction ||
14279 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14282 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14286 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14290 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14291 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14293 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14294 EP_CAN_SMASH_EVERYTHING, direction);
14296 if (IN_LEV_FIELD(hitx, hity))
14298 int opposite_direction = MV_DIR_OPPOSITE(direction);
14299 int hitting_side = direction;
14300 int touched_side = opposite_direction;
14302 int touched_element = MovingOrBlocked2Element(hitx, hity);
14305 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14306 MovDir[hitx][hity] != direction ||
14307 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14316 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14317 CE_SMASHED_BY_SOMETHING, opposite_direction);
14319 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14320 CE_OTHER_IS_SMASHING, touched_side);
14322 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14323 CE_OTHER_GETS_SMASHED, hitting_side);
14329 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14331 int i, kill_x = -1, kill_y = -1;
14333 int bad_element = -1;
14334 static int test_xy[4][2] =
14341 static int test_dir[4] =
14349 for (i = 0; i < NUM_DIRECTIONS; i++)
14351 int test_x, test_y, test_move_dir, test_element;
14353 test_x = good_x + test_xy[i][0];
14354 test_y = good_y + test_xy[i][1];
14356 if (!IN_LEV_FIELD(test_x, test_y))
14360 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14362 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14364 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14365 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14367 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14368 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14372 bad_element = test_element;
14378 if (kill_x != -1 || kill_y != -1)
14380 if (IS_PLAYER(good_x, good_y))
14382 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14384 if (player->shield_deadly_time_left > 0 &&
14385 !IS_INDESTRUCTIBLE(bad_element))
14386 Bang(kill_x, kill_y);
14387 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14388 KillPlayer(player);
14391 Bang(good_x, good_y);
14395 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14397 int i, kill_x = -1, kill_y = -1;
14398 int bad_element = Feld[bad_x][bad_y];
14399 static int test_xy[4][2] =
14406 static int touch_dir[4] =
14408 MV_LEFT | MV_RIGHT,
14413 static int test_dir[4] =
14421 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14424 for (i = 0; i < NUM_DIRECTIONS; i++)
14426 int test_x, test_y, test_move_dir, test_element;
14428 test_x = bad_x + test_xy[i][0];
14429 test_y = bad_y + test_xy[i][1];
14431 if (!IN_LEV_FIELD(test_x, test_y))
14435 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14437 test_element = Feld[test_x][test_y];
14439 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14440 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14442 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14443 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14445 /* good thing is player or penguin that does not move away */
14446 if (IS_PLAYER(test_x, test_y))
14448 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14450 if (bad_element == EL_ROBOT && player->is_moving)
14451 continue; /* robot does not kill player if he is moving */
14453 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14455 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14456 continue; /* center and border element do not touch */
14464 else if (test_element == EL_PENGUIN)
14474 if (kill_x != -1 || kill_y != -1)
14476 if (IS_PLAYER(kill_x, kill_y))
14478 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14480 if (player->shield_deadly_time_left > 0 &&
14481 !IS_INDESTRUCTIBLE(bad_element))
14482 Bang(bad_x, bad_y);
14483 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14484 KillPlayer(player);
14487 Bang(kill_x, kill_y);
14491 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14493 int bad_element = Feld[bad_x][bad_y];
14494 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14495 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14496 int test_x = bad_x + dx, test_y = bad_y + dy;
14497 int test_move_dir, test_element;
14498 int kill_x = -1, kill_y = -1;
14500 if (!IN_LEV_FIELD(test_x, test_y))
14504 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14506 test_element = Feld[test_x][test_y];
14508 if (test_move_dir != bad_move_dir)
14510 /* good thing can be player or penguin that does not move away */
14511 if (IS_PLAYER(test_x, test_y))
14513 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14515 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14516 player as being hit when he is moving towards the bad thing, because
14517 the "get hit by" condition would be lost after the player stops) */
14518 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14519 return; /* player moves away from bad thing */
14524 else if (test_element == EL_PENGUIN)
14531 if (kill_x != -1 || kill_y != -1)
14533 if (IS_PLAYER(kill_x, kill_y))
14535 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14537 if (player->shield_deadly_time_left > 0 &&
14538 !IS_INDESTRUCTIBLE(bad_element))
14539 Bang(bad_x, bad_y);
14540 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14541 KillPlayer(player);
14544 Bang(kill_x, kill_y);
14548 void TestIfPlayerTouchesBadThing(int x, int y)
14550 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14553 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14555 TestIfGoodThingHitsBadThing(x, y, move_dir);
14558 void TestIfBadThingTouchesPlayer(int x, int y)
14560 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14563 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14565 TestIfBadThingHitsGoodThing(x, y, move_dir);
14568 void TestIfFriendTouchesBadThing(int x, int y)
14570 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14573 void TestIfBadThingTouchesFriend(int x, int y)
14575 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14578 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14580 int i, kill_x = bad_x, kill_y = bad_y;
14581 static int xy[4][2] =
14589 for (i = 0; i < NUM_DIRECTIONS; i++)
14593 x = bad_x + xy[i][0];
14594 y = bad_y + xy[i][1];
14595 if (!IN_LEV_FIELD(x, y))
14598 element = Feld[x][y];
14599 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14600 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14608 if (kill_x != bad_x || kill_y != bad_y)
14609 Bang(bad_x, bad_y);
14612 void KillPlayer(struct PlayerInfo *player)
14614 int jx = player->jx, jy = player->jy;
14616 if (!player->active)
14620 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14621 player->killed, player->active, player->reanimated);
14624 /* the following code was introduced to prevent an infinite loop when calling
14626 -> CheckTriggeredElementChangeExt()
14627 -> ExecuteCustomElementAction()
14629 -> (infinitely repeating the above sequence of function calls)
14630 which occurs when killing the player while having a CE with the setting
14631 "kill player X when explosion of <player X>"; the solution using a new
14632 field "player->killed" was chosen for backwards compatibility, although
14633 clever use of the fields "player->active" etc. would probably also work */
14635 if (player->killed)
14639 player->killed = TRUE;
14641 /* remove accessible field at the player's position */
14642 Feld[jx][jy] = EL_EMPTY;
14644 /* deactivate shield (else Bang()/Explode() would not work right) */
14645 player->shield_normal_time_left = 0;
14646 player->shield_deadly_time_left = 0;
14649 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14650 player->killed, player->active, player->reanimated);
14656 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14657 player->killed, player->active, player->reanimated);
14660 #if USE_PLAYER_REANIMATION
14662 if (player->reanimated) /* killed player may have been reanimated */
14663 player->killed = player->reanimated = FALSE;
14665 BuryPlayer(player);
14667 if (player->killed) /* player may have been reanimated */
14668 BuryPlayer(player);
14671 BuryPlayer(player);
14675 static void KillPlayerUnlessEnemyProtected(int x, int y)
14677 if (!PLAYER_ENEMY_PROTECTED(x, y))
14678 KillPlayer(PLAYERINFO(x, y));
14681 static void KillPlayerUnlessExplosionProtected(int x, int y)
14683 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14684 KillPlayer(PLAYERINFO(x, y));
14687 void BuryPlayer(struct PlayerInfo *player)
14689 int jx = player->jx, jy = player->jy;
14691 if (!player->active)
14694 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14695 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14697 player->GameOver = TRUE;
14698 RemovePlayer(player);
14701 void RemovePlayer(struct PlayerInfo *player)
14703 int jx = player->jx, jy = player->jy;
14704 int i, found = FALSE;
14706 player->present = FALSE;
14707 player->active = FALSE;
14709 if (!ExplodeField[jx][jy])
14710 StorePlayer[jx][jy] = 0;
14712 if (player->is_moving)
14713 TEST_DrawLevelField(player->last_jx, player->last_jy);
14715 for (i = 0; i < MAX_PLAYERS; i++)
14716 if (stored_player[i].active)
14720 AllPlayersGone = TRUE;
14726 #if USE_NEW_SNAP_DELAY
14727 static void setFieldForSnapping(int x, int y, int element, int direction)
14729 struct ElementInfo *ei = &element_info[element];
14730 int direction_bit = MV_DIR_TO_BIT(direction);
14731 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14732 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14733 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14735 Feld[x][y] = EL_ELEMENT_SNAPPING;
14736 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14738 ResetGfxAnimation(x, y);
14740 GfxElement[x][y] = element;
14741 GfxAction[x][y] = action;
14742 GfxDir[x][y] = direction;
14743 GfxFrame[x][y] = -1;
14748 =============================================================================
14749 checkDiagonalPushing()
14750 -----------------------------------------------------------------------------
14751 check if diagonal input device direction results in pushing of object
14752 (by checking if the alternative direction is walkable, diggable, ...)
14753 =============================================================================
14756 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14757 int x, int y, int real_dx, int real_dy)
14759 int jx, jy, dx, dy, xx, yy;
14761 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14764 /* diagonal direction: check alternative direction */
14769 xx = jx + (dx == 0 ? real_dx : 0);
14770 yy = jy + (dy == 0 ? real_dy : 0);
14772 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14776 =============================================================================
14778 -----------------------------------------------------------------------------
14779 x, y: field next to player (non-diagonal) to try to dig to
14780 real_dx, real_dy: direction as read from input device (can be diagonal)
14781 =============================================================================
14784 static int DigField(struct PlayerInfo *player,
14785 int oldx, int oldy, int x, int y,
14786 int real_dx, int real_dy, int mode)
14788 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14789 boolean player_was_pushing = player->is_pushing;
14790 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14791 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14792 int jx = oldx, jy = oldy;
14793 int dx = x - jx, dy = y - jy;
14794 int nextx = x + dx, nexty = y + dy;
14795 int move_direction = (dx == -1 ? MV_LEFT :
14796 dx == +1 ? MV_RIGHT :
14798 dy == +1 ? MV_DOWN : MV_NONE);
14799 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14800 int dig_side = MV_DIR_OPPOSITE(move_direction);
14801 int old_element = Feld[jx][jy];
14802 #if USE_FIXED_DONT_RUN_INTO
14803 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14809 if (is_player) /* function can also be called by EL_PENGUIN */
14811 if (player->MovPos == 0)
14813 player->is_digging = FALSE;
14814 player->is_collecting = FALSE;
14817 if (player->MovPos == 0) /* last pushing move finished */
14818 player->is_pushing = FALSE;
14820 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14822 player->is_switching = FALSE;
14823 player->push_delay = -1;
14825 return MP_NO_ACTION;
14829 #if !USE_FIXED_DONT_RUN_INTO
14830 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14831 return MP_NO_ACTION;
14834 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14835 old_element = Back[jx][jy];
14837 /* in case of element dropped at player position, check background */
14838 else if (Back[jx][jy] != EL_EMPTY &&
14839 game.engine_version >= VERSION_IDENT(2,2,0,0))
14840 old_element = Back[jx][jy];
14842 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14843 return MP_NO_ACTION; /* field has no opening in this direction */
14845 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14846 return MP_NO_ACTION; /* field has no opening in this direction */
14848 #if USE_FIXED_DONT_RUN_INTO
14849 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14853 Feld[jx][jy] = player->artwork_element;
14854 InitMovingField(jx, jy, MV_DOWN);
14855 Store[jx][jy] = EL_ACID;
14856 ContinueMoving(jx, jy);
14857 BuryPlayer(player);
14859 return MP_DONT_RUN_INTO;
14863 #if USE_FIXED_DONT_RUN_INTO
14864 if (player_can_move && DONT_RUN_INTO(element))
14866 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14868 return MP_DONT_RUN_INTO;
14872 #if USE_FIXED_DONT_RUN_INTO
14873 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14874 return MP_NO_ACTION;
14877 #if !USE_FIXED_DONT_RUN_INTO
14878 element = Feld[x][y];
14881 collect_count = element_info[element].collect_count_initial;
14883 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14884 return MP_NO_ACTION;
14886 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14887 player_can_move = player_can_move_or_snap;
14889 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14890 game.engine_version >= VERSION_IDENT(2,2,0,0))
14892 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14893 player->index_bit, dig_side);
14894 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14895 player->index_bit, dig_side);
14897 if (element == EL_DC_LANDMINE)
14900 if (Feld[x][y] != element) /* field changed by snapping */
14903 return MP_NO_ACTION;
14906 #if USE_PLAYER_GRAVITY
14907 if (player->gravity && is_player && !player->is_auto_moving &&
14908 canFallDown(player) && move_direction != MV_DOWN &&
14909 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14910 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14912 if (game.gravity && is_player && !player->is_auto_moving &&
14913 canFallDown(player) && move_direction != MV_DOWN &&
14914 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14915 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14918 if (player_can_move &&
14919 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14921 int sound_element = SND_ELEMENT(element);
14922 int sound_action = ACTION_WALKING;
14924 if (IS_RND_GATE(element))
14926 if (!player->key[RND_GATE_NR(element)])
14927 return MP_NO_ACTION;
14929 else if (IS_RND_GATE_GRAY(element))
14931 if (!player->key[RND_GATE_GRAY_NR(element)])
14932 return MP_NO_ACTION;
14934 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14936 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14937 return MP_NO_ACTION;
14939 else if (element == EL_EXIT_OPEN ||
14940 element == EL_EM_EXIT_OPEN ||
14942 element == EL_EM_EXIT_OPENING ||
14944 element == EL_STEEL_EXIT_OPEN ||
14945 element == EL_EM_STEEL_EXIT_OPEN ||
14947 element == EL_EM_STEEL_EXIT_OPENING ||
14949 element == EL_SP_EXIT_OPEN ||
14950 element == EL_SP_EXIT_OPENING)
14952 sound_action = ACTION_PASSING; /* player is passing exit */
14954 else if (element == EL_EMPTY)
14956 sound_action = ACTION_MOVING; /* nothing to walk on */
14959 /* play sound from background or player, whatever is available */
14960 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14961 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14963 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14965 else if (player_can_move &&
14966 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14968 if (!ACCESS_FROM(element, opposite_direction))
14969 return MP_NO_ACTION; /* field not accessible from this direction */
14971 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14972 return MP_NO_ACTION;
14974 if (IS_EM_GATE(element))
14976 if (!player->key[EM_GATE_NR(element)])
14977 return MP_NO_ACTION;
14979 else if (IS_EM_GATE_GRAY(element))
14981 if (!player->key[EM_GATE_GRAY_NR(element)])
14982 return MP_NO_ACTION;
14984 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14986 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14987 return MP_NO_ACTION;
14989 else if (IS_EMC_GATE(element))
14991 if (!player->key[EMC_GATE_NR(element)])
14992 return MP_NO_ACTION;
14994 else if (IS_EMC_GATE_GRAY(element))
14996 if (!player->key[EMC_GATE_GRAY_NR(element)])
14997 return MP_NO_ACTION;
14999 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15001 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15002 return MP_NO_ACTION;
15004 else if (element == EL_DC_GATE_WHITE ||
15005 element == EL_DC_GATE_WHITE_GRAY ||
15006 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15008 if (player->num_white_keys == 0)
15009 return MP_NO_ACTION;
15011 player->num_white_keys--;
15013 else if (IS_SP_PORT(element))
15015 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15016 element == EL_SP_GRAVITY_PORT_RIGHT ||
15017 element == EL_SP_GRAVITY_PORT_UP ||
15018 element == EL_SP_GRAVITY_PORT_DOWN)
15019 #if USE_PLAYER_GRAVITY
15020 player->gravity = !player->gravity;
15022 game.gravity = !game.gravity;
15024 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15025 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15026 element == EL_SP_GRAVITY_ON_PORT_UP ||
15027 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15028 #if USE_PLAYER_GRAVITY
15029 player->gravity = TRUE;
15031 game.gravity = TRUE;
15033 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15034 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15035 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15036 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15037 #if USE_PLAYER_GRAVITY
15038 player->gravity = FALSE;
15040 game.gravity = FALSE;
15044 /* automatically move to the next field with double speed */
15045 player->programmed_action = move_direction;
15047 if (player->move_delay_reset_counter == 0)
15049 player->move_delay_reset_counter = 2; /* two double speed steps */
15051 DOUBLE_PLAYER_SPEED(player);
15054 PlayLevelSoundAction(x, y, ACTION_PASSING);
15056 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15060 if (mode != DF_SNAP)
15062 GfxElement[x][y] = GFX_ELEMENT(element);
15063 player->is_digging = TRUE;
15066 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15068 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15069 player->index_bit, dig_side);
15071 if (mode == DF_SNAP)
15073 #if USE_NEW_SNAP_DELAY
15074 if (level.block_snap_field)
15075 setFieldForSnapping(x, y, element, move_direction);
15077 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15079 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15082 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15083 player->index_bit, dig_side);
15086 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15090 if (is_player && mode != DF_SNAP)
15092 GfxElement[x][y] = element;
15093 player->is_collecting = TRUE;
15096 if (element == EL_SPEED_PILL)
15098 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15100 else if (element == EL_EXTRA_TIME && level.time > 0)
15102 TimeLeft += level.extra_time;
15105 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15107 DisplayGameControlValues();
15109 DrawGameValue_Time(TimeLeft);
15112 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15114 player->shield_normal_time_left += level.shield_normal_time;
15115 if (element == EL_SHIELD_DEADLY)
15116 player->shield_deadly_time_left += level.shield_deadly_time;
15118 else if (element == EL_DYNAMITE ||
15119 element == EL_EM_DYNAMITE ||
15120 element == EL_SP_DISK_RED)
15122 if (player->inventory_size < MAX_INVENTORY_SIZE)
15123 player->inventory_element[player->inventory_size++] = element;
15125 DrawGameDoorValues();
15127 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15129 player->dynabomb_count++;
15130 player->dynabombs_left++;
15132 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15134 player->dynabomb_size++;
15136 else if (element == EL_DYNABOMB_INCREASE_POWER)
15138 player->dynabomb_xl = TRUE;
15140 else if (IS_KEY(element))
15142 player->key[KEY_NR(element)] = TRUE;
15144 DrawGameDoorValues();
15146 else if (element == EL_DC_KEY_WHITE)
15148 player->num_white_keys++;
15150 /* display white keys? */
15151 /* DrawGameDoorValues(); */
15153 else if (IS_ENVELOPE(element))
15155 player->show_envelope = element;
15157 else if (element == EL_EMC_LENSES)
15159 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15161 RedrawAllInvisibleElementsForLenses();
15163 else if (element == EL_EMC_MAGNIFIER)
15165 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15167 RedrawAllInvisibleElementsForMagnifier();
15169 else if (IS_DROPPABLE(element) ||
15170 IS_THROWABLE(element)) /* can be collected and dropped */
15174 if (collect_count == 0)
15175 player->inventory_infinite_element = element;
15177 for (i = 0; i < collect_count; i++)
15178 if (player->inventory_size < MAX_INVENTORY_SIZE)
15179 player->inventory_element[player->inventory_size++] = element;
15181 DrawGameDoorValues();
15183 else if (collect_count > 0)
15185 local_player->gems_still_needed -= collect_count;
15186 if (local_player->gems_still_needed < 0)
15187 local_player->gems_still_needed = 0;
15190 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15192 DisplayGameControlValues();
15194 DrawGameValue_Emeralds(local_player->gems_still_needed);
15198 RaiseScoreElement(element);
15199 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15202 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15203 player->index_bit, dig_side);
15205 if (mode == DF_SNAP)
15207 #if USE_NEW_SNAP_DELAY
15208 if (level.block_snap_field)
15209 setFieldForSnapping(x, y, element, move_direction);
15211 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15213 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15216 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15217 player->index_bit, dig_side);
15220 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15222 if (mode == DF_SNAP && element != EL_BD_ROCK)
15223 return MP_NO_ACTION;
15225 if (CAN_FALL(element) && dy)
15226 return MP_NO_ACTION;
15228 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15229 !(element == EL_SPRING && level.use_spring_bug))
15230 return MP_NO_ACTION;
15232 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15233 ((move_direction & MV_VERTICAL &&
15234 ((element_info[element].move_pattern & MV_LEFT &&
15235 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15236 (element_info[element].move_pattern & MV_RIGHT &&
15237 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15238 (move_direction & MV_HORIZONTAL &&
15239 ((element_info[element].move_pattern & MV_UP &&
15240 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15241 (element_info[element].move_pattern & MV_DOWN &&
15242 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15243 return MP_NO_ACTION;
15245 /* do not push elements already moving away faster than player */
15246 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15247 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15248 return MP_NO_ACTION;
15250 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15252 if (player->push_delay_value == -1 || !player_was_pushing)
15253 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15255 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15257 if (player->push_delay_value == -1)
15258 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15260 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15262 if (!player->is_pushing)
15263 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15266 player->is_pushing = TRUE;
15267 player->is_active = TRUE;
15269 if (!(IN_LEV_FIELD(nextx, nexty) &&
15270 (IS_FREE(nextx, nexty) ||
15271 (IS_SB_ELEMENT(element) &&
15272 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15273 (IS_CUSTOM_ELEMENT(element) &&
15274 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15275 return MP_NO_ACTION;
15277 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15278 return MP_NO_ACTION;
15280 if (player->push_delay == -1) /* new pushing; restart delay */
15281 player->push_delay = 0;
15283 if (player->push_delay < player->push_delay_value &&
15284 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15285 element != EL_SPRING && element != EL_BALLOON)
15287 /* make sure that there is no move delay before next try to push */
15288 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15289 player->move_delay = 0;
15291 return MP_NO_ACTION;
15294 if (IS_CUSTOM_ELEMENT(element) &&
15295 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15297 if (!DigFieldByCE(nextx, nexty, element))
15298 return MP_NO_ACTION;
15301 if (IS_SB_ELEMENT(element))
15303 if (element == EL_SOKOBAN_FIELD_FULL)
15305 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15306 local_player->sokobanfields_still_needed++;
15309 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15311 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15312 local_player->sokobanfields_still_needed--;
15315 Feld[x][y] = EL_SOKOBAN_OBJECT;
15317 if (Back[x][y] == Back[nextx][nexty])
15318 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15319 else if (Back[x][y] != 0)
15320 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15323 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15326 if (local_player->sokobanfields_still_needed == 0 &&
15327 game.emulation == EMU_SOKOBAN)
15329 PlayerWins(player);
15331 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15335 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15337 InitMovingField(x, y, move_direction);
15338 GfxAction[x][y] = ACTION_PUSHING;
15340 if (mode == DF_SNAP)
15341 ContinueMoving(x, y);
15343 MovPos[x][y] = (dx != 0 ? dx : dy);
15345 Pushed[x][y] = TRUE;
15346 Pushed[nextx][nexty] = TRUE;
15348 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15349 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15351 player->push_delay_value = -1; /* get new value later */
15353 /* check for element change _after_ element has been pushed */
15354 if (game.use_change_when_pushing_bug)
15356 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15357 player->index_bit, dig_side);
15358 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15359 player->index_bit, dig_side);
15362 else if (IS_SWITCHABLE(element))
15364 if (PLAYER_SWITCHING(player, x, y))
15366 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15367 player->index_bit, dig_side);
15372 player->is_switching = TRUE;
15373 player->switch_x = x;
15374 player->switch_y = y;
15376 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15378 if (element == EL_ROBOT_WHEEL)
15380 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15384 game.robot_wheel_active = TRUE;
15386 TEST_DrawLevelField(x, y);
15388 else if (element == EL_SP_TERMINAL)
15392 SCAN_PLAYFIELD(xx, yy)
15394 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15396 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15397 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15400 else if (IS_BELT_SWITCH(element))
15402 ToggleBeltSwitch(x, y);
15404 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15405 element == EL_SWITCHGATE_SWITCH_DOWN ||
15406 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15407 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15409 ToggleSwitchgateSwitch(x, y);
15411 else if (element == EL_LIGHT_SWITCH ||
15412 element == EL_LIGHT_SWITCH_ACTIVE)
15414 ToggleLightSwitch(x, y);
15416 else if (element == EL_TIMEGATE_SWITCH ||
15417 element == EL_DC_TIMEGATE_SWITCH)
15419 ActivateTimegateSwitch(x, y);
15421 else if (element == EL_BALLOON_SWITCH_LEFT ||
15422 element == EL_BALLOON_SWITCH_RIGHT ||
15423 element == EL_BALLOON_SWITCH_UP ||
15424 element == EL_BALLOON_SWITCH_DOWN ||
15425 element == EL_BALLOON_SWITCH_NONE ||
15426 element == EL_BALLOON_SWITCH_ANY)
15428 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15429 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15430 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15431 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15432 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15435 else if (element == EL_LAMP)
15437 Feld[x][y] = EL_LAMP_ACTIVE;
15438 local_player->lights_still_needed--;
15440 ResetGfxAnimation(x, y);
15441 TEST_DrawLevelField(x, y);
15443 else if (element == EL_TIME_ORB_FULL)
15445 Feld[x][y] = EL_TIME_ORB_EMPTY;
15447 if (level.time > 0 || level.use_time_orb_bug)
15449 TimeLeft += level.time_orb_time;
15452 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15454 DisplayGameControlValues();
15456 DrawGameValue_Time(TimeLeft);
15460 ResetGfxAnimation(x, y);
15461 TEST_DrawLevelField(x, y);
15463 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15464 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15468 game.ball_state = !game.ball_state;
15470 SCAN_PLAYFIELD(xx, yy)
15472 int e = Feld[xx][yy];
15474 if (game.ball_state)
15476 if (e == EL_EMC_MAGIC_BALL)
15477 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15478 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15479 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15483 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15484 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15485 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15486 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15491 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15492 player->index_bit, dig_side);
15494 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15495 player->index_bit, dig_side);
15497 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15498 player->index_bit, dig_side);
15504 if (!PLAYER_SWITCHING(player, x, y))
15506 player->is_switching = TRUE;
15507 player->switch_x = x;
15508 player->switch_y = y;
15510 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15511 player->index_bit, dig_side);
15512 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15513 player->index_bit, dig_side);
15515 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15516 player->index_bit, dig_side);
15517 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15518 player->index_bit, dig_side);
15521 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15522 player->index_bit, dig_side);
15523 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15524 player->index_bit, dig_side);
15526 return MP_NO_ACTION;
15529 player->push_delay = -1;
15531 if (is_player) /* function can also be called by EL_PENGUIN */
15533 if (Feld[x][y] != element) /* really digged/collected something */
15535 player->is_collecting = !player->is_digging;
15536 player->is_active = TRUE;
15543 static boolean DigFieldByCE(int x, int y, int digging_element)
15545 int element = Feld[x][y];
15547 if (!IS_FREE(x, y))
15549 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15550 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15553 /* no element can dig solid indestructible elements */
15554 if (IS_INDESTRUCTIBLE(element) &&
15555 !IS_DIGGABLE(element) &&
15556 !IS_COLLECTIBLE(element))
15559 if (AmoebaNr[x][y] &&
15560 (element == EL_AMOEBA_FULL ||
15561 element == EL_BD_AMOEBA ||
15562 element == EL_AMOEBA_GROWING))
15564 AmoebaCnt[AmoebaNr[x][y]]--;
15565 AmoebaCnt2[AmoebaNr[x][y]]--;
15568 if (IS_MOVING(x, y))
15569 RemoveMovingField(x, y);
15573 TEST_DrawLevelField(x, y);
15576 /* if digged element was about to explode, prevent the explosion */
15577 ExplodeField[x][y] = EX_TYPE_NONE;
15579 PlayLevelSoundAction(x, y, action);
15582 Store[x][y] = EL_EMPTY;
15585 /* this makes it possible to leave the removed element again */
15586 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15587 Store[x][y] = element;
15589 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15591 int move_leave_element = element_info[digging_element].move_leave_element;
15593 /* this makes it possible to leave the removed element again */
15594 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15595 element : move_leave_element);
15602 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15604 int jx = player->jx, jy = player->jy;
15605 int x = jx + dx, y = jy + dy;
15606 int snap_direction = (dx == -1 ? MV_LEFT :
15607 dx == +1 ? MV_RIGHT :
15609 dy == +1 ? MV_DOWN : MV_NONE);
15610 boolean can_continue_snapping = (level.continuous_snapping &&
15611 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15613 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15616 if (!player->active || !IN_LEV_FIELD(x, y))
15624 if (player->MovPos == 0)
15625 player->is_pushing = FALSE;
15627 player->is_snapping = FALSE;
15629 if (player->MovPos == 0)
15631 player->is_moving = FALSE;
15632 player->is_digging = FALSE;
15633 player->is_collecting = FALSE;
15639 #if USE_NEW_CONTINUOUS_SNAPPING
15640 /* prevent snapping with already pressed snap key when not allowed */
15641 if (player->is_snapping && !can_continue_snapping)
15644 if (player->is_snapping)
15648 player->MovDir = snap_direction;
15650 if (player->MovPos == 0)
15652 player->is_moving = FALSE;
15653 player->is_digging = FALSE;
15654 player->is_collecting = FALSE;
15657 player->is_dropping = FALSE;
15658 player->is_dropping_pressed = FALSE;
15659 player->drop_pressed_delay = 0;
15661 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15664 player->is_snapping = TRUE;
15665 player->is_active = TRUE;
15667 if (player->MovPos == 0)
15669 player->is_moving = FALSE;
15670 player->is_digging = FALSE;
15671 player->is_collecting = FALSE;
15674 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15675 TEST_DrawLevelField(player->last_jx, player->last_jy);
15677 TEST_DrawLevelField(x, y);
15682 static boolean DropElement(struct PlayerInfo *player)
15684 int old_element, new_element;
15685 int dropx = player->jx, dropy = player->jy;
15686 int drop_direction = player->MovDir;
15687 int drop_side = drop_direction;
15689 int drop_element = get_next_dropped_element(player);
15691 int drop_element = (player->inventory_size > 0 ?
15692 player->inventory_element[player->inventory_size - 1] :
15693 player->inventory_infinite_element != EL_UNDEFINED ?
15694 player->inventory_infinite_element :
15695 player->dynabombs_left > 0 ?
15696 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15700 player->is_dropping_pressed = TRUE;
15702 /* do not drop an element on top of another element; when holding drop key
15703 pressed without moving, dropped element must move away before the next
15704 element can be dropped (this is especially important if the next element
15705 is dynamite, which can be placed on background for historical reasons) */
15706 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15709 if (IS_THROWABLE(drop_element))
15711 dropx += GET_DX_FROM_DIR(drop_direction);
15712 dropy += GET_DY_FROM_DIR(drop_direction);
15714 if (!IN_LEV_FIELD(dropx, dropy))
15718 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15719 new_element = drop_element; /* default: no change when dropping */
15721 /* check if player is active, not moving and ready to drop */
15722 if (!player->active || player->MovPos || player->drop_delay > 0)
15725 /* check if player has anything that can be dropped */
15726 if (new_element == EL_UNDEFINED)
15729 /* check if drop key was pressed long enough for EM style dynamite */
15730 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15733 /* check if anything can be dropped at the current position */
15734 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15737 /* collected custom elements can only be dropped on empty fields */
15738 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15741 if (old_element != EL_EMPTY)
15742 Back[dropx][dropy] = old_element; /* store old element on this field */
15744 ResetGfxAnimation(dropx, dropy);
15745 ResetRandomAnimationValue(dropx, dropy);
15747 if (player->inventory_size > 0 ||
15748 player->inventory_infinite_element != EL_UNDEFINED)
15750 if (player->inventory_size > 0)
15752 player->inventory_size--;
15754 DrawGameDoorValues();
15756 if (new_element == EL_DYNAMITE)
15757 new_element = EL_DYNAMITE_ACTIVE;
15758 else if (new_element == EL_EM_DYNAMITE)
15759 new_element = EL_EM_DYNAMITE_ACTIVE;
15760 else if (new_element == EL_SP_DISK_RED)
15761 new_element = EL_SP_DISK_RED_ACTIVE;
15764 Feld[dropx][dropy] = new_element;
15766 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15767 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15768 el2img(Feld[dropx][dropy]), 0);
15770 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15772 /* needed if previous element just changed to "empty" in the last frame */
15773 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15775 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15776 player->index_bit, drop_side);
15777 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15779 player->index_bit, drop_side);
15781 TestIfElementTouchesCustomElement(dropx, dropy);
15783 else /* player is dropping a dyna bomb */
15785 player->dynabombs_left--;
15787 Feld[dropx][dropy] = new_element;
15789 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15790 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15791 el2img(Feld[dropx][dropy]), 0);
15793 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15796 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15797 InitField_WithBug1(dropx, dropy, FALSE);
15799 new_element = Feld[dropx][dropy]; /* element might have changed */
15801 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15802 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15804 int move_direction, nextx, nexty;
15806 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15807 MovDir[dropx][dropy] = drop_direction;
15809 move_direction = MovDir[dropx][dropy];
15810 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15811 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15813 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15815 #if USE_FIX_IMPACT_COLLISION
15816 /* do not cause impact style collision by dropping elements that can fall */
15817 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15819 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15823 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15824 player->is_dropping = TRUE;
15826 player->drop_pressed_delay = 0;
15827 player->is_dropping_pressed = FALSE;
15829 player->drop_x = dropx;
15830 player->drop_y = dropy;
15835 /* ------------------------------------------------------------------------- */
15836 /* game sound playing functions */
15837 /* ------------------------------------------------------------------------- */
15839 static int *loop_sound_frame = NULL;
15840 static int *loop_sound_volume = NULL;
15842 void InitPlayLevelSound()
15844 int num_sounds = getSoundListSize();
15846 checked_free(loop_sound_frame);
15847 checked_free(loop_sound_volume);
15849 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15850 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15853 static void PlayLevelSound(int x, int y, int nr)
15855 int sx = SCREENX(x), sy = SCREENY(y);
15856 int volume, stereo_position;
15857 int max_distance = 8;
15858 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15860 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15861 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15864 if (!IN_LEV_FIELD(x, y) ||
15865 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15866 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15869 volume = SOUND_MAX_VOLUME;
15871 if (!IN_SCR_FIELD(sx, sy))
15873 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15874 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15876 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15879 stereo_position = (SOUND_MAX_LEFT +
15880 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15881 (SCR_FIELDX + 2 * max_distance));
15883 if (IS_LOOP_SOUND(nr))
15885 /* This assures that quieter loop sounds do not overwrite louder ones,
15886 while restarting sound volume comparison with each new game frame. */
15888 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15891 loop_sound_volume[nr] = volume;
15892 loop_sound_frame[nr] = FrameCounter;
15895 PlaySoundExt(nr, volume, stereo_position, type);
15898 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15900 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15901 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15902 y < LEVELY(BY1) ? LEVELY(BY1) :
15903 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15907 static void PlayLevelSoundAction(int x, int y, int action)
15909 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15912 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15914 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15916 if (sound_effect != SND_UNDEFINED)
15917 PlayLevelSound(x, y, sound_effect);
15920 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15923 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15925 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15926 PlayLevelSound(x, y, sound_effect);
15929 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15931 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15933 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15934 PlayLevelSound(x, y, sound_effect);
15937 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15939 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15941 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15942 StopSound(sound_effect);
15945 static void PlayLevelMusic()
15947 if (levelset.music[level_nr] != MUS_UNDEFINED)
15948 PlayMusic(levelset.music[level_nr]); /* from config file */
15950 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15953 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15955 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15956 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15957 int x = xx - 1 - offset;
15958 int y = yy - 1 - offset;
15963 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15967 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15971 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15975 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15979 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15983 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15987 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15990 case SAMPLE_android_clone:
15991 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15994 case SAMPLE_android_move:
15995 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15998 case SAMPLE_spring:
15999 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16003 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16007 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16010 case SAMPLE_eater_eat:
16011 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16015 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16018 case SAMPLE_collect:
16019 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16022 case SAMPLE_diamond:
16023 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16026 case SAMPLE_squash:
16027 /* !!! CHECK THIS !!! */
16029 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16031 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16035 case SAMPLE_wonderfall:
16036 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16040 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16044 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16048 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16052 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16056 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16060 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16063 case SAMPLE_wonder:
16064 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16068 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16071 case SAMPLE_exit_open:
16072 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16075 case SAMPLE_exit_leave:
16076 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16079 case SAMPLE_dynamite:
16080 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16084 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16088 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16092 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16096 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16100 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16104 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16108 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16114 void ChangeTime(int value)
16116 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16120 /* EMC game engine uses value from time counter of RND game engine */
16121 level.native_em_level->lev->time = *time;
16123 DrawGameValue_Time(*time);
16126 void RaiseScore(int value)
16128 /* EMC game engine and RND game engine have separate score counters */
16129 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16130 &level.native_em_level->lev->score : &local_player->score);
16134 DrawGameValue_Score(*score);
16138 void RaiseScore(int value)
16140 local_player->score += value;
16143 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16145 DisplayGameControlValues();
16147 DrawGameValue_Score(local_player->score);
16151 void RaiseScoreElement(int element)
16156 case EL_BD_DIAMOND:
16157 case EL_EMERALD_YELLOW:
16158 case EL_EMERALD_RED:
16159 case EL_EMERALD_PURPLE:
16160 case EL_SP_INFOTRON:
16161 RaiseScore(level.score[SC_EMERALD]);
16164 RaiseScore(level.score[SC_DIAMOND]);
16167 RaiseScore(level.score[SC_CRYSTAL]);
16170 RaiseScore(level.score[SC_PEARL]);
16173 case EL_BD_BUTTERFLY:
16174 case EL_SP_ELECTRON:
16175 RaiseScore(level.score[SC_BUG]);
16178 case EL_BD_FIREFLY:
16179 case EL_SP_SNIKSNAK:
16180 RaiseScore(level.score[SC_SPACESHIP]);
16183 case EL_DARK_YAMYAM:
16184 RaiseScore(level.score[SC_YAMYAM]);
16187 RaiseScore(level.score[SC_ROBOT]);
16190 RaiseScore(level.score[SC_PACMAN]);
16193 RaiseScore(level.score[SC_NUT]);
16196 case EL_EM_DYNAMITE:
16197 case EL_SP_DISK_RED:
16198 case EL_DYNABOMB_INCREASE_NUMBER:
16199 case EL_DYNABOMB_INCREASE_SIZE:
16200 case EL_DYNABOMB_INCREASE_POWER:
16201 RaiseScore(level.score[SC_DYNAMITE]);
16203 case EL_SHIELD_NORMAL:
16204 case EL_SHIELD_DEADLY:
16205 RaiseScore(level.score[SC_SHIELD]);
16207 case EL_EXTRA_TIME:
16208 RaiseScore(level.extra_time_score);
16222 case EL_DC_KEY_WHITE:
16223 RaiseScore(level.score[SC_KEY]);
16226 RaiseScore(element_info[element].collect_score);
16231 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16233 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16235 #if defined(NETWORK_AVALIABLE)
16236 if (options.network)
16237 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16246 FadeSkipNextFadeIn();
16248 fading = fading_none;
16252 OpenDoor(DOOR_CLOSE_1);
16255 game_status = GAME_MODE_MAIN;
16258 DrawAndFadeInMainMenu(REDRAW_FIELD);
16266 FadeOut(REDRAW_FIELD);
16269 game_status = GAME_MODE_MAIN;
16271 DrawAndFadeInMainMenu(REDRAW_FIELD);
16275 else /* continue playing the game */
16277 if (tape.playing && tape.deactivate_display)
16278 TapeDeactivateDisplayOff(TRUE);
16280 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16282 if (tape.playing && tape.deactivate_display)
16283 TapeDeactivateDisplayOn();
16287 void RequestQuitGame(boolean ask_if_really_quit)
16289 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16290 boolean skip_request = AllPlayersGone || quick_quit;
16292 RequestQuitGameExt(skip_request, quick_quit,
16293 "Do you really want to quit the game ?");
16297 /* ------------------------------------------------------------------------- */
16298 /* random generator functions */
16299 /* ------------------------------------------------------------------------- */
16301 unsigned int InitEngineRandom_RND(long seed)
16303 game.num_random_calls = 0;
16306 unsigned int rnd_seed = InitEngineRandom(seed);
16308 printf("::: START RND: %d\n", rnd_seed);
16313 return InitEngineRandom(seed);
16319 unsigned int RND(int max)
16323 game.num_random_calls++;
16325 return GetEngineRandom(max);
16332 /* ------------------------------------------------------------------------- */
16333 /* game engine snapshot handling functions */
16334 /* ------------------------------------------------------------------------- */
16336 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16338 struct EngineSnapshotInfo
16340 /* runtime values for custom element collect score */
16341 int collect_score[NUM_CUSTOM_ELEMENTS];
16343 /* runtime values for group element choice position */
16344 int choice_pos[NUM_GROUP_ELEMENTS];
16346 /* runtime values for belt position animations */
16347 int belt_graphic[4 * NUM_BELT_PARTS];
16348 int belt_anim_mode[4 * NUM_BELT_PARTS];
16351 struct EngineSnapshotNodeInfo
16358 static struct EngineSnapshotInfo engine_snapshot_rnd;
16359 static ListNode *engine_snapshot_list = NULL;
16360 static char *snapshot_level_identifier = NULL;
16361 static int snapshot_level_nr = -1;
16363 void FreeEngineSnapshot()
16365 while (engine_snapshot_list != NULL)
16366 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16369 setString(&snapshot_level_identifier, NULL);
16370 snapshot_level_nr = -1;
16373 static void SaveEngineSnapshotValues_RND()
16375 static int belt_base_active_element[4] =
16377 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16378 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16379 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16380 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16384 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16386 int element = EL_CUSTOM_START + i;
16388 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16391 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16393 int element = EL_GROUP_START + i;
16395 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16398 for (i = 0; i < 4; i++)
16400 for (j = 0; j < NUM_BELT_PARTS; j++)
16402 int element = belt_base_active_element[i] + j;
16403 int graphic = el2img(element);
16404 int anim_mode = graphic_info[graphic].anim_mode;
16406 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16407 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16412 static void LoadEngineSnapshotValues_RND()
16414 unsigned long num_random_calls = game.num_random_calls;
16417 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16419 int element = EL_CUSTOM_START + i;
16421 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16424 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16426 int element = EL_GROUP_START + i;
16428 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16431 for (i = 0; i < 4; i++)
16433 for (j = 0; j < NUM_BELT_PARTS; j++)
16435 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16436 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16438 graphic_info[graphic].anim_mode = anim_mode;
16442 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16444 InitRND(tape.random_seed);
16445 for (i = 0; i < num_random_calls; i++)
16449 if (game.num_random_calls != num_random_calls)
16451 Error(ERR_INFO, "number of random calls out of sync");
16452 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16453 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16454 Error(ERR_EXIT, "this should not happen -- please debug");
16458 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16460 struct EngineSnapshotNodeInfo *bi =
16461 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16463 bi->buffer_orig = buffer;
16464 bi->buffer_copy = checked_malloc(size);
16467 memcpy(bi->buffer_copy, buffer, size);
16469 addNodeToList(&engine_snapshot_list, NULL, bi);
16472 void SaveEngineSnapshot()
16474 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16476 if (level_editor_test_game) /* do not save snapshots from editor */
16479 /* copy some special values to a structure better suited for the snapshot */
16481 SaveEngineSnapshotValues_RND();
16482 SaveEngineSnapshotValues_EM();
16484 /* save values stored in special snapshot structure */
16486 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16487 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16489 /* save further RND engine values */
16491 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16492 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16493 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16495 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16496 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16497 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16498 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16500 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16501 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16502 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16503 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16504 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16506 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16507 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16508 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16510 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16512 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16514 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16515 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16517 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16518 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16519 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16520 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16521 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16522 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16523 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16524 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16525 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16526 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16527 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16528 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16530 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16531 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16532 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16533 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16534 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16536 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16539 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16543 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16555 /* save level identification information */
16557 setString(&snapshot_level_identifier, leveldir_current->identifier);
16558 snapshot_level_nr = level_nr;
16561 ListNode *node = engine_snapshot_list;
16564 while (node != NULL)
16566 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16571 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16575 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16577 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16580 void LoadEngineSnapshot()
16582 ListNode *node = engine_snapshot_list;
16584 if (engine_snapshot_list == NULL)
16587 while (node != NULL)
16589 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16594 /* restore special values from snapshot structure */
16596 LoadEngineSnapshotValues_RND();
16597 LoadEngineSnapshotValues_EM();
16600 boolean CheckEngineSnapshot()
16602 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16603 snapshot_level_nr == level_nr);
16607 /* ---------- new game button stuff ---------------------------------------- */
16609 /* graphic position values for game buttons */
16610 #define GAME_BUTTON_XSIZE 30
16611 #define GAME_BUTTON_YSIZE 30
16612 #define GAME_BUTTON_XPOS 5
16613 #define GAME_BUTTON_YPOS 215
16614 #define SOUND_BUTTON_XPOS 5
16615 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16617 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16618 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16619 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16620 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16621 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16622 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16630 } gamebutton_info[NUM_GAME_BUTTONS] =
16634 &game.button.stop.x, &game.button.stop.y,
16635 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16640 &game.button.pause.x, &game.button.pause.y,
16641 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16642 GAME_CTRL_ID_PAUSE,
16646 &game.button.play.x, &game.button.play.y,
16647 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16652 &game.button.sound_music.x, &game.button.sound_music.y,
16653 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16654 SOUND_CTRL_ID_MUSIC,
16655 "background music on/off"
16658 &game.button.sound_loops.x, &game.button.sound_loops.y,
16659 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16660 SOUND_CTRL_ID_LOOPS,
16661 "sound loops on/off"
16664 &game.button.sound_simple.x,&game.button.sound_simple.y,
16665 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16666 SOUND_CTRL_ID_SIMPLE,
16667 "normal sounds on/off"
16671 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16676 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16677 GAME_CTRL_ID_PAUSE,
16681 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16686 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16687 SOUND_CTRL_ID_MUSIC,
16688 "background music on/off"
16691 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16692 SOUND_CTRL_ID_LOOPS,
16693 "sound loops on/off"
16696 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16697 SOUND_CTRL_ID_SIMPLE,
16698 "normal sounds on/off"
16703 void CreateGameButtons()
16707 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16709 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16710 struct GadgetInfo *gi;
16713 unsigned long event_mask;
16715 int gd_xoffset, gd_yoffset;
16716 int gd_x1, gd_x2, gd_y1, gd_y2;
16719 x = DX + *gamebutton_info[i].x;
16720 y = DY + *gamebutton_info[i].y;
16721 gd_xoffset = gamebutton_info[i].gd_x;
16722 gd_yoffset = gamebutton_info[i].gd_y;
16723 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16724 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16726 if (id == GAME_CTRL_ID_STOP ||
16727 id == GAME_CTRL_ID_PAUSE ||
16728 id == GAME_CTRL_ID_PLAY)
16730 button_type = GD_TYPE_NORMAL_BUTTON;
16732 event_mask = GD_EVENT_RELEASED;
16733 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16734 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16738 button_type = GD_TYPE_CHECK_BUTTON;
16740 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16741 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16742 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16743 event_mask = GD_EVENT_PRESSED;
16744 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16745 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16748 gi = CreateGadget(GDI_CUSTOM_ID, id,
16749 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16754 GDI_X, DX + gd_xoffset,
16755 GDI_Y, DY + gd_yoffset,
16757 GDI_WIDTH, GAME_BUTTON_XSIZE,
16758 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16759 GDI_TYPE, button_type,
16760 GDI_STATE, GD_BUTTON_UNPRESSED,
16761 GDI_CHECKED, checked,
16762 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16763 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16764 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16765 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16766 GDI_DIRECT_DRAW, FALSE,
16767 GDI_EVENT_MASK, event_mask,
16768 GDI_CALLBACK_ACTION, HandleGameButtons,
16772 Error(ERR_EXIT, "cannot create gadget");
16774 game_gadget[id] = gi;
16778 void FreeGameButtons()
16782 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16783 FreeGadget(game_gadget[i]);
16786 static void MapGameButtons()
16790 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16791 MapGadget(game_gadget[i]);
16794 void UnmapGameButtons()
16798 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16799 UnmapGadget(game_gadget[i]);
16802 void RedrawGameButtons()
16806 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16807 RedrawGadget(game_gadget[i]);
16810 static void HandleGameButtons(struct GadgetInfo *gi)
16812 int id = gi->custom_id;
16814 if (game_status != GAME_MODE_PLAYING)
16819 case GAME_CTRL_ID_STOP:
16823 RequestQuitGame(TRUE);
16826 case GAME_CTRL_ID_PAUSE:
16827 if (options.network)
16829 #if defined(NETWORK_AVALIABLE)
16831 SendToServer_ContinuePlaying();
16833 SendToServer_PausePlaying();
16837 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16840 case GAME_CTRL_ID_PLAY:
16843 #if defined(NETWORK_AVALIABLE)
16844 if (options.network)
16845 SendToServer_ContinuePlaying();
16849 tape.pausing = FALSE;
16850 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16855 case SOUND_CTRL_ID_MUSIC:
16856 if (setup.sound_music)
16858 setup.sound_music = FALSE;
16861 else if (audio.music_available)
16863 setup.sound = setup.sound_music = TRUE;
16865 SetAudioMode(setup.sound);
16871 case SOUND_CTRL_ID_LOOPS:
16872 if (setup.sound_loops)
16873 setup.sound_loops = FALSE;
16874 else if (audio.loops_available)
16876 setup.sound = setup.sound_loops = TRUE;
16877 SetAudioMode(setup.sound);
16881 case SOUND_CTRL_ID_SIMPLE:
16882 if (setup.sound_simple)
16883 setup.sound_simple = FALSE;
16884 else if (audio.sound_available)
16886 setup.sound = setup.sound_simple = TRUE;
16887 SetAudioMode(setup.sound);