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_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
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 /* game panel display and control definitions */
124 #define GAME_PANEL_LEVEL_NUMBER 0
125 #define GAME_PANEL_GEMS 1
126 #define GAME_PANEL_INVENTORY_COUNT 2
127 #define GAME_PANEL_INVENTORY_FIRST_1 3
128 #define GAME_PANEL_INVENTORY_FIRST_2 4
129 #define GAME_PANEL_INVENTORY_FIRST_3 5
130 #define GAME_PANEL_INVENTORY_FIRST_4 6
131 #define GAME_PANEL_INVENTORY_FIRST_5 7
132 #define GAME_PANEL_INVENTORY_FIRST_6 8
133 #define GAME_PANEL_INVENTORY_FIRST_7 9
134 #define GAME_PANEL_INVENTORY_FIRST_8 10
135 #define GAME_PANEL_INVENTORY_LAST_1 11
136 #define GAME_PANEL_INVENTORY_LAST_2 12
137 #define GAME_PANEL_INVENTORY_LAST_3 13
138 #define GAME_PANEL_INVENTORY_LAST_4 14
139 #define GAME_PANEL_INVENTORY_LAST_5 15
140 #define GAME_PANEL_INVENTORY_LAST_6 16
141 #define GAME_PANEL_INVENTORY_LAST_7 17
142 #define GAME_PANEL_INVENTORY_LAST_8 18
143 #define GAME_PANEL_KEY_1 19
144 #define GAME_PANEL_KEY_2 20
145 #define GAME_PANEL_KEY_3 21
146 #define GAME_PANEL_KEY_4 22
147 #define GAME_PANEL_KEY_5 23
148 #define GAME_PANEL_KEY_6 24
149 #define GAME_PANEL_KEY_7 25
150 #define GAME_PANEL_KEY_8 26
151 #define GAME_PANEL_KEY_WHITE 27
152 #define GAME_PANEL_KEY_WHITE_COUNT 28
153 #define GAME_PANEL_SCORE 29
154 #define GAME_PANEL_HIGHSCORE 30
155 #define GAME_PANEL_TIME 31
156 #define GAME_PANEL_TIME_HH 32
157 #define GAME_PANEL_TIME_MM 33
158 #define GAME_PANEL_TIME_SS 34
159 #define GAME_PANEL_FRAME 35
160 #define GAME_PANEL_SHIELD_NORMAL 36
161 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
162 #define GAME_PANEL_SHIELD_DEADLY 38
163 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
164 #define GAME_PANEL_EXIT 40
165 #define GAME_PANEL_EMC_MAGIC_BALL 41
166 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
167 #define GAME_PANEL_LIGHT_SWITCH 43
168 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
169 #define GAME_PANEL_TIMEGATE_SWITCH 45
170 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
171 #define GAME_PANEL_SWITCHGATE_SWITCH 47
172 #define GAME_PANEL_EMC_LENSES 48
173 #define GAME_PANEL_EMC_LENSES_TIME 49
174 #define GAME_PANEL_EMC_MAGNIFIER 50
175 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
176 #define GAME_PANEL_BALLOON_SWITCH 52
177 #define GAME_PANEL_DYNABOMB_NUMBER 53
178 #define GAME_PANEL_DYNABOMB_SIZE 54
179 #define GAME_PANEL_DYNABOMB_POWER 55
180 #define GAME_PANEL_PENGUINS 56
181 #define GAME_PANEL_SOKOBAN_OBJECTS 57
182 #define GAME_PANEL_SOKOBAN_FIELDS 58
183 #define GAME_PANEL_ROBOT_WHEEL 59
184 #define GAME_PANEL_CONVEYOR_BELT_1 60
185 #define GAME_PANEL_CONVEYOR_BELT_2 61
186 #define GAME_PANEL_CONVEYOR_BELT_3 62
187 #define GAME_PANEL_CONVEYOR_BELT_4 63
188 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
189 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
190 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
191 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
192 #define GAME_PANEL_MAGIC_WALL 68
193 #define GAME_PANEL_MAGIC_WALL_TIME 69
194 #define GAME_PANEL_GRAVITY_STATE 70
195 #define GAME_PANEL_GRAPHIC_1 71
196 #define GAME_PANEL_GRAPHIC_2 72
197 #define GAME_PANEL_GRAPHIC_3 73
198 #define GAME_PANEL_GRAPHIC_4 74
199 #define GAME_PANEL_GRAPHIC_5 75
200 #define GAME_PANEL_GRAPHIC_6 76
201 #define GAME_PANEL_GRAPHIC_7 77
202 #define GAME_PANEL_GRAPHIC_8 78
203 #define GAME_PANEL_ELEMENT_1 79
204 #define GAME_PANEL_ELEMENT_2 80
205 #define GAME_PANEL_ELEMENT_3 81
206 #define GAME_PANEL_ELEMENT_4 82
207 #define GAME_PANEL_ELEMENT_5 83
208 #define GAME_PANEL_ELEMENT_6 84
209 #define GAME_PANEL_ELEMENT_7 85
210 #define GAME_PANEL_ELEMENT_8 86
211 #define GAME_PANEL_ELEMENT_COUNT_1 87
212 #define GAME_PANEL_ELEMENT_COUNT_2 88
213 #define GAME_PANEL_ELEMENT_COUNT_3 89
214 #define GAME_PANEL_ELEMENT_COUNT_4 90
215 #define GAME_PANEL_ELEMENT_COUNT_5 91
216 #define GAME_PANEL_ELEMENT_COUNT_6 92
217 #define GAME_PANEL_ELEMENT_COUNT_7 93
218 #define GAME_PANEL_ELEMENT_COUNT_8 94
219 #define GAME_PANEL_CE_SCORE_1 95
220 #define GAME_PANEL_CE_SCORE_2 96
221 #define GAME_PANEL_CE_SCORE_3 97
222 #define GAME_PANEL_CE_SCORE_4 98
223 #define GAME_PANEL_CE_SCORE_5 99
224 #define GAME_PANEL_CE_SCORE_6 100
225 #define GAME_PANEL_CE_SCORE_7 101
226 #define GAME_PANEL_CE_SCORE_8 102
227 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
228 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
229 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
230 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
231 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
232 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
233 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
234 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
235 #define GAME_PANEL_PLAYER_NAME 111
236 #define GAME_PANEL_LEVEL_NAME 112
237 #define GAME_PANEL_LEVEL_AUTHOR 113
239 #define NUM_GAME_PANEL_CONTROLS 114
241 struct GamePanelOrderInfo
247 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
249 struct GamePanelControlInfo
253 struct TextPosInfo *pos;
256 int value, last_value;
257 int frame, last_frame;
262 static struct GamePanelControlInfo game_panel_controls[] =
265 GAME_PANEL_LEVEL_NUMBER,
266 &game.panel.level_number,
275 GAME_PANEL_INVENTORY_COUNT,
276 &game.panel.inventory_count,
280 GAME_PANEL_INVENTORY_FIRST_1,
281 &game.panel.inventory_first[0],
285 GAME_PANEL_INVENTORY_FIRST_2,
286 &game.panel.inventory_first[1],
290 GAME_PANEL_INVENTORY_FIRST_3,
291 &game.panel.inventory_first[2],
295 GAME_PANEL_INVENTORY_FIRST_4,
296 &game.panel.inventory_first[3],
300 GAME_PANEL_INVENTORY_FIRST_5,
301 &game.panel.inventory_first[4],
305 GAME_PANEL_INVENTORY_FIRST_6,
306 &game.panel.inventory_first[5],
310 GAME_PANEL_INVENTORY_FIRST_7,
311 &game.panel.inventory_first[6],
315 GAME_PANEL_INVENTORY_FIRST_8,
316 &game.panel.inventory_first[7],
320 GAME_PANEL_INVENTORY_LAST_1,
321 &game.panel.inventory_last[0],
325 GAME_PANEL_INVENTORY_LAST_2,
326 &game.panel.inventory_last[1],
330 GAME_PANEL_INVENTORY_LAST_3,
331 &game.panel.inventory_last[2],
335 GAME_PANEL_INVENTORY_LAST_4,
336 &game.panel.inventory_last[3],
340 GAME_PANEL_INVENTORY_LAST_5,
341 &game.panel.inventory_last[4],
345 GAME_PANEL_INVENTORY_LAST_6,
346 &game.panel.inventory_last[5],
350 GAME_PANEL_INVENTORY_LAST_7,
351 &game.panel.inventory_last[6],
355 GAME_PANEL_INVENTORY_LAST_8,
356 &game.panel.inventory_last[7],
400 GAME_PANEL_KEY_WHITE,
401 &game.panel.key_white,
405 GAME_PANEL_KEY_WHITE_COUNT,
406 &game.panel.key_white_count,
415 GAME_PANEL_HIGHSCORE,
416 &game.panel.highscore,
445 GAME_PANEL_SHIELD_NORMAL,
446 &game.panel.shield_normal,
450 GAME_PANEL_SHIELD_NORMAL_TIME,
451 &game.panel.shield_normal_time,
455 GAME_PANEL_SHIELD_DEADLY,
456 &game.panel.shield_deadly,
460 GAME_PANEL_SHIELD_DEADLY_TIME,
461 &game.panel.shield_deadly_time,
470 GAME_PANEL_EMC_MAGIC_BALL,
471 &game.panel.emc_magic_ball,
475 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
476 &game.panel.emc_magic_ball_switch,
480 GAME_PANEL_LIGHT_SWITCH,
481 &game.panel.light_switch,
485 GAME_PANEL_LIGHT_SWITCH_TIME,
486 &game.panel.light_switch_time,
490 GAME_PANEL_TIMEGATE_SWITCH,
491 &game.panel.timegate_switch,
495 GAME_PANEL_TIMEGATE_SWITCH_TIME,
496 &game.panel.timegate_switch_time,
500 GAME_PANEL_SWITCHGATE_SWITCH,
501 &game.panel.switchgate_switch,
505 GAME_PANEL_EMC_LENSES,
506 &game.panel.emc_lenses,
510 GAME_PANEL_EMC_LENSES_TIME,
511 &game.panel.emc_lenses_time,
515 GAME_PANEL_EMC_MAGNIFIER,
516 &game.panel.emc_magnifier,
520 GAME_PANEL_EMC_MAGNIFIER_TIME,
521 &game.panel.emc_magnifier_time,
525 GAME_PANEL_BALLOON_SWITCH,
526 &game.panel.balloon_switch,
530 GAME_PANEL_DYNABOMB_NUMBER,
531 &game.panel.dynabomb_number,
535 GAME_PANEL_DYNABOMB_SIZE,
536 &game.panel.dynabomb_size,
540 GAME_PANEL_DYNABOMB_POWER,
541 &game.panel.dynabomb_power,
546 &game.panel.penguins,
550 GAME_PANEL_SOKOBAN_OBJECTS,
551 &game.panel.sokoban_objects,
555 GAME_PANEL_SOKOBAN_FIELDS,
556 &game.panel.sokoban_fields,
560 GAME_PANEL_ROBOT_WHEEL,
561 &game.panel.robot_wheel,
565 GAME_PANEL_CONVEYOR_BELT_1,
566 &game.panel.conveyor_belt[0],
570 GAME_PANEL_CONVEYOR_BELT_2,
571 &game.panel.conveyor_belt[1],
575 GAME_PANEL_CONVEYOR_BELT_3,
576 &game.panel.conveyor_belt[2],
580 GAME_PANEL_CONVEYOR_BELT_4,
581 &game.panel.conveyor_belt[3],
585 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
586 &game.panel.conveyor_belt_switch[0],
590 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
591 &game.panel.conveyor_belt_switch[1],
595 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
596 &game.panel.conveyor_belt_switch[2],
600 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
601 &game.panel.conveyor_belt_switch[3],
605 GAME_PANEL_MAGIC_WALL,
606 &game.panel.magic_wall,
610 GAME_PANEL_MAGIC_WALL_TIME,
611 &game.panel.magic_wall_time,
615 GAME_PANEL_GRAVITY_STATE,
616 &game.panel.gravity_state,
620 GAME_PANEL_GRAPHIC_1,
621 &game.panel.graphic[0],
625 GAME_PANEL_GRAPHIC_2,
626 &game.panel.graphic[1],
630 GAME_PANEL_GRAPHIC_3,
631 &game.panel.graphic[2],
635 GAME_PANEL_GRAPHIC_4,
636 &game.panel.graphic[3],
640 GAME_PANEL_GRAPHIC_5,
641 &game.panel.graphic[4],
645 GAME_PANEL_GRAPHIC_6,
646 &game.panel.graphic[5],
650 GAME_PANEL_GRAPHIC_7,
651 &game.panel.graphic[6],
655 GAME_PANEL_GRAPHIC_8,
656 &game.panel.graphic[7],
660 GAME_PANEL_ELEMENT_1,
661 &game.panel.element[0],
665 GAME_PANEL_ELEMENT_2,
666 &game.panel.element[1],
670 GAME_PANEL_ELEMENT_3,
671 &game.panel.element[2],
675 GAME_PANEL_ELEMENT_4,
676 &game.panel.element[3],
680 GAME_PANEL_ELEMENT_5,
681 &game.panel.element[4],
685 GAME_PANEL_ELEMENT_6,
686 &game.panel.element[5],
690 GAME_PANEL_ELEMENT_7,
691 &game.panel.element[6],
695 GAME_PANEL_ELEMENT_8,
696 &game.panel.element[7],
700 GAME_PANEL_ELEMENT_COUNT_1,
701 &game.panel.element_count[0],
705 GAME_PANEL_ELEMENT_COUNT_2,
706 &game.panel.element_count[1],
710 GAME_PANEL_ELEMENT_COUNT_3,
711 &game.panel.element_count[2],
715 GAME_PANEL_ELEMENT_COUNT_4,
716 &game.panel.element_count[3],
720 GAME_PANEL_ELEMENT_COUNT_5,
721 &game.panel.element_count[4],
725 GAME_PANEL_ELEMENT_COUNT_6,
726 &game.panel.element_count[5],
730 GAME_PANEL_ELEMENT_COUNT_7,
731 &game.panel.element_count[6],
735 GAME_PANEL_ELEMENT_COUNT_8,
736 &game.panel.element_count[7],
740 GAME_PANEL_CE_SCORE_1,
741 &game.panel.ce_score[0],
745 GAME_PANEL_CE_SCORE_2,
746 &game.panel.ce_score[1],
750 GAME_PANEL_CE_SCORE_3,
751 &game.panel.ce_score[2],
755 GAME_PANEL_CE_SCORE_4,
756 &game.panel.ce_score[3],
760 GAME_PANEL_CE_SCORE_5,
761 &game.panel.ce_score[4],
765 GAME_PANEL_CE_SCORE_6,
766 &game.panel.ce_score[5],
770 GAME_PANEL_CE_SCORE_7,
771 &game.panel.ce_score[6],
775 GAME_PANEL_CE_SCORE_8,
776 &game.panel.ce_score[7],
780 GAME_PANEL_CE_SCORE_1_ELEMENT,
781 &game.panel.ce_score_element[0],
785 GAME_PANEL_CE_SCORE_2_ELEMENT,
786 &game.panel.ce_score_element[1],
790 GAME_PANEL_CE_SCORE_3_ELEMENT,
791 &game.panel.ce_score_element[2],
795 GAME_PANEL_CE_SCORE_4_ELEMENT,
796 &game.panel.ce_score_element[3],
800 GAME_PANEL_CE_SCORE_5_ELEMENT,
801 &game.panel.ce_score_element[4],
805 GAME_PANEL_CE_SCORE_6_ELEMENT,
806 &game.panel.ce_score_element[5],
810 GAME_PANEL_CE_SCORE_7_ELEMENT,
811 &game.panel.ce_score_element[6],
815 GAME_PANEL_CE_SCORE_8_ELEMENT,
816 &game.panel.ce_score_element[7],
820 GAME_PANEL_PLAYER_NAME,
821 &game.panel.player_name,
825 GAME_PANEL_LEVEL_NAME,
826 &game.panel.level_name,
830 GAME_PANEL_LEVEL_AUTHOR,
831 &game.panel.level_author,
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING 3
844 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION 2
846 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF -1
850 #define INITIAL_MOVE_DELAY_ON 0
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED 32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED 4
856 #define MOVE_DELAY_MAX_SPEED 1
858 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
861 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define SOUND_CTRL_ID_MUSIC 3
1010 #define SOUND_CTRL_ID_LOOPS 4
1011 #define SOUND_CTRL_ID_SIMPLE 5
1013 #define NUM_GAME_BUTTONS 6
1016 /* forward declaration for internal use */
1018 static void CreateField(int, int, int);
1020 static void ResetGfxAnimation(int, int);
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1030 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031 static boolean DigFieldByCE(int, int, int);
1032 static boolean SnapField(struct PlayerInfo *, int, int);
1033 static boolean DropElement(struct PlayerInfo *);
1035 static void InitBeltMovement(void);
1036 static void CloseAllOpenTimegates(void);
1037 static void CheckGravityMovement(struct PlayerInfo *);
1038 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1039 static void KillPlayerUnlessEnemyProtected(int, int);
1040 static void KillPlayerUnlessExplosionProtected(int, int);
1042 static void TestIfPlayerTouchesCustomElement(int, int);
1043 static void TestIfElementTouchesCustomElement(int, int);
1044 static void TestIfElementHitsCustomElement(int, int, int);
1046 static void TestIfElementSmashesCustomElement(int, int, int);
1049 static void HandleElementChange(int, int, int);
1050 static void ExecuteCustomElementAction(int, int, int, int);
1051 static boolean ChangeElement(int, int, int, int);
1053 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1054 #define CheckTriggeredElementChange(x, y, e, ev) \
1055 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1056 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1057 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1058 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1059 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1060 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1061 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1063 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1064 #define CheckElementChange(x, y, e, te, ev) \
1065 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1066 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1068 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1069 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1071 static void PlayLevelSound(int, int, int);
1072 static void PlayLevelSoundNearest(int, int, int);
1073 static void PlayLevelSoundAction(int, int, int);
1074 static void PlayLevelSoundElementAction(int, int, int, int);
1075 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1076 static void PlayLevelSoundActionIfLoop(int, int, int);
1077 static void StopLevelSoundActionIfLoop(int, int, int);
1078 static void PlayLevelMusic();
1080 static void MapGameButtons();
1081 static void HandleGameButtons(struct GadgetInfo *);
1083 int AmoebeNachbarNr(int, int);
1084 void AmoebeUmwandeln(int, int);
1085 void ContinueMoving(int, int);
1086 void Bang(int, int);
1087 void InitMovDir(int, int);
1088 void InitAmoebaNr(int, int);
1089 int NewHiScore(void);
1091 void TestIfGoodThingHitsBadThing(int, int, int);
1092 void TestIfBadThingHitsGoodThing(int, int, int);
1093 void TestIfPlayerTouchesBadThing(int, int);
1094 void TestIfPlayerRunsIntoBadThing(int, int, int);
1095 void TestIfBadThingTouchesPlayer(int, int);
1096 void TestIfBadThingRunsIntoPlayer(int, int, int);
1097 void TestIfFriendTouchesBadThing(int, int);
1098 void TestIfBadThingTouchesFriend(int, int);
1099 void TestIfBadThingTouchesOtherBadThing(int, int);
1100 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1102 void KillPlayer(struct PlayerInfo *);
1103 void BuryPlayer(struct PlayerInfo *);
1104 void RemovePlayer(struct PlayerInfo *);
1106 static int getInvisibleActiveFromInvisibleElement(int);
1107 static int getInvisibleFromInvisibleActiveElement(int);
1109 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1111 /* for detection of endless loops, caused by custom element programming */
1112 /* (using maximal playfield width x 10 is just a rough approximation) */
1113 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1115 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1117 if (recursion_loop_detected) \
1120 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1122 recursion_loop_detected = TRUE; \
1123 recursion_loop_element = (e); \
1126 recursion_loop_depth++; \
1129 #define RECURSION_LOOP_DETECTION_END() \
1131 recursion_loop_depth--; \
1134 static int recursion_loop_depth;
1135 static boolean recursion_loop_detected;
1136 static boolean recursion_loop_element;
1138 static int map_player_action[MAX_PLAYERS];
1141 /* ------------------------------------------------------------------------- */
1142 /* definition of elements that automatically change to other elements after */
1143 /* a specified time, eventually calling a function when changing */
1144 /* ------------------------------------------------------------------------- */
1146 /* forward declaration for changer functions */
1147 static void InitBuggyBase(int, int);
1148 static void WarnBuggyBase(int, int);
1150 static void InitTrap(int, int);
1151 static void ActivateTrap(int, int);
1152 static void ChangeActiveTrap(int, int);
1154 static void InitRobotWheel(int, int);
1155 static void RunRobotWheel(int, int);
1156 static void StopRobotWheel(int, int);
1158 static void InitTimegateWheel(int, int);
1159 static void RunTimegateWheel(int, int);
1161 static void InitMagicBallDelay(int, int);
1162 static void ActivateMagicBall(int, int);
1164 struct ChangingElementInfo
1169 void (*pre_change_function)(int x, int y);
1170 void (*change_function)(int x, int y);
1171 void (*post_change_function)(int x, int y);
1174 static struct ChangingElementInfo change_delay_list[] =
1209 EL_STEEL_EXIT_OPENING,
1217 EL_STEEL_EXIT_CLOSING,
1218 EL_STEEL_EXIT_CLOSED,
1245 EL_EM_STEEL_EXIT_OPENING,
1246 EL_EM_STEEL_EXIT_OPEN,
1253 EL_EM_STEEL_EXIT_CLOSING,
1257 EL_EM_STEEL_EXIT_CLOSED,
1281 EL_SWITCHGATE_OPENING,
1289 EL_SWITCHGATE_CLOSING,
1290 EL_SWITCHGATE_CLOSED,
1297 EL_TIMEGATE_OPENING,
1305 EL_TIMEGATE_CLOSING,
1314 EL_ACID_SPLASH_LEFT,
1322 EL_ACID_SPLASH_RIGHT,
1331 EL_SP_BUGGY_BASE_ACTIVATING,
1338 EL_SP_BUGGY_BASE_ACTIVATING,
1339 EL_SP_BUGGY_BASE_ACTIVE,
1346 EL_SP_BUGGY_BASE_ACTIVE,
1370 EL_ROBOT_WHEEL_ACTIVE,
1378 EL_TIMEGATE_SWITCH_ACTIVE,
1386 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1387 EL_DC_TIMEGATE_SWITCH,
1394 EL_EMC_MAGIC_BALL_ACTIVE,
1395 EL_EMC_MAGIC_BALL_ACTIVE,
1402 EL_EMC_SPRING_BUMPER_ACTIVE,
1403 EL_EMC_SPRING_BUMPER,
1410 EL_DIAGONAL_SHRINKING,
1418 EL_DIAGONAL_GROWING,
1439 int push_delay_fixed, push_delay_random;
1443 { EL_SPRING, 0, 0 },
1444 { EL_BALLOON, 0, 0 },
1446 { EL_SOKOBAN_OBJECT, 2, 0 },
1447 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1448 { EL_SATELLITE, 2, 0 },
1449 { EL_SP_DISK_YELLOW, 2, 0 },
1451 { EL_UNDEFINED, 0, 0 },
1459 move_stepsize_list[] =
1461 { EL_AMOEBA_DROP, 2 },
1462 { EL_AMOEBA_DROPPING, 2 },
1463 { EL_QUICKSAND_FILLING, 1 },
1464 { EL_QUICKSAND_EMPTYING, 1 },
1465 { EL_QUICKSAND_FAST_FILLING, 2 },
1466 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1467 { EL_MAGIC_WALL_FILLING, 2 },
1468 { EL_MAGIC_WALL_EMPTYING, 2 },
1469 { EL_BD_MAGIC_WALL_FILLING, 2 },
1470 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_DC_MAGIC_WALL_FILLING, 2 },
1472 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1474 { EL_UNDEFINED, 0 },
1482 collect_count_list[] =
1485 { EL_BD_DIAMOND, 1 },
1486 { EL_EMERALD_YELLOW, 1 },
1487 { EL_EMERALD_RED, 1 },
1488 { EL_EMERALD_PURPLE, 1 },
1490 { EL_SP_INFOTRON, 1 },
1494 { EL_UNDEFINED, 0 },
1502 access_direction_list[] =
1504 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1505 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1506 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1507 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1508 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1509 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1510 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1511 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1512 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1513 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1514 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1516 { EL_SP_PORT_LEFT, MV_RIGHT },
1517 { EL_SP_PORT_RIGHT, MV_LEFT },
1518 { EL_SP_PORT_UP, MV_DOWN },
1519 { EL_SP_PORT_DOWN, MV_UP },
1520 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1521 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1522 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1524 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1525 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1526 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1527 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1528 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1529 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1530 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1531 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1532 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1533 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1534 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1536 { EL_UNDEFINED, MV_NONE }
1539 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1541 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1542 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1543 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1544 IS_JUST_CHANGING(x, y))
1546 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1548 /* static variables for playfield scan mode (scanning forward or backward) */
1549 static int playfield_scan_start_x = 0;
1550 static int playfield_scan_start_y = 0;
1551 static int playfield_scan_delta_x = 1;
1552 static int playfield_scan_delta_y = 1;
1554 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1555 (y) >= 0 && (y) <= lev_fieldy - 1; \
1556 (y) += playfield_scan_delta_y) \
1557 for ((x) = playfield_scan_start_x; \
1558 (x) >= 0 && (x) <= lev_fieldx - 1; \
1559 (x) += playfield_scan_delta_x)
1562 void DEBUG_SetMaximumDynamite()
1566 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1567 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1568 local_player->inventory_element[local_player->inventory_size++] =
1573 static void InitPlayfieldScanModeVars()
1575 if (game.use_reverse_scan_direction)
1577 playfield_scan_start_x = lev_fieldx - 1;
1578 playfield_scan_start_y = lev_fieldy - 1;
1580 playfield_scan_delta_x = -1;
1581 playfield_scan_delta_y = -1;
1585 playfield_scan_start_x = 0;
1586 playfield_scan_start_y = 0;
1588 playfield_scan_delta_x = 1;
1589 playfield_scan_delta_y = 1;
1593 static void InitPlayfieldScanMode(int mode)
1595 game.use_reverse_scan_direction =
1596 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1598 InitPlayfieldScanModeVars();
1601 static int get_move_delay_from_stepsize(int move_stepsize)
1604 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1606 /* make sure that stepsize value is always a power of 2 */
1607 move_stepsize = (1 << log_2(move_stepsize));
1609 return TILEX / move_stepsize;
1612 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615 int player_nr = player->index_nr;
1616 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1617 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1619 /* do no immediately change move delay -- the player might just be moving */
1620 player->move_delay_value_next = move_delay;
1622 /* information if player can move must be set separately */
1623 player->cannot_move = cannot_move;
1627 player->move_delay = game.initial_move_delay[player_nr];
1628 player->move_delay_value = game.initial_move_delay_value[player_nr];
1630 player->move_delay_value_next = -1;
1632 player->move_delay_reset_counter = 0;
1636 void GetPlayerConfig()
1638 GameFrameDelay = setup.game_frame_delay;
1640 if (!audio.sound_available)
1641 setup.sound_simple = FALSE;
1643 if (!audio.loops_available)
1644 setup.sound_loops = FALSE;
1646 if (!audio.music_available)
1647 setup.sound_music = FALSE;
1649 if (!video.fullscreen_available)
1650 setup.fullscreen = FALSE;
1652 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1654 SetAudioMode(setup.sound);
1658 int GetElementFromGroupElement(int element)
1660 if (IS_GROUP_ELEMENT(element))
1662 struct ElementGroupInfo *group = element_info[element].group;
1663 int last_anim_random_frame = gfx.anim_random_frame;
1666 if (group->choice_mode == ANIM_RANDOM)
1667 gfx.anim_random_frame = RND(group->num_elements_resolved);
1669 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1670 group->choice_mode, 0,
1673 if (group->choice_mode == ANIM_RANDOM)
1674 gfx.anim_random_frame = last_anim_random_frame;
1676 group->choice_pos++;
1678 element = group->element_resolved[element_pos];
1684 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 if (element == EL_SP_MURPHY)
1690 if (stored_player[0].present)
1692 Feld[x][y] = EL_SP_MURPHY_CLONE;
1698 stored_player[0].initial_element = element;
1699 stored_player[0].use_murphy = TRUE;
1701 if (!level.use_artwork_element[0])
1702 stored_player[0].artwork_element = EL_SP_MURPHY;
1705 Feld[x][y] = EL_PLAYER_1;
1711 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1712 int jx = player->jx, jy = player->jy;
1714 player->present = TRUE;
1716 player->block_last_field = (element == EL_SP_MURPHY ?
1717 level.sp_block_last_field :
1718 level.block_last_field);
1720 /* ---------- initialize player's last field block delay --------------- */
1722 /* always start with reliable default value (no adjustment needed) */
1723 player->block_delay_adjustment = 0;
1725 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1726 if (player->block_last_field && element == EL_SP_MURPHY)
1727 player->block_delay_adjustment = 1;
1729 /* special case 2: in game engines before 3.1.1, blocking was different */
1730 if (game.use_block_last_field_bug)
1731 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733 if (!options.network || player->connected)
1735 player->active = TRUE;
1737 /* remove potentially duplicate players */
1738 if (StorePlayer[jx][jy] == Feld[x][y])
1739 StorePlayer[jx][jy] = 0;
1741 StorePlayer[x][y] = Feld[x][y];
1745 printf("Player %d activated.\n", player->element_nr);
1746 printf("[Local player is %d and currently %s.]\n",
1747 local_player->element_nr,
1748 local_player->active ? "active" : "not active");
1752 Feld[x][y] = EL_EMPTY;
1754 player->jx = player->last_jx = x;
1755 player->jy = player->last_jy = y;
1758 #if USE_PLAYER_REANIMATION
1761 int player_nr = GET_PLAYER_NR(element);
1762 struct PlayerInfo *player = &stored_player[player_nr];
1764 if (player->active && player->killed)
1765 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1770 static void InitField(int x, int y, boolean init_game)
1772 int element = Feld[x][y];
1781 InitPlayerField(x, y, element, init_game);
1784 case EL_SOKOBAN_FIELD_PLAYER:
1785 element = Feld[x][y] = EL_PLAYER_1;
1786 InitField(x, y, init_game);
1788 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1789 InitField(x, y, init_game);
1792 case EL_SOKOBAN_FIELD_EMPTY:
1793 local_player->sokobanfields_still_needed++;
1797 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1798 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1799 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1800 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1801 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1802 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1803 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1804 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1805 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1806 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1815 case EL_SPACESHIP_RIGHT:
1816 case EL_SPACESHIP_UP:
1817 case EL_SPACESHIP_LEFT:
1818 case EL_SPACESHIP_DOWN:
1819 case EL_BD_BUTTERFLY:
1820 case EL_BD_BUTTERFLY_RIGHT:
1821 case EL_BD_BUTTERFLY_UP:
1822 case EL_BD_BUTTERFLY_LEFT:
1823 case EL_BD_BUTTERFLY_DOWN:
1825 case EL_BD_FIREFLY_RIGHT:
1826 case EL_BD_FIREFLY_UP:
1827 case EL_BD_FIREFLY_LEFT:
1828 case EL_BD_FIREFLY_DOWN:
1829 case EL_PACMAN_RIGHT:
1831 case EL_PACMAN_LEFT:
1832 case EL_PACMAN_DOWN:
1834 case EL_YAMYAM_LEFT:
1835 case EL_YAMYAM_RIGHT:
1837 case EL_YAMYAM_DOWN:
1838 case EL_DARK_YAMYAM:
1841 case EL_SP_SNIKSNAK:
1842 case EL_SP_ELECTRON:
1851 case EL_AMOEBA_FULL:
1856 case EL_AMOEBA_DROP:
1857 if (y == lev_fieldy - 1)
1859 Feld[x][y] = EL_AMOEBA_GROWING;
1860 Store[x][y] = EL_AMOEBA_WET;
1864 case EL_DYNAMITE_ACTIVE:
1865 case EL_SP_DISK_RED_ACTIVE:
1866 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1870 MovDelay[x][y] = 96;
1873 case EL_EM_DYNAMITE_ACTIVE:
1874 MovDelay[x][y] = 32;
1878 local_player->lights_still_needed++;
1882 local_player->friends_still_needed++;
1887 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1891 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1892 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1893 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1895 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1896 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1898 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1899 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1901 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1905 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1910 game.belt_dir[belt_nr] = belt_dir;
1911 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913 else /* more than one switch -- set it like the first switch */
1915 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1920 #if !USE_BOTH_SWITCHGATE_SWITCHES
1921 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1923 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1926 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1928 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1932 case EL_LIGHT_SWITCH_ACTIVE:
1934 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1937 case EL_INVISIBLE_STEELWALL:
1938 case EL_INVISIBLE_WALL:
1939 case EL_INVISIBLE_SAND:
1940 if (game.light_time_left > 0 ||
1941 game.lenses_time_left > 0)
1942 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1945 case EL_EMC_MAGIC_BALL:
1946 if (game.ball_state)
1947 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1950 case EL_EMC_MAGIC_BALL_SWITCH:
1951 if (game.ball_state)
1952 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1955 case EL_TRIGGER_PLAYER:
1956 case EL_TRIGGER_ELEMENT:
1957 case EL_TRIGGER_CE_VALUE:
1958 case EL_TRIGGER_CE_SCORE:
1960 case EL_ANY_ELEMENT:
1961 case EL_CURRENT_CE_VALUE:
1962 case EL_CURRENT_CE_SCORE:
1979 /* reference elements should not be used on the playfield */
1980 Feld[x][y] = EL_EMPTY;
1984 if (IS_CUSTOM_ELEMENT(element))
1986 if (CAN_MOVE(element))
1989 #if USE_NEW_CUSTOM_VALUE
1990 if (!element_info[element].use_last_ce_value || init_game)
1991 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1994 else if (IS_GROUP_ELEMENT(element))
1996 Feld[x][y] = GetElementFromGroupElement(element);
1998 InitField(x, y, init_game);
2005 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2008 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2010 InitField(x, y, init_game);
2012 /* not needed to call InitMovDir() -- already done by InitField()! */
2013 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014 CAN_MOVE(Feld[x][y]))
2018 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2020 int old_element = Feld[x][y];
2022 InitField(x, y, init_game);
2024 /* not needed to call InitMovDir() -- already done by InitField()! */
2025 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2026 CAN_MOVE(old_element) &&
2027 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2030 /* this case is in fact a combination of not less than three bugs:
2031 first, it calls InitMovDir() for elements that can move, although this is
2032 already done by InitField(); then, it checks the element that was at this
2033 field _before_ the call to InitField() (which can change it); lastly, it
2034 was not called for "mole with direction" elements, which were treated as
2035 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2039 static int get_key_element_from_nr(int key_nr)
2041 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2042 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2043 EL_EM_KEY_1 : EL_KEY_1);
2045 return key_base_element + key_nr;
2048 static int get_next_dropped_element(struct PlayerInfo *player)
2050 return (player->inventory_size > 0 ?
2051 player->inventory_element[player->inventory_size - 1] :
2052 player->inventory_infinite_element != EL_UNDEFINED ?
2053 player->inventory_infinite_element :
2054 player->dynabombs_left > 0 ?
2055 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2059 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2061 /* pos >= 0: get element from bottom of the stack;
2062 pos < 0: get element from top of the stack */
2066 int min_inventory_size = -pos;
2067 int inventory_pos = player->inventory_size - min_inventory_size;
2068 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2070 return (player->inventory_size >= min_inventory_size ?
2071 player->inventory_element[inventory_pos] :
2072 player->inventory_infinite_element != EL_UNDEFINED ?
2073 player->inventory_infinite_element :
2074 player->dynabombs_left >= min_dynabombs_left ?
2075 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2080 int min_dynabombs_left = pos + 1;
2081 int min_inventory_size = pos + 1 - player->dynabombs_left;
2082 int inventory_pos = pos - player->dynabombs_left;
2084 return (player->inventory_infinite_element != EL_UNDEFINED ?
2085 player->inventory_infinite_element :
2086 player->dynabombs_left >= min_dynabombs_left ?
2087 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088 player->inventory_size >= min_inventory_size ?
2089 player->inventory_element[inventory_pos] :
2094 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2096 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2097 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2100 if (gpo1->sort_priority != gpo2->sort_priority)
2101 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2103 compare_result = gpo1->nr - gpo2->nr;
2105 return compare_result;
2108 void InitGameControlValues()
2112 for (i = 0; game_panel_controls[i].nr != -1; i++)
2114 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2115 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2116 struct TextPosInfo *pos = gpc->pos;
2118 int type = gpc->type;
2122 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2123 Error(ERR_EXIT, "this should not happen -- please debug");
2126 /* force update of game controls after initialization */
2127 gpc->value = gpc->last_value = -1;
2128 gpc->frame = gpc->last_frame = -1;
2129 gpc->gfx_frame = -1;
2131 /* determine panel value width for later calculation of alignment */
2132 if (type == TYPE_INTEGER || type == TYPE_STRING)
2134 pos->width = pos->size * getFontWidth(pos->font);
2135 pos->height = getFontHeight(pos->font);
2137 else if (type == TYPE_ELEMENT)
2139 pos->width = pos->size;
2140 pos->height = pos->size;
2143 /* fill structure for game panel draw order */
2145 gpo->sort_priority = pos->sort_priority;
2148 /* sort game panel controls according to sort_priority and control number */
2149 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2150 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2153 void UpdatePlayfieldElementCount()
2155 boolean use_element_count = FALSE;
2158 /* first check if it is needed at all to calculate playfield element count */
2159 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2160 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2161 use_element_count = TRUE;
2163 if (!use_element_count)
2166 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2167 element_info[i].element_count = 0;
2169 SCAN_PLAYFIELD(x, y)
2171 element_info[Feld[x][y]].element_count++;
2174 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2175 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2176 if (IS_IN_GROUP(j, i))
2177 element_info[EL_GROUP_START + i].element_count +=
2178 element_info[j].element_count;
2181 void UpdateGameControlValues()
2184 int time = (local_player->LevelSolved ?
2185 local_player->LevelSolved_CountingTime :
2186 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187 level.native_em_level->lev->time :
2188 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189 level.native_sp_level->game_sp->time_played :
2190 game.no_time_limit ? TimePlayed : TimeLeft);
2191 int score = (local_player->LevelSolved ?
2192 local_player->LevelSolved_CountingScore :
2193 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 level.native_em_level->lev->score :
2195 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 level.native_sp_level->game_sp->score :
2197 local_player->score);
2198 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199 level.native_em_level->lev->required :
2200 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201 level.native_sp_level->game_sp->infotrons_still_needed :
2202 local_player->gems_still_needed);
2203 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2204 level.native_em_level->lev->required > 0 :
2205 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2206 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2207 local_player->gems_still_needed > 0 ||
2208 local_player->sokobanfields_still_needed > 0 ||
2209 local_player->lights_still_needed > 0);
2211 UpdatePlayfieldElementCount();
2213 /* update game panel control values */
2215 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2218 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219 for (i = 0; i < MAX_NUM_KEYS; i++)
2220 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2224 if (game.centered_player_nr == -1)
2226 for (i = 0; i < MAX_PLAYERS; i++)
2228 /* only one player in Supaplex game engine */
2229 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2232 for (k = 0; k < MAX_NUM_KEYS; k++)
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2236 if (level.native_em_level->ply[i]->keys & (1 << k))
2237 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238 get_key_element_from_nr(k);
2240 else if (stored_player[i].key[k])
2241 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242 get_key_element_from_nr(k);
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2247 level.native_em_level->ply[i]->dynamite;
2248 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2249 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2250 level.native_sp_level->game_sp->red_disk_count;
2252 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253 stored_player[i].inventory_size;
2255 if (stored_player[i].num_white_keys > 0)
2256 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2259 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2260 stored_player[i].num_white_keys;
2265 int player_nr = game.centered_player_nr;
2267 for (k = 0; k < MAX_NUM_KEYS; k++)
2269 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2271 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2272 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2273 get_key_element_from_nr(k);
2275 else if (stored_player[player_nr].key[k])
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2280 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2282 level.native_em_level->ply[player_nr]->dynamite;
2283 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 level.native_sp_level->game_sp->red_disk_count;
2287 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288 stored_player[player_nr].inventory_size;
2290 if (stored_player[player_nr].num_white_keys > 0)
2291 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2293 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294 stored_player[player_nr].num_white_keys;
2297 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2299 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300 get_inventory_element_from_pos(local_player, i);
2301 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302 get_inventory_element_from_pos(local_player, -i - 1);
2305 game_panel_controls[GAME_PANEL_SCORE].value = score;
2306 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2308 game_panel_controls[GAME_PANEL_TIME].value = time;
2310 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2314 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2316 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2317 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2319 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2320 local_player->shield_normal_time_left;
2321 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2322 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2325 local_player->shield_deadly_time_left;
2327 game_panel_controls[GAME_PANEL_EXIT].value =
2328 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2330 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2331 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2332 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2333 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2334 EL_EMC_MAGIC_BALL_SWITCH);
2336 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2337 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2338 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2339 game.light_time_left;
2341 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2342 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2343 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2344 game.timegate_time_left;
2346 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2347 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2349 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2350 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2351 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2352 game.lenses_time_left;
2354 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2355 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2357 game.magnify_time_left;
2359 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2360 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2361 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2362 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2363 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2364 EL_BALLOON_SWITCH_NONE);
2366 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2367 local_player->dynabomb_count;
2368 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2369 local_player->dynabomb_size;
2370 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2371 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2373 game_panel_controls[GAME_PANEL_PENGUINS].value =
2374 local_player->friends_still_needed;
2376 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2377 local_player->sokobanfields_still_needed;
2378 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2379 local_player->sokobanfields_still_needed;
2381 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2382 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2384 for (i = 0; i < NUM_BELTS; i++)
2386 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2387 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2388 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2389 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2390 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2393 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2394 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2395 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2396 game.magic_wall_time_left;
2398 #if USE_PLAYER_GRAVITY
2399 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2400 local_player->gravity;
2402 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2405 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2406 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2410 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2411 game.panel.element[i].id : EL_UNDEFINED);
2413 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2414 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2415 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2416 element_info[game.panel.element_count[i].id].element_count : 0);
2418 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2420 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2421 element_info[game.panel.ce_score[i].id].collect_score : 0);
2423 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2424 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2425 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2426 element_info[game.panel.ce_score_element[i].id].collect_score :
2429 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2430 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433 /* update game panel control frames */
2435 for (i = 0; game_panel_controls[i].nr != -1; i++)
2437 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439 if (gpc->type == TYPE_ELEMENT)
2441 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443 int last_anim_random_frame = gfx.anim_random_frame;
2444 int element = gpc->value;
2445 int graphic = el2panelimg(element);
2447 if (gpc->value != gpc->last_value)
2450 gpc->gfx_random = INIT_GFX_RANDOM();
2456 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2457 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2458 gpc->gfx_random = INIT_GFX_RANDOM();
2461 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2462 gfx.anim_random_frame = gpc->gfx_random;
2464 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2465 gpc->gfx_frame = element_info[element].collect_score;
2467 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2470 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2471 gfx.anim_random_frame = last_anim_random_frame;
2477 void DisplayGameControlValues()
2479 boolean redraw_panel = FALSE;
2482 for (i = 0; game_panel_controls[i].nr != -1; i++)
2484 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2486 if (PANEL_DEACTIVATED(gpc->pos))
2489 if (gpc->value == gpc->last_value &&
2490 gpc->frame == gpc->last_frame)
2493 redraw_panel = TRUE;
2499 /* copy default game door content to main double buffer */
2501 /* !!! CHECK AGAIN !!! */
2502 SetPanelBackground();
2503 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2504 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2506 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2507 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2510 /* redraw game control buttons */
2512 RedrawGameButtons();
2518 game_status = GAME_MODE_PSEUDO_PANEL;
2521 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2523 for (i = 0; game_panel_controls[i].nr != -1; i++)
2527 int nr = game_panel_order[i].nr;
2528 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2530 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2533 struct TextPosInfo *pos = gpc->pos;
2534 int type = gpc->type;
2535 int value = gpc->value;
2536 int frame = gpc->frame;
2538 int last_value = gpc->last_value;
2539 int last_frame = gpc->last_frame;
2541 int size = pos->size;
2542 int font = pos->font;
2543 boolean draw_masked = pos->draw_masked;
2544 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2546 if (PANEL_DEACTIVATED(pos))
2550 if (value == last_value && frame == last_frame)
2554 gpc->last_value = value;
2555 gpc->last_frame = frame;
2558 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2580 /* clear background if value just changed its size (dynamic digits) */
2581 if ((last_value < value_change) != (value < value_change))
2583 int width1 = size1 * getFontWidth(font1);
2584 int width2 = size2 * getFontWidth(font2);
2585 int max_width = MAX(width1, width2);
2586 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2588 pos->width = max_width;
2590 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591 max_width, max_height);
2598 /* correct text size if "digits" is zero or less */
2600 size = strlen(int2str(value, size));
2602 /* dynamically correct text alignment */
2603 pos->width = size * getFontWidth(font);
2606 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2607 int2str(value, size), font, mask_mode);
2609 else if (type == TYPE_ELEMENT)
2611 int element, graphic;
2615 int dst_x = PANEL_XPOS(pos);
2616 int dst_y = PANEL_YPOS(pos);
2619 if (value != EL_UNDEFINED && value != EL_EMPTY)
2622 graphic = el2panelimg(value);
2624 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2627 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2631 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2634 width = graphic_info[graphic].width * size / TILESIZE;
2635 height = graphic_info[graphic].height * size / TILESIZE;
2639 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2640 dst_x - src_x, dst_y - src_y);
2641 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2646 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2651 if (value == EL_UNDEFINED || value == EL_EMPTY)
2653 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2654 graphic = el2panelimg(element);
2656 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2657 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2658 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2663 graphic = el2panelimg(value);
2665 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2668 width = graphic_info[graphic].width * size / TILESIZE;
2669 height = graphic_info[graphic].height * size / TILESIZE;
2671 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2674 else if (type == TYPE_STRING)
2676 boolean active = (value != 0);
2677 char *state_normal = "off";
2678 char *state_active = "on";
2679 char *state = (active ? state_active : state_normal);
2680 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2681 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2682 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2683 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2685 if (nr == GAME_PANEL_GRAVITY_STATE)
2687 int font1 = pos->font; /* (used for normal state) */
2688 int font2 = pos->font_alt; /* (used for active state) */
2690 int size1 = strlen(state_normal);
2691 int size2 = strlen(state_active);
2692 int width1 = size1 * getFontWidth(font1);
2693 int width2 = size2 * getFontWidth(font2);
2694 int max_width = MAX(width1, width2);
2695 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2697 pos->width = max_width;
2699 /* clear background for values that may have changed its size */
2700 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701 max_width, max_height);
2704 font = (active ? font2 : font1);
2714 /* don't truncate output if "chars" is zero or less */
2717 /* dynamically correct text alignment */
2718 pos->width = size * getFontWidth(font);
2722 s_cut = getStringCopyN(s, size);
2724 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2725 s_cut, font, mask_mode);
2731 redraw_mask |= REDRAW_DOOR_1;
2734 game_status = GAME_MODE_PLAYING;
2737 void UpdateAndDisplayGameControlValues()
2739 if (tape.warp_forward)
2742 UpdateGameControlValues();
2743 DisplayGameControlValues();
2746 void DrawGameValue_Emeralds(int value)
2748 struct TextPosInfo *pos = &game.panel.gems;
2749 int font_nr = pos->font;
2750 int font_width = getFontWidth(font_nr);
2751 int chars = pos->size;
2754 return; /* !!! USE NEW STUFF !!! */
2757 if (PANEL_DEACTIVATED(pos))
2760 pos->width = chars * font_width;
2762 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2765 void DrawGameValue_Dynamite(int value)
2767 struct TextPosInfo *pos = &game.panel.inventory_count;
2768 int font_nr = pos->font;
2769 int font_width = getFontWidth(font_nr);
2770 int chars = pos->size;
2773 return; /* !!! USE NEW STUFF !!! */
2776 if (PANEL_DEACTIVATED(pos))
2779 pos->width = chars * font_width;
2781 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2784 void DrawGameValue_Score(int value)
2786 struct TextPosInfo *pos = &game.panel.score;
2787 int font_nr = pos->font;
2788 int font_width = getFontWidth(font_nr);
2789 int chars = pos->size;
2792 return; /* !!! USE NEW STUFF !!! */
2795 if (PANEL_DEACTIVATED(pos))
2798 pos->width = chars * font_width;
2800 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2803 void DrawGameValue_Time(int value)
2805 struct TextPosInfo *pos = &game.panel.time;
2806 static int last_value = -1;
2809 int chars = pos->size;
2810 int font1_nr = pos->font;
2811 int font2_nr = pos->font_alt;
2812 int font_nr = font1_nr;
2813 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2816 return; /* !!! USE NEW STUFF !!! */
2819 if (PANEL_DEACTIVATED(pos))
2822 if (use_dynamic_chars) /* use dynamic number of chars */
2824 chars = (value < 1000 ? chars1 : chars2);
2825 font_nr = (value < 1000 ? font1_nr : font2_nr);
2828 /* clear background if value just changed its size (dynamic chars only) */
2829 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2831 int width1 = chars1 * getFontWidth(font1_nr);
2832 int width2 = chars2 * getFontWidth(font2_nr);
2833 int max_width = MAX(width1, width2);
2834 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2836 pos->width = max_width;
2838 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2839 max_width, max_height);
2842 pos->width = chars * getFontWidth(font_nr);
2844 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2849 void DrawGameValue_Level(int value)
2851 struct TextPosInfo *pos = &game.panel.level_number;
2854 int chars = pos->size;
2855 int font1_nr = pos->font;
2856 int font2_nr = pos->font_alt;
2857 int font_nr = font1_nr;
2858 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2861 return; /* !!! USE NEW STUFF !!! */
2864 if (PANEL_DEACTIVATED(pos))
2867 if (use_dynamic_chars) /* use dynamic number of chars */
2869 chars = (level_nr < 100 ? chars1 : chars2);
2870 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2873 pos->width = chars * getFontWidth(font_nr);
2875 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2878 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2883 return; /* !!! USE NEW STUFF !!! */
2886 for (i = 0; i < MAX_NUM_KEYS; i++)
2888 struct TextPosInfo *pos = &game.panel.key[i];
2889 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2890 int src_y = DOOR_GFX_PAGEY1 + 123;
2891 int dst_x = PANEL_XPOS(pos);
2892 int dst_y = PANEL_YPOS(pos);
2894 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2895 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2897 int graphic = el2edimg(element);
2899 if (PANEL_DEACTIVATED(pos))
2903 /* masked blit with tiles from half-size scaled bitmap does not work yet
2904 (no mask bitmap created for these sizes after loading and scaling) --
2905 solution: load without creating mask, scale, then create final mask */
2907 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2908 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2915 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2917 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2918 dst_x - src_x, dst_y - src_y);
2919 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2924 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2926 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2927 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2932 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2935 int key[MAX_NUM_KEYS];
2938 /* prevent EM engine from updating time/score values parallel to GameWon() */
2939 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2940 local_player->LevelSolved)
2943 for (i = 0; i < MAX_NUM_KEYS; i++)
2944 key[i] = key_bits & (1 << i);
2946 DrawGameValue_Level(level_nr);
2948 DrawGameValue_Emeralds(emeralds);
2949 DrawGameValue_Dynamite(dynamite);
2950 DrawGameValue_Score(score);
2951 DrawGameValue_Time(time);
2953 DrawGameValue_Keys(key);
2956 void UpdateGameDoorValues()
2958 UpdateGameControlValues();
2961 void DrawGameDoorValues()
2963 DisplayGameControlValues();
2966 void DrawGameDoorValues_OLD()
2968 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2969 int dynamite_value = 0;
2970 int score_value = (local_player->LevelSolved ? local_player->score_final :
2971 local_player->score);
2972 int gems_value = local_player->gems_still_needed;
2976 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2978 DrawGameDoorValues_EM();
2983 if (game.centered_player_nr == -1)
2985 for (i = 0; i < MAX_PLAYERS; i++)
2987 for (j = 0; j < MAX_NUM_KEYS; j++)
2988 if (stored_player[i].key[j])
2989 key_bits |= (1 << j);
2991 dynamite_value += stored_player[i].inventory_size;
2996 int player_nr = game.centered_player_nr;
2998 for (i = 0; i < MAX_NUM_KEYS; i++)
2999 if (stored_player[player_nr].key[i])
3000 key_bits |= (1 << i);
3002 dynamite_value = stored_player[player_nr].inventory_size;
3005 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3011 =============================================================================
3013 -----------------------------------------------------------------------------
3014 initialize game engine due to level / tape version number
3015 =============================================================================
3018 static void InitGameEngine()
3020 int i, j, k, l, x, y;
3022 /* set game engine from tape file when re-playing, else from level file */
3023 game.engine_version = (tape.playing ? tape.engine_version :
3024 level.game_version);
3026 /* ---------------------------------------------------------------------- */
3027 /* set flags for bugs and changes according to active game engine version */
3028 /* ---------------------------------------------------------------------- */
3031 Summary of bugfix/change:
3032 Fixed handling for custom elements that change when pushed by the player.
3034 Fixed/changed in version:
3038 Before 3.1.0, custom elements that "change when pushing" changed directly
3039 after the player started pushing them (until then handled in "DigField()").
3040 Since 3.1.0, these custom elements are not changed until the "pushing"
3041 move of the element is finished (now handled in "ContinueMoving()").
3043 Affected levels/tapes:
3044 The first condition is generally needed for all levels/tapes before version
3045 3.1.0, which might use the old behaviour before it was changed; known tapes
3046 that are affected are some tapes from the level set "Walpurgis Gardens" by
3048 The second condition is an exception from the above case and is needed for
3049 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3050 above (including some development versions of 3.1.0), but before it was
3051 known that this change would break tapes like the above and was fixed in
3052 3.1.1, so that the changed behaviour was active although the engine version
3053 while recording maybe was before 3.1.0. There is at least one tape that is
3054 affected by this exception, which is the tape for the one-level set "Bug
3055 Machine" by Juergen Bonhagen.
3058 game.use_change_when_pushing_bug =
3059 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3062 tape.game_version < VERSION_IDENT(3,1,1,0)));
3065 Summary of bugfix/change:
3066 Fixed handling for blocking the field the player leaves when moving.
3068 Fixed/changed in version:
3072 Before 3.1.1, when "block last field when moving" was enabled, the field
3073 the player is leaving when moving was blocked for the time of the move,
3074 and was directly unblocked afterwards. This resulted in the last field
3075 being blocked for exactly one less than the number of frames of one player
3076 move. Additionally, even when blocking was disabled, the last field was
3077 blocked for exactly one frame.
3078 Since 3.1.1, due to changes in player movement handling, the last field
3079 is not blocked at all when blocking is disabled. When blocking is enabled,
3080 the last field is blocked for exactly the number of frames of one player
3081 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3082 last field is blocked for exactly one more than the number of frames of
3085 Affected levels/tapes:
3086 (!!! yet to be determined -- probably many !!!)
3089 game.use_block_last_field_bug =
3090 (game.engine_version < VERSION_IDENT(3,1,1,0));
3093 Summary of bugfix/change:
3094 Changed behaviour of CE changes with multiple changes per single frame.
3096 Fixed/changed in version:
3100 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3101 This resulted in race conditions where CEs seem to behave strange in some
3102 situations (where triggered CE changes were just skipped because there was
3103 already a CE change on that tile in the playfield in that engine frame).
3104 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3105 (The number of changes per frame must be limited in any case, because else
3106 it is easily possible to define CE changes that would result in an infinite
3107 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3108 should be set large enough so that it would only be reached in cases where
3109 the corresponding CE change conditions run into a loop. Therefore, it seems
3110 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3111 maximal number of change pages for custom elements.)
3113 Affected levels/tapes:
3117 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3118 game.max_num_changes_per_frame = 1;
3120 game.max_num_changes_per_frame =
3121 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3124 /* ---------------------------------------------------------------------- */
3126 /* default scan direction: scan playfield from top/left to bottom/right */
3127 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3129 /* dynamically adjust element properties according to game engine version */
3130 InitElementPropertiesEngine(game.engine_version);
3133 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3134 printf(" tape version == %06d [%s] [file: %06d]\n",
3135 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3137 printf(" => game.engine_version == %06d\n", game.engine_version);
3140 /* ---------- initialize player's initial move delay --------------------- */
3142 /* dynamically adjust player properties according to level information */
3143 for (i = 0; i < MAX_PLAYERS; i++)
3144 game.initial_move_delay_value[i] =
3145 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3147 /* dynamically adjust player properties according to game engine version */
3148 for (i = 0; i < MAX_PLAYERS; i++)
3149 game.initial_move_delay[i] =
3150 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3151 game.initial_move_delay_value[i] : 0);
3153 /* ---------- initialize player's initial push delay --------------------- */
3155 /* dynamically adjust player properties according to game engine version */
3156 game.initial_push_delay_value =
3157 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3159 /* ---------- initialize changing elements ------------------------------- */
3161 /* initialize changing elements information */
3162 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3164 struct ElementInfo *ei = &element_info[i];
3166 /* this pointer might have been changed in the level editor */
3167 ei->change = &ei->change_page[0];
3169 if (!IS_CUSTOM_ELEMENT(i))
3171 ei->change->target_element = EL_EMPTY_SPACE;
3172 ei->change->delay_fixed = 0;
3173 ei->change->delay_random = 0;
3174 ei->change->delay_frames = 1;
3177 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3179 ei->has_change_event[j] = FALSE;
3181 ei->event_page_nr[j] = 0;
3182 ei->event_page[j] = &ei->change_page[0];
3186 /* add changing elements from pre-defined list */
3187 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3189 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3190 struct ElementInfo *ei = &element_info[ch_delay->element];
3192 ei->change->target_element = ch_delay->target_element;
3193 ei->change->delay_fixed = ch_delay->change_delay;
3195 ei->change->pre_change_function = ch_delay->pre_change_function;
3196 ei->change->change_function = ch_delay->change_function;
3197 ei->change->post_change_function = ch_delay->post_change_function;
3199 ei->change->can_change = TRUE;
3200 ei->change->can_change_or_has_action = TRUE;
3202 ei->has_change_event[CE_DELAY] = TRUE;
3204 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3205 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3208 /* ---------- initialize internal run-time variables --------------------- */
3210 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3212 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3214 for (j = 0; j < ei->num_change_pages; j++)
3216 ei->change_page[j].can_change_or_has_action =
3217 (ei->change_page[j].can_change |
3218 ei->change_page[j].has_action);
3222 /* add change events from custom element configuration */
3223 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227 for (j = 0; j < ei->num_change_pages; j++)
3229 if (!ei->change_page[j].can_change_or_has_action)
3232 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3234 /* only add event page for the first page found with this event */
3235 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3237 ei->has_change_event[k] = TRUE;
3239 ei->event_page_nr[k] = j;
3240 ei->event_page[k] = &ei->change_page[j];
3247 /* ---------- initialize reference elements in change conditions --------- */
3249 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3251 int element = EL_CUSTOM_START + i;
3252 struct ElementInfo *ei = &element_info[element];
3254 for (j = 0; j < ei->num_change_pages; j++)
3256 int trigger_element = ei->change_page[j].initial_trigger_element;
3258 if (trigger_element >= EL_PREV_CE_8 &&
3259 trigger_element <= EL_NEXT_CE_8)
3260 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3262 ei->change_page[j].trigger_element = trigger_element;
3267 /* ---------- initialize run-time trigger player and element ------------- */
3269 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3271 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3273 for (j = 0; j < ei->num_change_pages; j++)
3275 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3276 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3277 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3278 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3279 ei->change_page[j].actual_trigger_ce_value = 0;
3280 ei->change_page[j].actual_trigger_ce_score = 0;
3284 /* ---------- initialize trigger events ---------------------------------- */
3286 /* initialize trigger events information */
3287 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3288 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3289 trigger_events[i][j] = FALSE;
3291 /* add trigger events from element change event properties */
3292 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294 struct ElementInfo *ei = &element_info[i];
3296 for (j = 0; j < ei->num_change_pages; j++)
3298 if (!ei->change_page[j].can_change_or_has_action)
3301 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3303 int trigger_element = ei->change_page[j].trigger_element;
3305 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3307 if (ei->change_page[j].has_event[k])
3309 if (IS_GROUP_ELEMENT(trigger_element))
3311 struct ElementGroupInfo *group =
3312 element_info[trigger_element].group;
3314 for (l = 0; l < group->num_elements_resolved; l++)
3315 trigger_events[group->element_resolved[l]][k] = TRUE;
3317 else if (trigger_element == EL_ANY_ELEMENT)
3318 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3319 trigger_events[l][k] = TRUE;
3321 trigger_events[trigger_element][k] = TRUE;
3328 /* ---------- initialize push delay -------------------------------------- */
3330 /* initialize push delay values to default */
3331 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333 if (!IS_CUSTOM_ELEMENT(i))
3335 /* set default push delay values (corrected since version 3.0.7-1) */
3336 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3338 element_info[i].push_delay_fixed = 2;
3339 element_info[i].push_delay_random = 8;
3343 element_info[i].push_delay_fixed = 8;
3344 element_info[i].push_delay_random = 8;
3349 /* set push delay value for certain elements from pre-defined list */
3350 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3352 int e = push_delay_list[i].element;
3354 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3355 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3358 /* set push delay value for Supaplex elements for newer engine versions */
3359 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3361 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3363 if (IS_SP_ELEMENT(i))
3365 /* set SP push delay to just enough to push under a falling zonk */
3366 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3368 element_info[i].push_delay_fixed = delay;
3369 element_info[i].push_delay_random = 0;
3374 /* ---------- initialize move stepsize ----------------------------------- */
3376 /* initialize move stepsize values to default */
3377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378 if (!IS_CUSTOM_ELEMENT(i))
3379 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3381 /* set move stepsize value for certain elements from pre-defined list */
3382 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3384 int e = move_stepsize_list[i].element;
3386 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3389 /* ---------- initialize collect score ----------------------------------- */
3391 /* initialize collect score values for custom elements from initial value */
3392 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3393 if (IS_CUSTOM_ELEMENT(i))
3394 element_info[i].collect_score = element_info[i].collect_score_initial;
3396 /* ---------- initialize collect count ----------------------------------- */
3398 /* initialize collect count values for non-custom elements */
3399 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3400 if (!IS_CUSTOM_ELEMENT(i))
3401 element_info[i].collect_count_initial = 0;
3403 /* add collect count values for all elements from pre-defined list */
3404 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3405 element_info[collect_count_list[i].element].collect_count_initial =
3406 collect_count_list[i].count;
3408 /* ---------- initialize access direction -------------------------------- */
3410 /* initialize access direction values to default (access from every side) */
3411 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3412 if (!IS_CUSTOM_ELEMENT(i))
3413 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3415 /* set access direction value for certain elements from pre-defined list */
3416 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3417 element_info[access_direction_list[i].element].access_direction =
3418 access_direction_list[i].direction;
3420 /* ---------- initialize explosion content ------------------------------- */
3421 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423 if (IS_CUSTOM_ELEMENT(i))
3426 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3428 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3430 element_info[i].content.e[x][y] =
3431 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3432 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3433 i == EL_PLAYER_3 ? EL_EMERALD :
3434 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3435 i == EL_MOLE ? EL_EMERALD_RED :
3436 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3437 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3438 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3439 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3440 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3441 i == EL_WALL_EMERALD ? EL_EMERALD :
3442 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3443 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3444 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3445 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3446 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3447 i == EL_WALL_PEARL ? EL_PEARL :
3448 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3453 /* ---------- initialize recursion detection ------------------------------ */
3454 recursion_loop_depth = 0;
3455 recursion_loop_detected = FALSE;
3456 recursion_loop_element = EL_UNDEFINED;
3458 /* ---------- initialize graphics engine ---------------------------------- */
3459 game.scroll_delay_value =
3460 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3461 setup.scroll_delay ? setup.scroll_delay_value : 0);
3462 game.scroll_delay_value =
3463 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3466 int get_num_special_action(int element, int action_first, int action_last)
3468 int num_special_action = 0;
3471 for (i = action_first; i <= action_last; i++)
3473 boolean found = FALSE;
3475 for (j = 0; j < NUM_DIRECTIONS; j++)
3476 if (el_act_dir2img(element, i, j) !=
3477 el_act_dir2img(element, ACTION_DEFAULT, j))
3481 num_special_action++;
3486 return num_special_action;
3491 =============================================================================
3493 -----------------------------------------------------------------------------
3494 initialize and start new game
3495 =============================================================================
3500 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3501 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3502 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3504 boolean do_fading = (game_status == GAME_MODE_MAIN);
3507 int initial_move_dir = MV_DOWN;
3509 int initial_move_dir = MV_NONE;
3513 game_status = GAME_MODE_PLAYING;
3516 /* needed if different viewport properties defined for playing */
3517 ChangeViewportPropertiesIfNeeded();
3521 DrawCompleteVideoDisplay();
3525 InitGameControlValues();
3527 /* don't play tapes over network */
3528 network_playing = (options.network && !tape.playing);
3530 for (i = 0; i < MAX_PLAYERS; i++)
3532 struct PlayerInfo *player = &stored_player[i];
3534 player->index_nr = i;
3535 player->index_bit = (1 << i);
3536 player->element_nr = EL_PLAYER_1 + i;
3538 player->present = FALSE;
3539 player->active = FALSE;
3540 player->mapped = FALSE;
3542 player->killed = FALSE;
3543 player->reanimated = FALSE;
3546 player->effective_action = 0;
3547 player->programmed_action = 0;
3550 player->score_final = 0;
3552 player->gems_still_needed = level.gems_needed;
3553 player->sokobanfields_still_needed = 0;
3554 player->lights_still_needed = 0;
3555 player->friends_still_needed = 0;
3557 for (j = 0; j < MAX_NUM_KEYS; j++)
3558 player->key[j] = FALSE;
3560 player->num_white_keys = 0;
3562 player->dynabomb_count = 0;
3563 player->dynabomb_size = 1;
3564 player->dynabombs_left = 0;
3565 player->dynabomb_xl = FALSE;
3567 player->MovDir = initial_move_dir;
3570 player->GfxDir = initial_move_dir;
3571 player->GfxAction = ACTION_DEFAULT;
3573 player->StepFrame = 0;
3575 player->initial_element = player->element_nr;
3576 player->artwork_element =
3577 (level.use_artwork_element[i] ? level.artwork_element[i] :
3578 player->element_nr);
3579 player->use_murphy = FALSE;
3581 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3582 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3584 player->gravity = level.initial_player_gravity[i];
3586 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3588 player->actual_frame_counter = 0;
3590 player->step_counter = 0;
3592 player->last_move_dir = initial_move_dir;
3594 player->is_active = FALSE;
3596 player->is_waiting = FALSE;
3597 player->is_moving = FALSE;
3598 player->is_auto_moving = FALSE;
3599 player->is_digging = FALSE;
3600 player->is_snapping = FALSE;
3601 player->is_collecting = FALSE;
3602 player->is_pushing = FALSE;
3603 player->is_switching = FALSE;
3604 player->is_dropping = FALSE;
3605 player->is_dropping_pressed = FALSE;
3607 player->is_bored = FALSE;
3608 player->is_sleeping = FALSE;
3610 player->frame_counter_bored = -1;
3611 player->frame_counter_sleeping = -1;
3613 player->anim_delay_counter = 0;
3614 player->post_delay_counter = 0;
3616 player->dir_waiting = initial_move_dir;
3617 player->action_waiting = ACTION_DEFAULT;
3618 player->last_action_waiting = ACTION_DEFAULT;
3619 player->special_action_bored = ACTION_DEFAULT;
3620 player->special_action_sleeping = ACTION_DEFAULT;
3622 player->switch_x = -1;
3623 player->switch_y = -1;
3625 player->drop_x = -1;
3626 player->drop_y = -1;
3628 player->show_envelope = 0;
3630 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3632 player->push_delay = -1; /* initialized when pushing starts */
3633 player->push_delay_value = game.initial_push_delay_value;
3635 player->drop_delay = 0;
3636 player->drop_pressed_delay = 0;
3638 player->last_jx = -1;
3639 player->last_jy = -1;
3643 player->shield_normal_time_left = 0;
3644 player->shield_deadly_time_left = 0;
3646 player->inventory_infinite_element = EL_UNDEFINED;
3647 player->inventory_size = 0;
3649 if (level.use_initial_inventory[i])
3651 for (j = 0; j < level.initial_inventory_size[i]; j++)
3653 int element = level.initial_inventory_content[i][j];
3654 int collect_count = element_info[element].collect_count_initial;
3657 if (!IS_CUSTOM_ELEMENT(element))
3660 if (collect_count == 0)
3661 player->inventory_infinite_element = element;
3663 for (k = 0; k < collect_count; k++)
3664 if (player->inventory_size < MAX_INVENTORY_SIZE)
3665 player->inventory_element[player->inventory_size++] = element;
3669 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3670 SnapField(player, 0, 0);
3672 player->LevelSolved = FALSE;
3673 player->GameOver = FALSE;
3675 player->LevelSolved_GameWon = FALSE;
3676 player->LevelSolved_GameEnd = FALSE;
3677 player->LevelSolved_PanelOff = FALSE;
3678 player->LevelSolved_SaveTape = FALSE;
3679 player->LevelSolved_SaveScore = FALSE;
3680 player->LevelSolved_CountingTime = 0;
3681 player->LevelSolved_CountingScore = 0;
3683 map_player_action[i] = i;
3686 network_player_action_received = FALSE;
3688 #if defined(NETWORK_AVALIABLE)
3689 /* initial null action */
3690 if (network_playing)
3691 SendToServer_MovePlayer(MV_NONE);
3700 TimeLeft = level.time;
3703 ScreenMovDir = MV_NONE;
3707 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3709 AllPlayersGone = FALSE;
3711 game.no_time_limit = (level.time == 0);
3713 game.yamyam_content_nr = 0;
3714 game.robot_wheel_active = FALSE;
3715 game.magic_wall_active = FALSE;
3716 game.magic_wall_time_left = 0;
3717 game.light_time_left = 0;
3718 game.timegate_time_left = 0;
3719 game.switchgate_pos = 0;
3720 game.wind_direction = level.wind_direction_initial;
3722 #if !USE_PLAYER_GRAVITY
3723 game.gravity = FALSE;
3724 game.explosions_delayed = TRUE;
3727 game.lenses_time_left = 0;
3728 game.magnify_time_left = 0;
3730 game.ball_state = level.ball_state_initial;
3731 game.ball_content_nr = 0;
3733 game.envelope_active = FALSE;
3735 /* set focus to local player for network games, else to all players */
3736 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3737 game.centered_player_nr_next = game.centered_player_nr;
3738 game.set_centered_player = FALSE;
3740 if (network_playing && tape.recording)
3742 /* store client dependent player focus when recording network games */
3743 tape.centered_player_nr_next = game.centered_player_nr_next;
3744 tape.set_centered_player = TRUE;
3747 for (i = 0; i < NUM_BELTS; i++)
3749 game.belt_dir[i] = MV_NONE;
3750 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3753 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3754 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3756 SCAN_PLAYFIELD(x, y)
3758 Feld[x][y] = level.field[x][y];
3759 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3760 ChangeDelay[x][y] = 0;
3761 ChangePage[x][y] = -1;
3762 #if USE_NEW_CUSTOM_VALUE
3763 CustomValue[x][y] = 0; /* initialized in InitField() */
3765 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3767 WasJustMoving[x][y] = 0;
3768 WasJustFalling[x][y] = 0;
3769 CheckCollision[x][y] = 0;
3770 CheckImpact[x][y] = 0;
3772 Pushed[x][y] = FALSE;
3774 ChangeCount[x][y] = 0;
3775 ChangeEvent[x][y] = -1;
3777 ExplodePhase[x][y] = 0;
3778 ExplodeDelay[x][y] = 0;
3779 ExplodeField[x][y] = EX_TYPE_NONE;
3781 RunnerVisit[x][y] = 0;
3782 PlayerVisit[x][y] = 0;
3785 GfxRandom[x][y] = INIT_GFX_RANDOM();
3786 GfxElement[x][y] = EL_UNDEFINED;
3787 GfxAction[x][y] = ACTION_DEFAULT;
3788 GfxDir[x][y] = MV_NONE;
3789 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3792 SCAN_PLAYFIELD(x, y)
3794 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3796 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3798 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3801 InitField(x, y, TRUE);
3803 ResetGfxAnimation(x, y);
3808 for (i = 0; i < MAX_PLAYERS; i++)
3810 struct PlayerInfo *player = &stored_player[i];
3812 /* set number of special actions for bored and sleeping animation */
3813 player->num_special_action_bored =
3814 get_num_special_action(player->artwork_element,
3815 ACTION_BORING_1, ACTION_BORING_LAST);
3816 player->num_special_action_sleeping =
3817 get_num_special_action(player->artwork_element,
3818 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3821 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3822 emulate_sb ? EMU_SOKOBAN :
3823 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3825 #if USE_NEW_ALL_SLIPPERY
3826 /* initialize type of slippery elements */
3827 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3829 if (!IS_CUSTOM_ELEMENT(i))
3831 /* default: elements slip down either to the left or right randomly */
3832 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3834 /* SP style elements prefer to slip down on the left side */
3835 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3836 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3838 /* BD style elements prefer to slip down on the left side */
3839 if (game.emulation == EMU_BOULDERDASH)
3840 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3845 /* initialize explosion and ignition delay */
3846 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3848 if (!IS_CUSTOM_ELEMENT(i))
3851 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3852 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3853 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3854 int last_phase = (num_phase + 1) * delay;
3855 int half_phase = (num_phase / 2) * delay;
3857 element_info[i].explosion_delay = last_phase - 1;
3858 element_info[i].ignition_delay = half_phase;
3860 if (i == EL_BLACK_ORB)
3861 element_info[i].ignition_delay = 1;
3865 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3866 element_info[i].explosion_delay = 1;
3868 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3869 element_info[i].ignition_delay = 1;
3873 /* correct non-moving belts to start moving left */
3874 for (i = 0; i < NUM_BELTS; i++)
3875 if (game.belt_dir[i] == MV_NONE)
3876 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3878 #if USE_NEW_PLAYER_ASSIGNMENTS
3879 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3880 /* choose default local player */
3881 local_player = &stored_player[0];
3883 for (i = 0; i < MAX_PLAYERS; i++)
3884 stored_player[i].connected = FALSE;
3886 local_player->connected = TRUE;
3887 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3891 /* try to guess locally connected team mode players (needed for correct
3892 assignment of player figures from level to locally playing players) */
3894 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (tape.player_participates[i])
3896 stored_player[i].connected = TRUE;
3898 else if (setup.team_mode && !options.network)
3900 /* try to guess locally connected team mode players (needed for correct
3901 assignment of player figures from level to locally playing players) */
3903 for (i = 0; i < MAX_PLAYERS; i++)
3904 if (setup.input[i].use_joystick ||
3905 setup.input[i].key.left != KSYM_UNDEFINED)
3906 stored_player[i].connected = TRUE;
3910 for (i = 0; i < MAX_PLAYERS; i++)
3911 printf("::: player %d: %s\n", i,
3912 (stored_player[i].connected ? "connected" : "not connected"));
3914 for (i = 0; i < MAX_PLAYERS; i++)
3915 printf("::: player %d: %s\n", i,
3916 (stored_player[i].present ? "present" : "not present"));
3919 /* check if any connected player was not found in playfield */
3920 for (i = 0; i < MAX_PLAYERS; i++)
3922 struct PlayerInfo *player = &stored_player[i];
3924 if (player->connected && !player->present)
3926 struct PlayerInfo *field_player = NULL;
3929 printf("::: looking for field player for player %d ...\n", i);
3932 /* assign first free player found that is present in the playfield */
3934 /* first try: look for unmapped playfield player that is not connected */
3935 if (field_player == NULL)
3936 for (j = 0; j < MAX_PLAYERS; j++)
3937 if (stored_player[j].present &&
3938 !stored_player[j].mapped &&
3939 !stored_player[j].connected)
3940 field_player = &stored_player[j];
3942 /* second try: look for *any* unmapped playfield player */
3943 if (field_player == NULL)
3944 for (j = 0; j < MAX_PLAYERS; j++)
3945 if (stored_player[j].present &&
3946 !stored_player[j].mapped)
3947 field_player = &stored_player[j];
3949 if (field_player != NULL)
3951 int jx = field_player->jx, jy = field_player->jy;
3954 printf("::: found player figure %d\n", field_player->index_nr);
3957 player->present = FALSE;
3958 player->active = FALSE;
3960 field_player->present = TRUE;
3961 field_player->active = TRUE;
3964 player->initial_element = field_player->initial_element;
3965 player->artwork_element = field_player->artwork_element;
3967 player->block_last_field = field_player->block_last_field;
3968 player->block_delay_adjustment = field_player->block_delay_adjustment;
3971 StorePlayer[jx][jy] = field_player->element_nr;
3973 field_player->jx = field_player->last_jx = jx;
3974 field_player->jy = field_player->last_jy = jy;
3976 if (local_player == player)
3977 local_player = field_player;
3979 map_player_action[field_player->index_nr] = i;
3981 field_player->mapped = TRUE;
3984 printf("::: map_player_action[%d] == %d\n",
3985 field_player->index_nr, i);
3990 if (player->connected && player->present)
3991 player->mapped = TRUE;
3996 /* check if any connected player was not found in playfield */
3997 for (i = 0; i < MAX_PLAYERS; i++)
3999 struct PlayerInfo *player = &stored_player[i];
4001 if (player->connected && !player->present)
4003 for (j = 0; j < MAX_PLAYERS; j++)
4005 struct PlayerInfo *field_player = &stored_player[j];
4006 int jx = field_player->jx, jy = field_player->jy;
4008 /* assign first free player found that is present in the playfield */
4009 if (field_player->present && !field_player->connected)
4011 player->present = TRUE;
4012 player->active = TRUE;
4014 field_player->present = FALSE;
4015 field_player->active = FALSE;
4017 player->initial_element = field_player->initial_element;
4018 player->artwork_element = field_player->artwork_element;
4020 player->block_last_field = field_player->block_last_field;
4021 player->block_delay_adjustment = field_player->block_delay_adjustment;
4023 StorePlayer[jx][jy] = player->element_nr;
4025 player->jx = player->last_jx = jx;
4026 player->jy = player->last_jy = jy;
4036 printf("::: local_player->present == %d\n", local_player->present);
4041 /* when playing a tape, eliminate all players who do not participate */
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044 for (i = 0; i < MAX_PLAYERS; i++)
4046 if (stored_player[i].active &&
4047 !tape.player_participates[map_player_action[i]])
4049 struct PlayerInfo *player = &stored_player[i];
4050 int jx = player->jx, jy = player->jy;
4052 player->active = FALSE;
4053 StorePlayer[jx][jy] = 0;
4054 Feld[jx][jy] = EL_EMPTY;
4058 for (i = 0; i < MAX_PLAYERS; i++)
4060 if (stored_player[i].active &&
4061 !tape.player_participates[i])
4063 struct PlayerInfo *player = &stored_player[i];
4064 int jx = player->jx, jy = player->jy;
4066 player->active = FALSE;
4067 StorePlayer[jx][jy] = 0;
4068 Feld[jx][jy] = EL_EMPTY;
4073 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4075 /* when in single player mode, eliminate all but the first active player */
4077 for (i = 0; i < MAX_PLAYERS; i++)
4079 if (stored_player[i].active)
4081 for (j = i + 1; j < MAX_PLAYERS; j++)
4083 if (stored_player[j].active)
4085 struct PlayerInfo *player = &stored_player[j];
4086 int jx = player->jx, jy = player->jy;
4088 player->active = FALSE;
4089 player->present = FALSE;
4091 StorePlayer[jx][jy] = 0;
4092 Feld[jx][jy] = EL_EMPTY;
4099 /* when recording the game, store which players take part in the game */
4102 #if USE_NEW_PLAYER_ASSIGNMENTS
4103 for (i = 0; i < MAX_PLAYERS; i++)
4104 if (stored_player[i].connected)
4105 tape.player_participates[i] = TRUE;
4107 for (i = 0; i < MAX_PLAYERS; i++)
4108 if (stored_player[i].active)
4109 tape.player_participates[i] = TRUE;
4115 for (i = 0; i < MAX_PLAYERS; i++)
4117 struct PlayerInfo *player = &stored_player[i];
4119 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4124 if (local_player == player)
4125 printf("Player %d is local player.\n", i+1);
4129 if (BorderElement == EL_EMPTY)
4132 SBX_Right = lev_fieldx - SCR_FIELDX;
4134 SBY_Lower = lev_fieldy - SCR_FIELDY;
4139 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4141 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4146 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4147 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4149 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4150 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4152 if (EVEN(SCR_FIELDX))
4154 if (EVEN(SCR_FIELDY))
4159 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4162 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4166 /* if local player not found, look for custom element that might create
4167 the player (make some assumptions about the right custom element) */
4168 if (!local_player->present)
4170 int start_x = 0, start_y = 0;
4171 int found_rating = 0;
4172 int found_element = EL_UNDEFINED;
4173 int player_nr = local_player->index_nr;
4175 SCAN_PLAYFIELD(x, y)
4177 int element = Feld[x][y];
4182 if (level.use_start_element[player_nr] &&
4183 level.start_element[player_nr] == element &&
4190 found_element = element;
4193 if (!IS_CUSTOM_ELEMENT(element))
4196 if (CAN_CHANGE(element))
4198 for (i = 0; i < element_info[element].num_change_pages; i++)
4200 /* check for player created from custom element as single target */
4201 content = element_info[element].change_page[i].target_element;
4202 is_player = ELEM_IS_PLAYER(content);
4204 if (is_player && (found_rating < 3 ||
4205 (found_rating == 3 && element < found_element)))
4211 found_element = element;
4216 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4218 /* check for player created from custom element as explosion content */
4219 content = element_info[element].content.e[xx][yy];
4220 is_player = ELEM_IS_PLAYER(content);
4222 if (is_player && (found_rating < 2 ||
4223 (found_rating == 2 && element < found_element)))
4225 start_x = x + xx - 1;
4226 start_y = y + yy - 1;
4229 found_element = element;
4232 if (!CAN_CHANGE(element))
4235 for (i = 0; i < element_info[element].num_change_pages; i++)
4237 /* check for player created from custom element as extended target */
4239 element_info[element].change_page[i].target_content.e[xx][yy];
4241 is_player = ELEM_IS_PLAYER(content);
4243 if (is_player && (found_rating < 1 ||
4244 (found_rating == 1 && element < found_element)))
4246 start_x = x + xx - 1;
4247 start_y = y + yy - 1;
4250 found_element = element;
4256 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4257 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4260 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4261 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4266 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4267 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4268 local_player->jx - MIDPOSX);
4270 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4271 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4272 local_player->jy - MIDPOSY);
4276 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4280 /* do not use PLAYING mask for fading out from main screen */
4281 game_status = GAME_MODE_MAIN;
4286 if (!game.restart_level)
4287 CloseDoor(DOOR_CLOSE_1);
4290 if (level_editor_test_game)
4291 FadeSkipNextFadeIn();
4293 FadeSetEnterScreen();
4295 if (level_editor_test_game)
4296 fading = fading_none;
4298 fading = menu.destination;
4302 FadeOut(REDRAW_FIELD);
4305 FadeOut(REDRAW_FIELD);
4309 game_status = GAME_MODE_PLAYING;
4312 /* !!! FIX THIS (START) !!! */
4313 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4315 InitGameEngine_EM();
4317 /* blit playfield from scroll buffer to normal back buffer for fading in */
4318 BlitScreenToBitmap_EM(backbuffer);
4320 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4322 InitGameEngine_SP();
4324 /* blit playfield from scroll buffer to normal back buffer for fading in */
4325 BlitScreenToBitmap_SP(backbuffer);
4332 /* after drawing the level, correct some elements */
4333 if (game.timegate_time_left == 0)
4334 CloseAllOpenTimegates();
4337 BlitScreenToBitmap(backbuffer);
4339 /* blit playfield from scroll buffer to normal back buffer for fading in */
4340 if (setup.soft_scrolling)
4341 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4344 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4346 /* !!! FIX THIS (END) !!! */
4349 FadeIn(REDRAW_FIELD);
4352 FadeIn(REDRAW_FIELD);
4357 if (!game.restart_level)
4359 /* copy default game door content to main double buffer */
4362 /* !!! CHECK AGAIN !!! */
4363 SetPanelBackground();
4364 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4365 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4367 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4369 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4370 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4371 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4372 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4375 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4376 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4380 SetPanelBackground();
4381 SetDrawBackgroundMask(REDRAW_DOOR_1);
4384 UpdateAndDisplayGameControlValues();
4386 UpdateGameDoorValues();
4387 DrawGameDoorValues();
4390 if (!game.restart_level)
4394 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4395 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4396 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4400 /* copy actual game door content to door double buffer for OpenDoor() */
4401 BlitBitmap(drawto, bitmap_db_door,
4402 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4404 OpenDoor(DOOR_OPEN_ALL);
4406 PlaySound(SND_GAME_STARTING);
4408 if (setup.sound_music)
4411 KeyboardAutoRepeatOffUnlessAutoplay();
4415 for (i = 0; i < MAX_PLAYERS; i++)
4416 printf("Player %d %sactive.\n",
4417 i + 1, (stored_player[i].active ? "" : "not "));
4428 if (!game.restart_level && !tape.playing)
4430 LevelStats_incPlayed(level_nr);
4432 SaveLevelSetup_SeriesInfo();
4435 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4439 game.restart_level = FALSE;
4442 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4444 /* this is used for non-R'n'D game engines to update certain engine values */
4446 /* needed to determine if sounds are played within the visible screen area */
4447 scroll_x = actual_scroll_x;
4448 scroll_y = actual_scroll_y;
4451 void InitMovDir(int x, int y)
4453 int i, element = Feld[x][y];
4454 static int xy[4][2] =
4461 static int direction[3][4] =
4463 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4464 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4465 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4474 Feld[x][y] = EL_BUG;
4475 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4478 case EL_SPACESHIP_RIGHT:
4479 case EL_SPACESHIP_UP:
4480 case EL_SPACESHIP_LEFT:
4481 case EL_SPACESHIP_DOWN:
4482 Feld[x][y] = EL_SPACESHIP;
4483 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4486 case EL_BD_BUTTERFLY_RIGHT:
4487 case EL_BD_BUTTERFLY_UP:
4488 case EL_BD_BUTTERFLY_LEFT:
4489 case EL_BD_BUTTERFLY_DOWN:
4490 Feld[x][y] = EL_BD_BUTTERFLY;
4491 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4494 case EL_BD_FIREFLY_RIGHT:
4495 case EL_BD_FIREFLY_UP:
4496 case EL_BD_FIREFLY_LEFT:
4497 case EL_BD_FIREFLY_DOWN:
4498 Feld[x][y] = EL_BD_FIREFLY;
4499 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4502 case EL_PACMAN_RIGHT:
4504 case EL_PACMAN_LEFT:
4505 case EL_PACMAN_DOWN:
4506 Feld[x][y] = EL_PACMAN;
4507 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4510 case EL_YAMYAM_LEFT:
4511 case EL_YAMYAM_RIGHT:
4513 case EL_YAMYAM_DOWN:
4514 Feld[x][y] = EL_YAMYAM;
4515 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4518 case EL_SP_SNIKSNAK:
4519 MovDir[x][y] = MV_UP;
4522 case EL_SP_ELECTRON:
4523 MovDir[x][y] = MV_LEFT;
4530 Feld[x][y] = EL_MOLE;
4531 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4535 if (IS_CUSTOM_ELEMENT(element))
4537 struct ElementInfo *ei = &element_info[element];
4538 int move_direction_initial = ei->move_direction_initial;
4539 int move_pattern = ei->move_pattern;
4541 if (move_direction_initial == MV_START_PREVIOUS)
4543 if (MovDir[x][y] != MV_NONE)
4546 move_direction_initial = MV_START_AUTOMATIC;
4549 if (move_direction_initial == MV_START_RANDOM)
4550 MovDir[x][y] = 1 << RND(4);
4551 else if (move_direction_initial & MV_ANY_DIRECTION)
4552 MovDir[x][y] = move_direction_initial;
4553 else if (move_pattern == MV_ALL_DIRECTIONS ||
4554 move_pattern == MV_TURNING_LEFT ||
4555 move_pattern == MV_TURNING_RIGHT ||
4556 move_pattern == MV_TURNING_LEFT_RIGHT ||
4557 move_pattern == MV_TURNING_RIGHT_LEFT ||
4558 move_pattern == MV_TURNING_RANDOM)
4559 MovDir[x][y] = 1 << RND(4);
4560 else if (move_pattern == MV_HORIZONTAL)
4561 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4562 else if (move_pattern == MV_VERTICAL)
4563 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4564 else if (move_pattern & MV_ANY_DIRECTION)
4565 MovDir[x][y] = element_info[element].move_pattern;
4566 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4567 move_pattern == MV_ALONG_RIGHT_SIDE)
4569 /* use random direction as default start direction */
4570 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4571 MovDir[x][y] = 1 << RND(4);
4573 for (i = 0; i < NUM_DIRECTIONS; i++)
4575 int x1 = x + xy[i][0];
4576 int y1 = y + xy[i][1];
4578 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4580 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4581 MovDir[x][y] = direction[0][i];
4583 MovDir[x][y] = direction[1][i];
4592 MovDir[x][y] = 1 << RND(4);
4594 if (element != EL_BUG &&
4595 element != EL_SPACESHIP &&
4596 element != EL_BD_BUTTERFLY &&
4597 element != EL_BD_FIREFLY)
4600 for (i = 0; i < NUM_DIRECTIONS; i++)
4602 int x1 = x + xy[i][0];
4603 int y1 = y + xy[i][1];
4605 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4607 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4609 MovDir[x][y] = direction[0][i];
4612 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4613 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4615 MovDir[x][y] = direction[1][i];
4624 GfxDir[x][y] = MovDir[x][y];
4627 void InitAmoebaNr(int x, int y)
4630 int group_nr = AmoebeNachbarNr(x, y);
4634 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4636 if (AmoebaCnt[i] == 0)
4644 AmoebaNr[x][y] = group_nr;
4645 AmoebaCnt[group_nr]++;
4646 AmoebaCnt2[group_nr]++;
4649 static void PlayerWins(struct PlayerInfo *player)
4651 player->LevelSolved = TRUE;
4652 player->GameOver = TRUE;
4654 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4655 level.native_em_level->lev->score : player->score);
4657 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4659 player->LevelSolved_CountingScore = player->score_final;
4664 static int time, time_final;
4665 static int score, score_final;
4666 static int game_over_delay_1 = 0;
4667 static int game_over_delay_2 = 0;
4668 int game_over_delay_value_1 = 50;
4669 int game_over_delay_value_2 = 50;
4671 if (!local_player->LevelSolved_GameWon)
4675 /* do not start end game actions before the player stops moving (to exit) */
4676 if (local_player->MovPos)
4679 local_player->LevelSolved_GameWon = TRUE;
4680 local_player->LevelSolved_SaveTape = tape.recording;
4681 local_player->LevelSolved_SaveScore = !tape.playing;
4685 LevelStats_incSolved(level_nr);
4687 SaveLevelSetup_SeriesInfo();
4690 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4694 if (tape.auto_play) /* tape might already be stopped here */
4695 tape.auto_play_level_solved = TRUE;
4701 game_over_delay_1 = game_over_delay_value_1;
4702 game_over_delay_2 = game_over_delay_value_2;
4704 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4705 score = score_final = local_player->score_final;
4710 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4712 else if (game.no_time_limit && TimePlayed < 999)
4715 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4718 local_player->score_final = score_final;
4720 if (level_editor_test_game)
4723 score = score_final;
4726 local_player->LevelSolved_CountingTime = time;
4727 local_player->LevelSolved_CountingScore = score;
4729 game_panel_controls[GAME_PANEL_TIME].value = time;
4730 game_panel_controls[GAME_PANEL_SCORE].value = score;
4732 DisplayGameControlValues();
4734 DrawGameValue_Time(time);
4735 DrawGameValue_Score(score);
4739 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4741 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4743 /* close exit door after last player */
4744 if ((AllPlayersGone &&
4745 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4746 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4747 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4748 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4749 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4751 int element = Feld[ExitX][ExitY];
4754 if (element == EL_EM_EXIT_OPEN ||
4755 element == EL_EM_STEEL_EXIT_OPEN)
4762 Feld[ExitX][ExitY] =
4763 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4764 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4765 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4766 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4767 EL_EM_STEEL_EXIT_CLOSING);
4769 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4773 /* player disappears */
4774 DrawLevelField(ExitX, ExitY);
4777 for (i = 0; i < MAX_PLAYERS; i++)
4779 struct PlayerInfo *player = &stored_player[i];
4781 if (player->present)
4783 RemovePlayer(player);
4785 /* player disappears */
4786 DrawLevelField(player->jx, player->jy);
4791 PlaySound(SND_GAME_WINNING);
4794 if (game_over_delay_1 > 0)
4796 game_over_delay_1--;
4801 if (time != time_final)
4803 int time_to_go = ABS(time_final - time);
4804 int time_count_dir = (time < time_final ? +1 : -1);
4805 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4807 time += time_count_steps * time_count_dir;
4808 score += time_count_steps * level.score[SC_TIME_BONUS];
4811 local_player->LevelSolved_CountingTime = time;
4812 local_player->LevelSolved_CountingScore = score;
4814 game_panel_controls[GAME_PANEL_TIME].value = time;
4815 game_panel_controls[GAME_PANEL_SCORE].value = score;
4817 DisplayGameControlValues();
4819 DrawGameValue_Time(time);
4820 DrawGameValue_Score(score);
4823 if (time == time_final)
4824 StopSound(SND_GAME_LEVELTIME_BONUS);
4825 else if (setup.sound_loops)
4826 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4828 PlaySound(SND_GAME_LEVELTIME_BONUS);
4833 local_player->LevelSolved_PanelOff = TRUE;
4835 if (game_over_delay_2 > 0)
4837 game_over_delay_2--;
4850 boolean raise_level = FALSE;
4852 local_player->LevelSolved_GameEnd = TRUE;
4854 CloseDoor(DOOR_CLOSE_1);
4856 if (local_player->LevelSolved_SaveTape)
4863 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4865 SaveTape(tape.level_nr); /* ask to save tape */
4869 if (level_editor_test_game)
4871 game_status = GAME_MODE_MAIN;
4874 DrawAndFadeInMainMenu(REDRAW_FIELD);
4882 if (!local_player->LevelSolved_SaveScore)
4885 FadeOut(REDRAW_FIELD);
4888 game_status = GAME_MODE_MAIN;
4890 DrawAndFadeInMainMenu(REDRAW_FIELD);
4895 if (level_nr == leveldir_current->handicap_level)
4897 leveldir_current->handicap_level++;
4899 SaveLevelSetup_SeriesInfo();
4902 if (level_nr < leveldir_current->last_level)
4903 raise_level = TRUE; /* advance to next level */
4905 if ((hi_pos = NewHiScore()) >= 0)
4907 game_status = GAME_MODE_SCORES;
4909 DrawHallOfFame(hi_pos);
4920 FadeOut(REDRAW_FIELD);
4923 game_status = GAME_MODE_MAIN;
4931 DrawAndFadeInMainMenu(REDRAW_FIELD);
4940 LoadScore(level_nr);
4942 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4943 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4946 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4948 if (local_player->score_final > highscore[k].Score)
4950 /* player has made it to the hall of fame */
4952 if (k < MAX_SCORE_ENTRIES - 1)
4954 int m = MAX_SCORE_ENTRIES - 1;
4957 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4958 if (strEqual(setup.player_name, highscore[l].Name))
4960 if (m == k) /* player's new highscore overwrites his old one */
4964 for (l = m; l > k; l--)
4966 strcpy(highscore[l].Name, highscore[l - 1].Name);
4967 highscore[l].Score = highscore[l - 1].Score;
4974 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4975 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4976 highscore[k].Score = local_player->score_final;
4982 else if (!strncmp(setup.player_name, highscore[k].Name,
4983 MAX_PLAYER_NAME_LEN))
4984 break; /* player already there with a higher score */
4990 SaveScore(level_nr);
4995 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4997 int element = Feld[x][y];
4998 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4999 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5000 int horiz_move = (dx != 0);
5001 int sign = (horiz_move ? dx : dy);
5002 int step = sign * element_info[element].move_stepsize;
5004 /* special values for move stepsize for spring and things on conveyor belt */
5007 if (CAN_FALL(element) &&
5008 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5009 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5010 else if (element == EL_SPRING)
5011 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5017 inline static int getElementMoveStepsize(int x, int y)
5019 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5022 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5024 if (player->GfxAction != action || player->GfxDir != dir)
5027 printf("Player frame reset! (%d => %d, %d => %d)\n",
5028 player->GfxAction, action, player->GfxDir, dir);
5031 player->GfxAction = action;
5032 player->GfxDir = dir;
5034 player->StepFrame = 0;
5038 #if USE_GFX_RESET_GFX_ANIMATION
5039 static void ResetGfxFrame(int x, int y, boolean redraw)
5041 int element = Feld[x][y];
5042 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5043 int last_gfx_frame = GfxFrame[x][y];
5045 if (graphic_info[graphic].anim_global_sync)
5046 GfxFrame[x][y] = FrameCounter;
5047 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5048 GfxFrame[x][y] = CustomValue[x][y];
5049 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5050 GfxFrame[x][y] = element_info[element].collect_score;
5051 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5052 GfxFrame[x][y] = ChangeDelay[x][y];
5054 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5055 DrawLevelGraphicAnimation(x, y, graphic);
5059 static void ResetGfxAnimation(int x, int y)
5061 GfxAction[x][y] = ACTION_DEFAULT;
5062 GfxDir[x][y] = MovDir[x][y];
5065 #if USE_GFX_RESET_GFX_ANIMATION
5066 ResetGfxFrame(x, y, FALSE);
5070 static void ResetRandomAnimationValue(int x, int y)
5072 GfxRandom[x][y] = INIT_GFX_RANDOM();
5075 void InitMovingField(int x, int y, int direction)
5077 int element = Feld[x][y];
5078 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5079 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5082 boolean is_moving_before, is_moving_after;
5084 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5087 /* check if element was/is moving or being moved before/after mode change */
5090 is_moving_before = (WasJustMoving[x][y] != 0);
5092 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5093 is_moving_before = WasJustMoving[x][y];
5096 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5098 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5100 /* reset animation only for moving elements which change direction of moving
5101 or which just started or stopped moving
5102 (else CEs with property "can move" / "not moving" are reset each frame) */
5103 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5105 if (is_moving_before != is_moving_after ||
5106 direction != MovDir[x][y])
5107 ResetGfxAnimation(x, y);
5109 if ((is_moving_before || is_moving_after) && !continues_moving)
5110 ResetGfxAnimation(x, y);
5113 if (!continues_moving)
5114 ResetGfxAnimation(x, y);
5117 MovDir[x][y] = direction;
5118 GfxDir[x][y] = direction;
5120 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5121 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5122 direction == MV_DOWN && CAN_FALL(element) ?
5123 ACTION_FALLING : ACTION_MOVING);
5125 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5126 ACTION_FALLING : ACTION_MOVING);
5129 /* this is needed for CEs with property "can move" / "not moving" */
5131 if (is_moving_after)
5133 if (Feld[newx][newy] == EL_EMPTY)
5134 Feld[newx][newy] = EL_BLOCKED;
5136 MovDir[newx][newy] = MovDir[x][y];
5138 #if USE_NEW_CUSTOM_VALUE
5139 CustomValue[newx][newy] = CustomValue[x][y];
5142 GfxFrame[newx][newy] = GfxFrame[x][y];
5143 GfxRandom[newx][newy] = GfxRandom[x][y];
5144 GfxAction[newx][newy] = GfxAction[x][y];
5145 GfxDir[newx][newy] = GfxDir[x][y];
5149 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5151 int direction = MovDir[x][y];
5152 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5153 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5159 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5161 int oldx = x, oldy = y;
5162 int direction = MovDir[x][y];
5164 if (direction == MV_LEFT)
5166 else if (direction == MV_RIGHT)
5168 else if (direction == MV_UP)
5170 else if (direction == MV_DOWN)
5173 *comes_from_x = oldx;
5174 *comes_from_y = oldy;
5177 int MovingOrBlocked2Element(int x, int y)
5179 int element = Feld[x][y];
5181 if (element == EL_BLOCKED)
5185 Blocked2Moving(x, y, &oldx, &oldy);
5186 return Feld[oldx][oldy];
5192 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5194 /* like MovingOrBlocked2Element(), but if element is moving
5195 and (x,y) is the field the moving element is just leaving,
5196 return EL_BLOCKED instead of the element value */
5197 int element = Feld[x][y];
5199 if (IS_MOVING(x, y))
5201 if (element == EL_BLOCKED)
5205 Blocked2Moving(x, y, &oldx, &oldy);
5206 return Feld[oldx][oldy];
5215 static void RemoveField(int x, int y)
5217 Feld[x][y] = EL_EMPTY;
5223 #if USE_NEW_CUSTOM_VALUE
5224 CustomValue[x][y] = 0;
5228 ChangeDelay[x][y] = 0;
5229 ChangePage[x][y] = -1;
5230 Pushed[x][y] = FALSE;
5233 ExplodeField[x][y] = EX_TYPE_NONE;
5236 GfxElement[x][y] = EL_UNDEFINED;
5237 GfxAction[x][y] = ACTION_DEFAULT;
5238 GfxDir[x][y] = MV_NONE;
5240 /* !!! this would prevent the removed tile from being redrawn !!! */
5241 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5245 void RemoveMovingField(int x, int y)
5247 int oldx = x, oldy = y, newx = x, newy = y;
5248 int element = Feld[x][y];
5249 int next_element = EL_UNDEFINED;
5251 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5254 if (IS_MOVING(x, y))
5256 Moving2Blocked(x, y, &newx, &newy);
5258 if (Feld[newx][newy] != EL_BLOCKED)
5260 /* element is moving, but target field is not free (blocked), but
5261 already occupied by something different (example: acid pool);
5262 in this case, only remove the moving field, but not the target */
5264 RemoveField(oldx, oldy);
5266 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5268 TEST_DrawLevelField(oldx, oldy);
5273 else if (element == EL_BLOCKED)
5275 Blocked2Moving(x, y, &oldx, &oldy);
5276 if (!IS_MOVING(oldx, oldy))
5280 if (element == EL_BLOCKED &&
5281 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5282 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5283 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5284 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5285 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5286 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5287 next_element = get_next_element(Feld[oldx][oldy]);
5289 RemoveField(oldx, oldy);
5290 RemoveField(newx, newy);
5292 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5294 if (next_element != EL_UNDEFINED)
5295 Feld[oldx][oldy] = next_element;
5297 TEST_DrawLevelField(oldx, oldy);
5298 TEST_DrawLevelField(newx, newy);
5301 void DrawDynamite(int x, int y)
5303 int sx = SCREENX(x), sy = SCREENY(y);
5304 int graphic = el2img(Feld[x][y]);
5307 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5310 if (IS_WALKABLE_INSIDE(Back[x][y]))
5314 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5315 else if (Store[x][y])
5316 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5318 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5320 if (Back[x][y] || Store[x][y])
5321 DrawGraphicThruMask(sx, sy, graphic, frame);
5323 DrawGraphic(sx, sy, graphic, frame);
5326 void CheckDynamite(int x, int y)
5328 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5332 if (MovDelay[x][y] != 0)
5335 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5341 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5346 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5348 boolean num_checked_players = 0;
5351 for (i = 0; i < MAX_PLAYERS; i++)
5353 if (stored_player[i].active)
5355 int sx = stored_player[i].jx;
5356 int sy = stored_player[i].jy;
5358 if (num_checked_players == 0)
5365 *sx1 = MIN(*sx1, sx);
5366 *sy1 = MIN(*sy1, sy);
5367 *sx2 = MAX(*sx2, sx);
5368 *sy2 = MAX(*sy2, sy);
5371 num_checked_players++;
5376 static boolean checkIfAllPlayersFitToScreen_RND()
5378 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5380 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5382 return (sx2 - sx1 < SCR_FIELDX &&
5383 sy2 - sy1 < SCR_FIELDY);
5386 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5388 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5390 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5392 *sx = (sx1 + sx2) / 2;
5393 *sy = (sy1 + sy2) / 2;
5396 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5397 boolean center_screen, boolean quick_relocation)
5399 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5400 boolean no_delay = (tape.warp_forward);
5401 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5402 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5404 if (quick_relocation)
5406 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5408 if (!level.shifted_relocation || center_screen)
5410 /* quick relocation (without scrolling), with centering of screen */
5412 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5413 x > SBX_Right + MIDPOSX ? SBX_Right :
5416 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5417 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5422 /* quick relocation (without scrolling), but do not center screen */
5424 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5425 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5428 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5429 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5432 int offset_x = x + (scroll_x - center_scroll_x);
5433 int offset_y = y + (scroll_y - center_scroll_y);
5435 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5436 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5437 offset_x - MIDPOSX);
5439 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5440 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5441 offset_y - MIDPOSY);
5447 if (!level.shifted_relocation || center_screen)
5449 /* quick relocation (without scrolling), with centering of screen */
5451 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5452 x > SBX_Right + MIDPOSX ? SBX_Right :
5455 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5456 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5461 /* quick relocation (without scrolling), but do not center screen */
5463 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5464 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5467 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5468 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5471 int offset_x = x + (scroll_x - center_scroll_x);
5472 int offset_y = y + (scroll_y - center_scroll_y);
5474 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5475 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5476 offset_x - MIDPOSX);
5478 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5479 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5480 offset_y - MIDPOSY);
5483 /* quick relocation (without scrolling), inside visible screen area */
5485 int offset = game.scroll_delay_value;
5487 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5488 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5489 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5491 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5492 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5493 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5495 /* don't scroll over playfield boundaries */
5496 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5497 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5499 /* don't scroll over playfield boundaries */
5500 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5501 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5505 RedrawPlayfield(TRUE, 0,0,0,0);
5510 int scroll_xx, scroll_yy;
5512 if (!level.shifted_relocation || center_screen)
5514 /* visible relocation (with scrolling), with centering of screen */
5516 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5517 x > SBX_Right + MIDPOSX ? SBX_Right :
5520 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5521 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5526 /* visible relocation (with scrolling), but do not center screen */
5528 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5529 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5532 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5533 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5536 int offset_x = x + (scroll_x - center_scroll_x);
5537 int offset_y = y + (scroll_y - center_scroll_y);
5539 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5540 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5541 offset_x - MIDPOSX);
5543 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5544 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5545 offset_y - MIDPOSY);
5550 /* visible relocation (with scrolling), with centering of screen */
5552 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5553 x > SBX_Right + MIDPOSX ? SBX_Right :
5556 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5557 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5561 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5563 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5566 int fx = FX, fy = FY;
5568 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5569 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5571 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5577 fx += dx * TILEX / 2;
5578 fy += dy * TILEY / 2;
5580 ScrollLevel(dx, dy);
5583 /* scroll in two steps of half tile size to make things smoother */
5584 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5586 Delay(wait_delay_value);
5588 /* scroll second step to align at full tile size */
5590 Delay(wait_delay_value);
5595 Delay(wait_delay_value);
5599 void RelocatePlayer(int jx, int jy, int el_player_raw)
5601 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5602 int player_nr = GET_PLAYER_NR(el_player);
5603 struct PlayerInfo *player = &stored_player[player_nr];
5604 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5605 boolean no_delay = (tape.warp_forward);
5606 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5607 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5608 int old_jx = player->jx;
5609 int old_jy = player->jy;
5610 int old_element = Feld[old_jx][old_jy];
5611 int element = Feld[jx][jy];
5612 boolean player_relocated = (old_jx != jx || old_jy != jy);
5614 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5615 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5616 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5617 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5618 int leave_side_horiz = move_dir_horiz;
5619 int leave_side_vert = move_dir_vert;
5620 int enter_side = enter_side_horiz | enter_side_vert;
5621 int leave_side = leave_side_horiz | leave_side_vert;
5623 if (player->GameOver) /* do not reanimate dead player */
5626 if (!player_relocated) /* no need to relocate the player */
5629 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5631 RemoveField(jx, jy); /* temporarily remove newly placed player */
5632 DrawLevelField(jx, jy);
5635 if (player->present)
5637 while (player->MovPos)
5639 ScrollPlayer(player, SCROLL_GO_ON);
5640 ScrollScreen(NULL, SCROLL_GO_ON);
5642 AdvanceFrameAndPlayerCounters(player->index_nr);
5647 Delay(wait_delay_value);
5650 DrawPlayer(player); /* needed here only to cleanup last field */
5651 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5653 player->is_moving = FALSE;
5656 if (IS_CUSTOM_ELEMENT(old_element))
5657 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5659 player->index_bit, leave_side);
5661 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5663 player->index_bit, leave_side);
5665 Feld[jx][jy] = el_player;
5666 InitPlayerField(jx, jy, el_player, TRUE);
5668 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5669 possible that the relocation target field did not contain a player element,
5670 but a walkable element, to which the new player was relocated -- in this
5671 case, restore that (already initialized!) element on the player field */
5672 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5674 Feld[jx][jy] = element; /* restore previously existing element */
5676 /* !!! do not initialize already initialized element a second time !!! */
5677 /* (this causes at least problems with "element creation" CE trigger for
5678 already existing elements, and existing Sokoban fields counted twice) */
5679 InitField(jx, jy, FALSE);
5683 /* only visually relocate centered player */
5684 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5685 FALSE, level.instant_relocation);
5687 TestIfPlayerTouchesBadThing(jx, jy);
5688 TestIfPlayerTouchesCustomElement(jx, jy);
5690 if (IS_CUSTOM_ELEMENT(element))
5691 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5692 player->index_bit, enter_side);
5694 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5695 player->index_bit, enter_side);
5698 if (player->is_switching)
5700 /* ensure that relocation while still switching an element does not cause
5701 a new element to be treated as also switched directly after relocation
5702 (this is important for teleporter switches that teleport the player to
5703 a place where another teleporter switch is in the same direction, which
5704 would then incorrectly be treated as immediately switched before the
5705 direction key that caused the switch was released) */
5707 player->switch_x += jx - old_jx;
5708 player->switch_y += jy - old_jy;
5713 void Explode(int ex, int ey, int phase, int mode)
5719 /* !!! eliminate this variable !!! */
5720 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5722 if (game.explosions_delayed)
5724 ExplodeField[ex][ey] = mode;
5728 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5730 int center_element = Feld[ex][ey];
5731 int artwork_element, explosion_element; /* set these values later */
5734 /* --- This is only really needed (and now handled) in "Impact()". --- */
5735 /* do not explode moving elements that left the explode field in time */
5736 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5737 center_element == EL_EMPTY &&
5738 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5743 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5744 if (mode == EX_TYPE_NORMAL ||
5745 mode == EX_TYPE_CENTER ||
5746 mode == EX_TYPE_CROSS)
5747 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5750 /* remove things displayed in background while burning dynamite */
5751 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5754 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5756 /* put moving element to center field (and let it explode there) */
5757 center_element = MovingOrBlocked2Element(ex, ey);
5758 RemoveMovingField(ex, ey);
5759 Feld[ex][ey] = center_element;
5762 /* now "center_element" is finally determined -- set related values now */
5763 artwork_element = center_element; /* for custom player artwork */
5764 explosion_element = center_element; /* for custom player artwork */
5766 if (IS_PLAYER(ex, ey))
5768 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5770 artwork_element = stored_player[player_nr].artwork_element;
5772 if (level.use_explosion_element[player_nr])
5774 explosion_element = level.explosion_element[player_nr];
5775 artwork_element = explosion_element;
5780 if (mode == EX_TYPE_NORMAL ||
5781 mode == EX_TYPE_CENTER ||
5782 mode == EX_TYPE_CROSS)
5783 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5786 last_phase = element_info[explosion_element].explosion_delay + 1;
5788 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5790 int xx = x - ex + 1;
5791 int yy = y - ey + 1;
5794 if (!IN_LEV_FIELD(x, y) ||
5795 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5796 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5799 element = Feld[x][y];
5801 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5803 element = MovingOrBlocked2Element(x, y);
5805 if (!IS_EXPLOSION_PROOF(element))
5806 RemoveMovingField(x, y);
5809 /* indestructible elements can only explode in center (but not flames) */
5810 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5811 mode == EX_TYPE_BORDER)) ||
5812 element == EL_FLAMES)
5815 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5816 behaviour, for example when touching a yamyam that explodes to rocks
5817 with active deadly shield, a rock is created under the player !!! */
5818 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5820 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5821 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5822 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5824 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5827 if (IS_ACTIVE_BOMB(element))
5829 /* re-activate things under the bomb like gate or penguin */
5830 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5837 /* save walkable background elements while explosion on same tile */
5838 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5839 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5840 Back[x][y] = element;
5842 /* ignite explodable elements reached by other explosion */
5843 if (element == EL_EXPLOSION)
5844 element = Store2[x][y];
5846 if (AmoebaNr[x][y] &&
5847 (element == EL_AMOEBA_FULL ||
5848 element == EL_BD_AMOEBA ||
5849 element == EL_AMOEBA_GROWING))
5851 AmoebaCnt[AmoebaNr[x][y]]--;
5852 AmoebaCnt2[AmoebaNr[x][y]]--;
5857 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5859 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5861 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5863 if (PLAYERINFO(ex, ey)->use_murphy)
5864 Store[x][y] = EL_EMPTY;
5867 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5868 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5869 else if (ELEM_IS_PLAYER(center_element))
5870 Store[x][y] = EL_EMPTY;
5871 else if (center_element == EL_YAMYAM)
5872 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5873 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5874 Store[x][y] = element_info[center_element].content.e[xx][yy];
5876 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5877 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5878 otherwise) -- FIX THIS !!! */
5879 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5880 Store[x][y] = element_info[element].content.e[1][1];
5882 else if (!CAN_EXPLODE(element))
5883 Store[x][y] = element_info[element].content.e[1][1];
5886 Store[x][y] = EL_EMPTY;
5888 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5889 center_element == EL_AMOEBA_TO_DIAMOND)
5890 Store2[x][y] = element;
5892 Feld[x][y] = EL_EXPLOSION;
5893 GfxElement[x][y] = artwork_element;
5895 ExplodePhase[x][y] = 1;
5896 ExplodeDelay[x][y] = last_phase;
5901 if (center_element == EL_YAMYAM)
5902 game.yamyam_content_nr =
5903 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5915 GfxFrame[x][y] = 0; /* restart explosion animation */
5917 last_phase = ExplodeDelay[x][y];
5919 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5923 /* activate this even in non-DEBUG version until cause for crash in
5924 getGraphicAnimationFrame() (see below) is found and eliminated */
5930 /* this can happen if the player leaves an explosion just in time */
5931 if (GfxElement[x][y] == EL_UNDEFINED)
5932 GfxElement[x][y] = EL_EMPTY;
5934 if (GfxElement[x][y] == EL_UNDEFINED)
5937 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5938 printf("Explode(): This should never happen!\n");
5941 GfxElement[x][y] = EL_EMPTY;
5947 border_element = Store2[x][y];
5948 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5949 border_element = StorePlayer[x][y];
5951 if (phase == element_info[border_element].ignition_delay ||
5952 phase == last_phase)
5954 boolean border_explosion = FALSE;
5956 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5957 !PLAYER_EXPLOSION_PROTECTED(x, y))
5959 KillPlayerUnlessExplosionProtected(x, y);
5960 border_explosion = TRUE;
5962 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5964 Feld[x][y] = Store2[x][y];
5967 border_explosion = TRUE;
5969 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5971 AmoebeUmwandeln(x, y);
5973 border_explosion = TRUE;
5976 /* if an element just explodes due to another explosion (chain-reaction),
5977 do not immediately end the new explosion when it was the last frame of
5978 the explosion (as it would be done in the following "if"-statement!) */
5979 if (border_explosion && phase == last_phase)
5983 if (phase == last_phase)
5987 element = Feld[x][y] = Store[x][y];
5988 Store[x][y] = Store2[x][y] = 0;
5989 GfxElement[x][y] = EL_UNDEFINED;
5991 /* player can escape from explosions and might therefore be still alive */
5992 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5993 element <= EL_PLAYER_IS_EXPLODING_4)
5995 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5996 int explosion_element = EL_PLAYER_1 + player_nr;
5997 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5998 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6000 if (level.use_explosion_element[player_nr])
6001 explosion_element = level.explosion_element[player_nr];
6003 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6004 element_info[explosion_element].content.e[xx][yy]);
6007 /* restore probably existing indestructible background element */
6008 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6009 element = Feld[x][y] = Back[x][y];
6012 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6013 GfxDir[x][y] = MV_NONE;
6014 ChangeDelay[x][y] = 0;
6015 ChangePage[x][y] = -1;
6017 #if USE_NEW_CUSTOM_VALUE
6018 CustomValue[x][y] = 0;
6021 InitField_WithBug2(x, y, FALSE);
6023 TEST_DrawLevelField(x, y);
6025 TestIfElementTouchesCustomElement(x, y);
6027 if (GFX_CRUMBLED(element))
6028 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6030 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6031 StorePlayer[x][y] = 0;
6033 if (ELEM_IS_PLAYER(element))
6034 RelocatePlayer(x, y, element);
6036 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6038 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6039 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6042 TEST_DrawLevelFieldCrumbled(x, y);
6044 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6046 DrawLevelElement(x, y, Back[x][y]);
6047 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6049 else if (IS_WALKABLE_UNDER(Back[x][y]))
6051 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6052 DrawLevelElementThruMask(x, y, Back[x][y]);
6054 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6055 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6059 void DynaExplode(int ex, int ey)
6062 int dynabomb_element = Feld[ex][ey];
6063 int dynabomb_size = 1;
6064 boolean dynabomb_xl = FALSE;
6065 struct PlayerInfo *player;
6066 static int xy[4][2] =
6074 if (IS_ACTIVE_BOMB(dynabomb_element))
6076 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6077 dynabomb_size = player->dynabomb_size;
6078 dynabomb_xl = player->dynabomb_xl;
6079 player->dynabombs_left++;
6082 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6084 for (i = 0; i < NUM_DIRECTIONS; i++)
6086 for (j = 1; j <= dynabomb_size; j++)
6088 int x = ex + j * xy[i][0];
6089 int y = ey + j * xy[i][1];
6092 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6095 element = Feld[x][y];
6097 /* do not restart explosions of fields with active bombs */
6098 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6101 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6103 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6104 !IS_DIGGABLE(element) && !dynabomb_xl)
6110 void Bang(int x, int y)
6112 int element = MovingOrBlocked2Element(x, y);
6113 int explosion_type = EX_TYPE_NORMAL;
6115 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6117 struct PlayerInfo *player = PLAYERINFO(x, y);
6119 #if USE_FIX_CE_ACTION_WITH_PLAYER
6120 element = Feld[x][y] = player->initial_element;
6122 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6123 player->element_nr);
6126 if (level.use_explosion_element[player->index_nr])
6128 int explosion_element = level.explosion_element[player->index_nr];
6130 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6131 explosion_type = EX_TYPE_CROSS;
6132 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6133 explosion_type = EX_TYPE_CENTER;
6141 case EL_BD_BUTTERFLY:
6144 case EL_DARK_YAMYAM:
6148 RaiseScoreElement(element);
6151 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6152 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6153 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6154 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6155 case EL_DYNABOMB_INCREASE_NUMBER:
6156 case EL_DYNABOMB_INCREASE_SIZE:
6157 case EL_DYNABOMB_INCREASE_POWER:
6158 explosion_type = EX_TYPE_DYNA;
6161 case EL_DC_LANDMINE:
6163 case EL_EM_EXIT_OPEN:
6164 case EL_EM_STEEL_EXIT_OPEN:
6166 explosion_type = EX_TYPE_CENTER;
6171 case EL_LAMP_ACTIVE:
6172 case EL_AMOEBA_TO_DIAMOND:
6173 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6174 explosion_type = EX_TYPE_CENTER;
6178 if (element_info[element].explosion_type == EXPLODES_CROSS)
6179 explosion_type = EX_TYPE_CROSS;
6180 else if (element_info[element].explosion_type == EXPLODES_1X1)
6181 explosion_type = EX_TYPE_CENTER;
6185 if (explosion_type == EX_TYPE_DYNA)
6188 Explode(x, y, EX_PHASE_START, explosion_type);
6190 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6193 void SplashAcid(int x, int y)
6195 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6196 (!IN_LEV_FIELD(x - 1, y - 2) ||
6197 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6198 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6200 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6201 (!IN_LEV_FIELD(x + 1, y - 2) ||
6202 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6203 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6205 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6208 static void InitBeltMovement()
6210 static int belt_base_element[4] =
6212 EL_CONVEYOR_BELT_1_LEFT,
6213 EL_CONVEYOR_BELT_2_LEFT,
6214 EL_CONVEYOR_BELT_3_LEFT,
6215 EL_CONVEYOR_BELT_4_LEFT
6217 static int belt_base_active_element[4] =
6219 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6220 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6221 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6222 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6227 /* set frame order for belt animation graphic according to belt direction */
6228 for (i = 0; i < NUM_BELTS; i++)
6232 for (j = 0; j < NUM_BELT_PARTS; j++)
6234 int element = belt_base_active_element[belt_nr] + j;
6235 int graphic_1 = el2img(element);
6236 int graphic_2 = el2panelimg(element);
6238 if (game.belt_dir[i] == MV_LEFT)
6240 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6241 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6245 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6246 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6251 SCAN_PLAYFIELD(x, y)
6253 int element = Feld[x][y];
6255 for (i = 0; i < NUM_BELTS; i++)
6257 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6259 int e_belt_nr = getBeltNrFromBeltElement(element);
6262 if (e_belt_nr == belt_nr)
6264 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6266 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6273 static void ToggleBeltSwitch(int x, int y)
6275 static int belt_base_element[4] =
6277 EL_CONVEYOR_BELT_1_LEFT,
6278 EL_CONVEYOR_BELT_2_LEFT,
6279 EL_CONVEYOR_BELT_3_LEFT,
6280 EL_CONVEYOR_BELT_4_LEFT
6282 static int belt_base_active_element[4] =
6284 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6285 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6286 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6287 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6289 static int belt_base_switch_element[4] =
6291 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6292 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6293 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6294 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6296 static int belt_move_dir[4] =
6304 int element = Feld[x][y];
6305 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6306 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6307 int belt_dir = belt_move_dir[belt_dir_nr];
6310 if (!IS_BELT_SWITCH(element))
6313 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6314 game.belt_dir[belt_nr] = belt_dir;
6316 if (belt_dir_nr == 3)
6319 /* set frame order for belt animation graphic according to belt direction */
6320 for (i = 0; i < NUM_BELT_PARTS; i++)
6322 int element = belt_base_active_element[belt_nr] + i;
6323 int graphic_1 = el2img(element);
6324 int graphic_2 = el2panelimg(element);
6326 if (belt_dir == MV_LEFT)
6328 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6329 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6333 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6334 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6338 SCAN_PLAYFIELD(xx, yy)
6340 int element = Feld[xx][yy];
6342 if (IS_BELT_SWITCH(element))
6344 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6346 if (e_belt_nr == belt_nr)
6348 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6349 TEST_DrawLevelField(xx, yy);
6352 else if (IS_BELT(element) && belt_dir != MV_NONE)
6354 int e_belt_nr = getBeltNrFromBeltElement(element);
6356 if (e_belt_nr == belt_nr)
6358 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6360 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6361 TEST_DrawLevelField(xx, yy);
6364 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6366 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6368 if (e_belt_nr == belt_nr)
6370 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6372 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6373 TEST_DrawLevelField(xx, yy);
6379 static void ToggleSwitchgateSwitch(int x, int y)
6383 game.switchgate_pos = !game.switchgate_pos;
6385 SCAN_PLAYFIELD(xx, yy)
6387 int element = Feld[xx][yy];
6389 #if !USE_BOTH_SWITCHGATE_SWITCHES
6390 if (element == EL_SWITCHGATE_SWITCH_UP ||
6391 element == EL_SWITCHGATE_SWITCH_DOWN)
6393 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6394 TEST_DrawLevelField(xx, yy);
6396 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6397 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6399 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6400 TEST_DrawLevelField(xx, yy);
6403 if (element == EL_SWITCHGATE_SWITCH_UP)
6405 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6406 TEST_DrawLevelField(xx, yy);
6408 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6410 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6411 TEST_DrawLevelField(xx, yy);
6413 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6415 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6416 TEST_DrawLevelField(xx, yy);
6418 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6420 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6421 TEST_DrawLevelField(xx, yy);
6424 else if (element == EL_SWITCHGATE_OPEN ||
6425 element == EL_SWITCHGATE_OPENING)
6427 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6429 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6431 else if (element == EL_SWITCHGATE_CLOSED ||
6432 element == EL_SWITCHGATE_CLOSING)
6434 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6436 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6441 static int getInvisibleActiveFromInvisibleElement(int element)
6443 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6444 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6445 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6449 static int getInvisibleFromInvisibleActiveElement(int element)
6451 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6452 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6453 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6457 static void RedrawAllLightSwitchesAndInvisibleElements()
6461 SCAN_PLAYFIELD(x, y)
6463 int element = Feld[x][y];
6465 if (element == EL_LIGHT_SWITCH &&
6466 game.light_time_left > 0)
6468 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6469 TEST_DrawLevelField(x, y);
6471 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6472 game.light_time_left == 0)
6474 Feld[x][y] = EL_LIGHT_SWITCH;
6475 TEST_DrawLevelField(x, y);
6477 else if (element == EL_EMC_DRIPPER &&
6478 game.light_time_left > 0)
6480 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6481 TEST_DrawLevelField(x, y);
6483 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6484 game.light_time_left == 0)
6486 Feld[x][y] = EL_EMC_DRIPPER;
6487 TEST_DrawLevelField(x, y);
6489 else if (element == EL_INVISIBLE_STEELWALL ||
6490 element == EL_INVISIBLE_WALL ||
6491 element == EL_INVISIBLE_SAND)
6493 if (game.light_time_left > 0)
6494 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6496 TEST_DrawLevelField(x, y);
6498 /* uncrumble neighbour fields, if needed */
6499 if (element == EL_INVISIBLE_SAND)
6500 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6502 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6503 element == EL_INVISIBLE_WALL_ACTIVE ||
6504 element == EL_INVISIBLE_SAND_ACTIVE)
6506 if (game.light_time_left == 0)
6507 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6509 TEST_DrawLevelField(x, y);
6511 /* re-crumble neighbour fields, if needed */
6512 if (element == EL_INVISIBLE_SAND)
6513 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6518 static void RedrawAllInvisibleElementsForLenses()
6522 SCAN_PLAYFIELD(x, y)
6524 int element = Feld[x][y];
6526 if (element == EL_EMC_DRIPPER &&
6527 game.lenses_time_left > 0)
6529 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6530 TEST_DrawLevelField(x, y);
6532 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6533 game.lenses_time_left == 0)
6535 Feld[x][y] = EL_EMC_DRIPPER;
6536 TEST_DrawLevelField(x, y);
6538 else if (element == EL_INVISIBLE_STEELWALL ||
6539 element == EL_INVISIBLE_WALL ||
6540 element == EL_INVISIBLE_SAND)
6542 if (game.lenses_time_left > 0)
6543 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6545 TEST_DrawLevelField(x, y);
6547 /* uncrumble neighbour fields, if needed */
6548 if (element == EL_INVISIBLE_SAND)
6549 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6551 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6552 element == EL_INVISIBLE_WALL_ACTIVE ||
6553 element == EL_INVISIBLE_SAND_ACTIVE)
6555 if (game.lenses_time_left == 0)
6556 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6558 TEST_DrawLevelField(x, y);
6560 /* re-crumble neighbour fields, if needed */
6561 if (element == EL_INVISIBLE_SAND)
6562 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6567 static void RedrawAllInvisibleElementsForMagnifier()
6571 SCAN_PLAYFIELD(x, y)
6573 int element = Feld[x][y];
6575 if (element == EL_EMC_FAKE_GRASS &&
6576 game.magnify_time_left > 0)
6578 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6579 TEST_DrawLevelField(x, y);
6581 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6582 game.magnify_time_left == 0)
6584 Feld[x][y] = EL_EMC_FAKE_GRASS;
6585 TEST_DrawLevelField(x, y);
6587 else if (IS_GATE_GRAY(element) &&
6588 game.magnify_time_left > 0)
6590 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6591 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6592 IS_EM_GATE_GRAY(element) ?
6593 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6594 IS_EMC_GATE_GRAY(element) ?
6595 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6596 IS_DC_GATE_GRAY(element) ?
6597 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6599 TEST_DrawLevelField(x, y);
6601 else if (IS_GATE_GRAY_ACTIVE(element) &&
6602 game.magnify_time_left == 0)
6604 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6605 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6606 IS_EM_GATE_GRAY_ACTIVE(element) ?
6607 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6608 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6609 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6610 IS_DC_GATE_GRAY_ACTIVE(element) ?
6611 EL_DC_GATE_WHITE_GRAY :
6613 TEST_DrawLevelField(x, y);
6618 static void ToggleLightSwitch(int x, int y)
6620 int element = Feld[x][y];
6622 game.light_time_left =
6623 (element == EL_LIGHT_SWITCH ?
6624 level.time_light * FRAMES_PER_SECOND : 0);
6626 RedrawAllLightSwitchesAndInvisibleElements();
6629 static void ActivateTimegateSwitch(int x, int y)
6633 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6635 SCAN_PLAYFIELD(xx, yy)
6637 int element = Feld[xx][yy];
6639 if (element == EL_TIMEGATE_CLOSED ||
6640 element == EL_TIMEGATE_CLOSING)
6642 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6643 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6647 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6649 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6650 TEST_DrawLevelField(xx, yy);
6657 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6658 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6660 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6664 void Impact(int x, int y)
6666 boolean last_line = (y == lev_fieldy - 1);
6667 boolean object_hit = FALSE;
6668 boolean impact = (last_line || object_hit);
6669 int element = Feld[x][y];
6670 int smashed = EL_STEELWALL;
6672 if (!last_line) /* check if element below was hit */
6674 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6677 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6678 MovDir[x][y + 1] != MV_DOWN ||
6679 MovPos[x][y + 1] <= TILEY / 2));
6681 /* do not smash moving elements that left the smashed field in time */
6682 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6683 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6686 #if USE_QUICKSAND_IMPACT_BUGFIX
6687 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6689 RemoveMovingField(x, y + 1);
6690 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6691 Feld[x][y + 2] = EL_ROCK;
6692 TEST_DrawLevelField(x, y + 2);
6697 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6699 RemoveMovingField(x, y + 1);
6700 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6701 Feld[x][y + 2] = EL_ROCK;
6702 TEST_DrawLevelField(x, y + 2);
6709 smashed = MovingOrBlocked2Element(x, y + 1);
6711 impact = (last_line || object_hit);
6714 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6716 SplashAcid(x, y + 1);
6720 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6721 /* only reset graphic animation if graphic really changes after impact */
6723 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6725 ResetGfxAnimation(x, y);
6726 TEST_DrawLevelField(x, y);
6729 if (impact && CAN_EXPLODE_IMPACT(element))
6734 else if (impact && element == EL_PEARL &&
6735 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6737 ResetGfxAnimation(x, y);
6739 Feld[x][y] = EL_PEARL_BREAKING;
6740 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6743 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6745 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6750 if (impact && element == EL_AMOEBA_DROP)
6752 if (object_hit && IS_PLAYER(x, y + 1))
6753 KillPlayerUnlessEnemyProtected(x, y + 1);
6754 else if (object_hit && smashed == EL_PENGUIN)
6758 Feld[x][y] = EL_AMOEBA_GROWING;
6759 Store[x][y] = EL_AMOEBA_WET;
6761 ResetRandomAnimationValue(x, y);
6766 if (object_hit) /* check which object was hit */
6768 if ((CAN_PASS_MAGIC_WALL(element) &&
6769 (smashed == EL_MAGIC_WALL ||
6770 smashed == EL_BD_MAGIC_WALL)) ||
6771 (CAN_PASS_DC_MAGIC_WALL(element) &&
6772 smashed == EL_DC_MAGIC_WALL))
6775 int activated_magic_wall =
6776 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6777 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6778 EL_DC_MAGIC_WALL_ACTIVE);
6780 /* activate magic wall / mill */
6781 SCAN_PLAYFIELD(xx, yy)
6783 if (Feld[xx][yy] == smashed)
6784 Feld[xx][yy] = activated_magic_wall;
6787 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6788 game.magic_wall_active = TRUE;
6790 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6791 SND_MAGIC_WALL_ACTIVATING :
6792 smashed == EL_BD_MAGIC_WALL ?
6793 SND_BD_MAGIC_WALL_ACTIVATING :
6794 SND_DC_MAGIC_WALL_ACTIVATING));
6797 if (IS_PLAYER(x, y + 1))
6799 if (CAN_SMASH_PLAYER(element))
6801 KillPlayerUnlessEnemyProtected(x, y + 1);
6805 else if (smashed == EL_PENGUIN)
6807 if (CAN_SMASH_PLAYER(element))
6813 else if (element == EL_BD_DIAMOND)
6815 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6821 else if (((element == EL_SP_INFOTRON ||
6822 element == EL_SP_ZONK) &&
6823 (smashed == EL_SP_SNIKSNAK ||
6824 smashed == EL_SP_ELECTRON ||
6825 smashed == EL_SP_DISK_ORANGE)) ||
6826 (element == EL_SP_INFOTRON &&
6827 smashed == EL_SP_DISK_YELLOW))
6832 else if (CAN_SMASH_EVERYTHING(element))
6834 if (IS_CLASSIC_ENEMY(smashed) ||
6835 CAN_EXPLODE_SMASHED(smashed))
6840 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6842 if (smashed == EL_LAMP ||
6843 smashed == EL_LAMP_ACTIVE)
6848 else if (smashed == EL_NUT)
6850 Feld[x][y + 1] = EL_NUT_BREAKING;
6851 PlayLevelSound(x, y, SND_NUT_BREAKING);
6852 RaiseScoreElement(EL_NUT);
6855 else if (smashed == EL_PEARL)
6857 ResetGfxAnimation(x, y);
6859 Feld[x][y + 1] = EL_PEARL_BREAKING;
6860 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6863 else if (smashed == EL_DIAMOND)
6865 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6866 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6869 else if (IS_BELT_SWITCH(smashed))
6871 ToggleBeltSwitch(x, y + 1);
6873 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6874 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6875 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6876 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6878 ToggleSwitchgateSwitch(x, y + 1);
6880 else if (smashed == EL_LIGHT_SWITCH ||
6881 smashed == EL_LIGHT_SWITCH_ACTIVE)
6883 ToggleLightSwitch(x, y + 1);
6888 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6891 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6893 CheckElementChangeBySide(x, y + 1, smashed, element,
6894 CE_SWITCHED, CH_SIDE_TOP);
6895 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6901 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6906 /* play sound of magic wall / mill */
6908 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6909 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6910 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6912 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6913 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6914 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6915 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6916 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6917 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6922 /* play sound of object that hits the ground */
6923 if (last_line || object_hit)
6924 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6927 inline static void TurnRoundExt(int x, int y)
6939 { 0, 0 }, { 0, 0 }, { 0, 0 },
6944 int left, right, back;
6948 { MV_DOWN, MV_UP, MV_RIGHT },
6949 { MV_UP, MV_DOWN, MV_LEFT },
6951 { MV_LEFT, MV_RIGHT, MV_DOWN },
6955 { MV_RIGHT, MV_LEFT, MV_UP }
6958 int element = Feld[x][y];
6959 int move_pattern = element_info[element].move_pattern;
6961 int old_move_dir = MovDir[x][y];
6962 int left_dir = turn[old_move_dir].left;
6963 int right_dir = turn[old_move_dir].right;
6964 int back_dir = turn[old_move_dir].back;
6966 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6967 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6968 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6969 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6971 int left_x = x + left_dx, left_y = y + left_dy;
6972 int right_x = x + right_dx, right_y = y + right_dy;
6973 int move_x = x + move_dx, move_y = y + move_dy;
6977 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6979 TestIfBadThingTouchesOtherBadThing(x, y);
6981 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6982 MovDir[x][y] = right_dir;
6983 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6984 MovDir[x][y] = left_dir;
6986 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6988 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6991 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6993 TestIfBadThingTouchesOtherBadThing(x, y);
6995 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6996 MovDir[x][y] = left_dir;
6997 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998 MovDir[x][y] = right_dir;
7000 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7002 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7005 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7007 TestIfBadThingTouchesOtherBadThing(x, y);
7009 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7010 MovDir[x][y] = left_dir;
7011 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7012 MovDir[x][y] = right_dir;
7014 if (MovDir[x][y] != old_move_dir)
7017 else if (element == EL_YAMYAM)
7019 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7020 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7022 if (can_turn_left && can_turn_right)
7023 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024 else if (can_turn_left)
7025 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026 else if (can_turn_right)
7027 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7029 MovDir[x][y] = back_dir;
7031 MovDelay[x][y] = 16 + 16 * RND(3);
7033 else if (element == EL_DARK_YAMYAM)
7035 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7037 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7040 if (can_turn_left && can_turn_right)
7041 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7042 else if (can_turn_left)
7043 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7044 else if (can_turn_right)
7045 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7047 MovDir[x][y] = back_dir;
7049 MovDelay[x][y] = 16 + 16 * RND(3);
7051 else if (element == EL_PACMAN)
7053 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7054 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7056 if (can_turn_left && can_turn_right)
7057 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7058 else if (can_turn_left)
7059 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7060 else if (can_turn_right)
7061 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7063 MovDir[x][y] = back_dir;
7065 MovDelay[x][y] = 6 + RND(40);
7067 else if (element == EL_PIG)
7069 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7070 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7071 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7072 boolean should_turn_left, should_turn_right, should_move_on;
7074 int rnd = RND(rnd_value);
7076 should_turn_left = (can_turn_left &&
7078 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7079 y + back_dy + left_dy)));
7080 should_turn_right = (can_turn_right &&
7082 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7083 y + back_dy + right_dy)));
7084 should_move_on = (can_move_on &&
7087 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7088 y + move_dy + left_dy) ||
7089 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7090 y + move_dy + right_dy)));
7092 if (should_turn_left || should_turn_right || should_move_on)
7094 if (should_turn_left && should_turn_right && should_move_on)
7095 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7096 rnd < 2 * rnd_value / 3 ? right_dir :
7098 else if (should_turn_left && should_turn_right)
7099 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7100 else if (should_turn_left && should_move_on)
7101 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7102 else if (should_turn_right && should_move_on)
7103 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7104 else if (should_turn_left)
7105 MovDir[x][y] = left_dir;
7106 else if (should_turn_right)
7107 MovDir[x][y] = right_dir;
7108 else if (should_move_on)
7109 MovDir[x][y] = old_move_dir;
7111 else if (can_move_on && rnd > rnd_value / 8)
7112 MovDir[x][y] = old_move_dir;
7113 else if (can_turn_left && can_turn_right)
7114 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115 else if (can_turn_left && rnd > rnd_value / 8)
7116 MovDir[x][y] = left_dir;
7117 else if (can_turn_right && rnd > rnd_value/8)
7118 MovDir[x][y] = right_dir;
7120 MovDir[x][y] = back_dir;
7122 xx = x + move_xy[MovDir[x][y]].dx;
7123 yy = y + move_xy[MovDir[x][y]].dy;
7125 if (!IN_LEV_FIELD(xx, yy) ||
7126 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7127 MovDir[x][y] = old_move_dir;
7131 else if (element == EL_DRAGON)
7133 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7134 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7135 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7137 int rnd = RND(rnd_value);
7139 if (can_move_on && rnd > rnd_value / 8)
7140 MovDir[x][y] = old_move_dir;
7141 else if (can_turn_left && can_turn_right)
7142 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143 else if (can_turn_left && rnd > rnd_value / 8)
7144 MovDir[x][y] = left_dir;
7145 else if (can_turn_right && rnd > rnd_value / 8)
7146 MovDir[x][y] = right_dir;
7148 MovDir[x][y] = back_dir;
7150 xx = x + move_xy[MovDir[x][y]].dx;
7151 yy = y + move_xy[MovDir[x][y]].dy;
7153 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7154 MovDir[x][y] = old_move_dir;
7158 else if (element == EL_MOLE)
7160 boolean can_move_on =
7161 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7162 IS_AMOEBOID(Feld[move_x][move_y]) ||
7163 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7166 boolean can_turn_left =
7167 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7168 IS_AMOEBOID(Feld[left_x][left_y])));
7170 boolean can_turn_right =
7171 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7172 IS_AMOEBOID(Feld[right_x][right_y])));
7174 if (can_turn_left && can_turn_right)
7175 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7176 else if (can_turn_left)
7177 MovDir[x][y] = left_dir;
7179 MovDir[x][y] = right_dir;
7182 if (MovDir[x][y] != old_move_dir)
7185 else if (element == EL_BALLOON)
7187 MovDir[x][y] = game.wind_direction;
7190 else if (element == EL_SPRING)
7192 #if USE_NEW_SPRING_BUMPER
7193 if (MovDir[x][y] & MV_HORIZONTAL)
7195 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7196 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7198 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7199 ResetGfxAnimation(move_x, move_y);
7200 TEST_DrawLevelField(move_x, move_y);
7202 MovDir[x][y] = back_dir;
7204 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7205 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7206 MovDir[x][y] = MV_NONE;
7209 if (MovDir[x][y] & MV_HORIZONTAL &&
7210 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7211 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7212 MovDir[x][y] = MV_NONE;
7217 else if (element == EL_ROBOT ||
7218 element == EL_SATELLITE ||
7219 element == EL_PENGUIN ||
7220 element == EL_EMC_ANDROID)
7222 int attr_x = -1, attr_y = -1;
7233 for (i = 0; i < MAX_PLAYERS; i++)
7235 struct PlayerInfo *player = &stored_player[i];
7236 int jx = player->jx, jy = player->jy;
7238 if (!player->active)
7242 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7250 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7251 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7252 game.engine_version < VERSION_IDENT(3,1,0,0)))
7258 if (element == EL_PENGUIN)
7261 static int xy[4][2] =
7269 for (i = 0; i < NUM_DIRECTIONS; i++)
7271 int ex = x + xy[i][0];
7272 int ey = y + xy[i][1];
7274 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7275 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7276 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7277 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7286 MovDir[x][y] = MV_NONE;
7288 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7289 else if (attr_x > x)
7290 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7292 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7293 else if (attr_y > y)
7294 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7296 if (element == EL_ROBOT)
7300 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7301 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7302 Moving2Blocked(x, y, &newx, &newy);
7304 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7305 MovDelay[x][y] = 8 + 8 * !RND(3);
7307 MovDelay[x][y] = 16;
7309 else if (element == EL_PENGUIN)
7315 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7317 boolean first_horiz = RND(2);
7318 int new_move_dir = MovDir[x][y];
7321 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7322 Moving2Blocked(x, y, &newx, &newy);
7324 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7328 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7329 Moving2Blocked(x, y, &newx, &newy);
7331 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7334 MovDir[x][y] = old_move_dir;
7338 else if (element == EL_SATELLITE)
7344 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7346 boolean first_horiz = RND(2);
7347 int new_move_dir = MovDir[x][y];
7350 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7351 Moving2Blocked(x, y, &newx, &newy);
7353 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7357 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358 Moving2Blocked(x, y, &newx, &newy);
7360 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7363 MovDir[x][y] = old_move_dir;
7367 else if (element == EL_EMC_ANDROID)
7369 static int check_pos[16] =
7371 -1, /* 0 => (invalid) */
7372 7, /* 1 => MV_LEFT */
7373 3, /* 2 => MV_RIGHT */
7374 -1, /* 3 => (invalid) */
7376 0, /* 5 => MV_LEFT | MV_UP */
7377 2, /* 6 => MV_RIGHT | MV_UP */
7378 -1, /* 7 => (invalid) */
7379 5, /* 8 => MV_DOWN */
7380 6, /* 9 => MV_LEFT | MV_DOWN */
7381 4, /* 10 => MV_RIGHT | MV_DOWN */
7382 -1, /* 11 => (invalid) */
7383 -1, /* 12 => (invalid) */
7384 -1, /* 13 => (invalid) */
7385 -1, /* 14 => (invalid) */
7386 -1, /* 15 => (invalid) */
7394 { -1, -1, MV_LEFT | MV_UP },
7396 { +1, -1, MV_RIGHT | MV_UP },
7397 { +1, 0, MV_RIGHT },
7398 { +1, +1, MV_RIGHT | MV_DOWN },
7400 { -1, +1, MV_LEFT | MV_DOWN },
7403 int start_pos, check_order;
7404 boolean can_clone = FALSE;
7407 /* check if there is any free field around current position */
7408 for (i = 0; i < 8; i++)
7410 int newx = x + check_xy[i].dx;
7411 int newy = y + check_xy[i].dy;
7413 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7421 if (can_clone) /* randomly find an element to clone */
7425 start_pos = check_pos[RND(8)];
7426 check_order = (RND(2) ? -1 : +1);
7428 for (i = 0; i < 8; i++)
7430 int pos_raw = start_pos + i * check_order;
7431 int pos = (pos_raw + 8) % 8;
7432 int newx = x + check_xy[pos].dx;
7433 int newy = y + check_xy[pos].dy;
7435 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7437 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7438 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7440 Store[x][y] = Feld[newx][newy];
7449 if (can_clone) /* randomly find a direction to move */
7453 start_pos = check_pos[RND(8)];
7454 check_order = (RND(2) ? -1 : +1);
7456 for (i = 0; i < 8; i++)
7458 int pos_raw = start_pos + i * check_order;
7459 int pos = (pos_raw + 8) % 8;
7460 int newx = x + check_xy[pos].dx;
7461 int newy = y + check_xy[pos].dy;
7462 int new_move_dir = check_xy[pos].dir;
7464 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7466 MovDir[x][y] = new_move_dir;
7467 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7476 if (can_clone) /* cloning and moving successful */
7479 /* cannot clone -- try to move towards player */
7481 start_pos = check_pos[MovDir[x][y] & 0x0f];
7482 check_order = (RND(2) ? -1 : +1);
7484 for (i = 0; i < 3; i++)
7486 /* first check start_pos, then previous/next or (next/previous) pos */
7487 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7488 int pos = (pos_raw + 8) % 8;
7489 int newx = x + check_xy[pos].dx;
7490 int newy = y + check_xy[pos].dy;
7491 int new_move_dir = check_xy[pos].dir;
7493 if (IS_PLAYER(newx, newy))
7496 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7498 MovDir[x][y] = new_move_dir;
7499 MovDelay[x][y] = level.android_move_time * 8 + 1;
7506 else if (move_pattern == MV_TURNING_LEFT ||
7507 move_pattern == MV_TURNING_RIGHT ||
7508 move_pattern == MV_TURNING_LEFT_RIGHT ||
7509 move_pattern == MV_TURNING_RIGHT_LEFT ||
7510 move_pattern == MV_TURNING_RANDOM ||
7511 move_pattern == MV_ALL_DIRECTIONS)
7513 boolean can_turn_left =
7514 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7515 boolean can_turn_right =
7516 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7518 if (element_info[element].move_stepsize == 0) /* "not moving" */
7521 if (move_pattern == MV_TURNING_LEFT)
7522 MovDir[x][y] = left_dir;
7523 else if (move_pattern == MV_TURNING_RIGHT)
7524 MovDir[x][y] = right_dir;
7525 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7526 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7527 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7528 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7529 else if (move_pattern == MV_TURNING_RANDOM)
7530 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7531 can_turn_right && !can_turn_left ? right_dir :
7532 RND(2) ? left_dir : right_dir);
7533 else if (can_turn_left && can_turn_right)
7534 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7535 else if (can_turn_left)
7536 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7537 else if (can_turn_right)
7538 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7540 MovDir[x][y] = back_dir;
7542 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7544 else if (move_pattern == MV_HORIZONTAL ||
7545 move_pattern == MV_VERTICAL)
7547 if (move_pattern & old_move_dir)
7548 MovDir[x][y] = back_dir;
7549 else if (move_pattern == MV_HORIZONTAL)
7550 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7551 else if (move_pattern == MV_VERTICAL)
7552 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7554 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7556 else if (move_pattern & MV_ANY_DIRECTION)
7558 MovDir[x][y] = move_pattern;
7559 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7561 else if (move_pattern & MV_WIND_DIRECTION)
7563 MovDir[x][y] = game.wind_direction;
7564 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7566 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7568 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7569 MovDir[x][y] = left_dir;
7570 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7571 MovDir[x][y] = right_dir;
7573 if (MovDir[x][y] != old_move_dir)
7574 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7576 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7578 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7579 MovDir[x][y] = right_dir;
7580 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7581 MovDir[x][y] = left_dir;
7583 if (MovDir[x][y] != old_move_dir)
7584 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7586 else if (move_pattern == MV_TOWARDS_PLAYER ||
7587 move_pattern == MV_AWAY_FROM_PLAYER)
7589 int attr_x = -1, attr_y = -1;
7591 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7602 for (i = 0; i < MAX_PLAYERS; i++)
7604 struct PlayerInfo *player = &stored_player[i];
7605 int jx = player->jx, jy = player->jy;
7607 if (!player->active)
7611 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7619 MovDir[x][y] = MV_NONE;
7621 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7622 else if (attr_x > x)
7623 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7625 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7626 else if (attr_y > y)
7627 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7629 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7631 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7633 boolean first_horiz = RND(2);
7634 int new_move_dir = MovDir[x][y];
7636 if (element_info[element].move_stepsize == 0) /* "not moving" */
7638 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7639 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7645 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7646 Moving2Blocked(x, y, &newx, &newy);
7648 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7652 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7653 Moving2Blocked(x, y, &newx, &newy);
7655 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7658 MovDir[x][y] = old_move_dir;
7661 else if (move_pattern == MV_WHEN_PUSHED ||
7662 move_pattern == MV_WHEN_DROPPED)
7664 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7665 MovDir[x][y] = MV_NONE;
7669 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7671 static int test_xy[7][2] =
7681 static int test_dir[7] =
7691 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7692 int move_preference = -1000000; /* start with very low preference */
7693 int new_move_dir = MV_NONE;
7694 int start_test = RND(4);
7697 for (i = 0; i < NUM_DIRECTIONS; i++)
7699 int move_dir = test_dir[start_test + i];
7700 int move_dir_preference;
7702 xx = x + test_xy[start_test + i][0];
7703 yy = y + test_xy[start_test + i][1];
7705 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7706 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7708 new_move_dir = move_dir;
7713 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7716 move_dir_preference = -1 * RunnerVisit[xx][yy];
7717 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7718 move_dir_preference = PlayerVisit[xx][yy];
7720 if (move_dir_preference > move_preference)
7722 /* prefer field that has not been visited for the longest time */
7723 move_preference = move_dir_preference;
7724 new_move_dir = move_dir;
7726 else if (move_dir_preference == move_preference &&
7727 move_dir == old_move_dir)
7729 /* prefer last direction when all directions are preferred equally */
7730 move_preference = move_dir_preference;
7731 new_move_dir = move_dir;
7735 MovDir[x][y] = new_move_dir;
7736 if (old_move_dir != new_move_dir)
7737 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7741 static void TurnRound(int x, int y)
7743 int direction = MovDir[x][y];
7747 GfxDir[x][y] = MovDir[x][y];
7749 if (direction != MovDir[x][y])
7753 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7755 ResetGfxFrame(x, y, FALSE);
7758 static boolean JustBeingPushed(int x, int y)
7762 for (i = 0; i < MAX_PLAYERS; i++)
7764 struct PlayerInfo *player = &stored_player[i];
7766 if (player->active && player->is_pushing && player->MovPos)
7768 int next_jx = player->jx + (player->jx - player->last_jx);
7769 int next_jy = player->jy + (player->jy - player->last_jy);
7771 if (x == next_jx && y == next_jy)
7779 void StartMoving(int x, int y)
7781 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7782 int element = Feld[x][y];
7787 if (MovDelay[x][y] == 0)
7788 GfxAction[x][y] = ACTION_DEFAULT;
7790 if (CAN_FALL(element) && y < lev_fieldy - 1)
7792 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7793 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7794 if (JustBeingPushed(x, y))
7797 if (element == EL_QUICKSAND_FULL)
7799 if (IS_FREE(x, y + 1))
7801 InitMovingField(x, y, MV_DOWN);
7802 started_moving = TRUE;
7804 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7805 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7806 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7807 Store[x][y] = EL_ROCK;
7809 Store[x][y] = EL_ROCK;
7812 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7814 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7816 if (!MovDelay[x][y])
7818 MovDelay[x][y] = TILEY + 1;
7820 ResetGfxAnimation(x, y);
7821 ResetGfxAnimation(x, y + 1);
7826 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7827 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7834 Feld[x][y] = EL_QUICKSAND_EMPTY;
7835 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7836 Store[x][y + 1] = Store[x][y];
7839 PlayLevelSoundAction(x, y, ACTION_FILLING);
7841 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7843 if (!MovDelay[x][y])
7845 MovDelay[x][y] = TILEY + 1;
7847 ResetGfxAnimation(x, y);
7848 ResetGfxAnimation(x, y + 1);
7853 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7854 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7861 Feld[x][y] = EL_QUICKSAND_EMPTY;
7862 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7863 Store[x][y + 1] = Store[x][y];
7866 PlayLevelSoundAction(x, y, ACTION_FILLING);
7869 else if (element == EL_QUICKSAND_FAST_FULL)
7871 if (IS_FREE(x, y + 1))
7873 InitMovingField(x, y, MV_DOWN);
7874 started_moving = TRUE;
7876 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7877 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7878 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7879 Store[x][y] = EL_ROCK;
7881 Store[x][y] = EL_ROCK;
7884 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7886 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7888 if (!MovDelay[x][y])
7890 MovDelay[x][y] = TILEY + 1;
7892 ResetGfxAnimation(x, y);
7893 ResetGfxAnimation(x, y + 1);
7898 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7899 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7906 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7907 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908 Store[x][y + 1] = Store[x][y];
7911 PlayLevelSoundAction(x, y, ACTION_FILLING);
7913 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7915 if (!MovDelay[x][y])
7917 MovDelay[x][y] = TILEY + 1;
7919 ResetGfxAnimation(x, y);
7920 ResetGfxAnimation(x, y + 1);
7925 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7926 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7933 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7934 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7935 Store[x][y + 1] = Store[x][y];
7938 PlayLevelSoundAction(x, y, ACTION_FILLING);
7941 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7942 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7944 InitMovingField(x, y, MV_DOWN);
7945 started_moving = TRUE;
7947 Feld[x][y] = EL_QUICKSAND_FILLING;
7948 Store[x][y] = element;
7950 PlayLevelSoundAction(x, y, ACTION_FILLING);
7952 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7953 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7955 InitMovingField(x, y, MV_DOWN);
7956 started_moving = TRUE;
7958 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7959 Store[x][y] = element;
7961 PlayLevelSoundAction(x, y, ACTION_FILLING);
7963 else if (element == EL_MAGIC_WALL_FULL)
7965 if (IS_FREE(x, y + 1))
7967 InitMovingField(x, y, MV_DOWN);
7968 started_moving = TRUE;
7970 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7971 Store[x][y] = EL_CHANGED(Store[x][y]);
7973 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7975 if (!MovDelay[x][y])
7976 MovDelay[x][y] = TILEY / 4 + 1;
7985 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7986 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7987 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7991 else if (element == EL_BD_MAGIC_WALL_FULL)
7993 if (IS_FREE(x, y + 1))
7995 InitMovingField(x, y, MV_DOWN);
7996 started_moving = TRUE;
7998 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7999 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8001 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8003 if (!MovDelay[x][y])
8004 MovDelay[x][y] = TILEY / 4 + 1;
8013 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8014 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8015 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8019 else if (element == EL_DC_MAGIC_WALL_FULL)
8021 if (IS_FREE(x, y + 1))
8023 InitMovingField(x, y, MV_DOWN);
8024 started_moving = TRUE;
8026 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8027 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8029 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8031 if (!MovDelay[x][y])
8032 MovDelay[x][y] = TILEY / 4 + 1;
8041 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8042 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8043 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8047 else if ((CAN_PASS_MAGIC_WALL(element) &&
8048 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8049 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8050 (CAN_PASS_DC_MAGIC_WALL(element) &&
8051 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8054 InitMovingField(x, y, MV_DOWN);
8055 started_moving = TRUE;
8058 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8059 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8060 EL_DC_MAGIC_WALL_FILLING);
8061 Store[x][y] = element;
8063 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8065 SplashAcid(x, y + 1);
8067 InitMovingField(x, y, MV_DOWN);
8068 started_moving = TRUE;
8070 Store[x][y] = EL_ACID;
8073 #if USE_FIX_IMPACT_COLLISION
8074 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8075 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8077 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8078 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8080 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8081 CAN_FALL(element) && WasJustFalling[x][y] &&
8082 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8084 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8085 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8086 (Feld[x][y + 1] == EL_BLOCKED)))
8088 /* this is needed for a special case not covered by calling "Impact()"
8089 from "ContinueMoving()": if an element moves to a tile directly below
8090 another element which was just falling on that tile (which was empty
8091 in the previous frame), the falling element above would just stop
8092 instead of smashing the element below (in previous version, the above
8093 element was just checked for "moving" instead of "falling", resulting
8094 in incorrect smashes caused by horizontal movement of the above
8095 element; also, the case of the player being the element to smash was
8096 simply not covered here... :-/ ) */
8098 CheckCollision[x][y] = 0;
8099 CheckImpact[x][y] = 0;
8103 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8105 if (MovDir[x][y] == MV_NONE)
8107 InitMovingField(x, y, MV_DOWN);
8108 started_moving = TRUE;
8111 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8113 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8114 MovDir[x][y] = MV_DOWN;
8116 InitMovingField(x, y, MV_DOWN);
8117 started_moving = TRUE;
8119 else if (element == EL_AMOEBA_DROP)
8121 Feld[x][y] = EL_AMOEBA_GROWING;
8122 Store[x][y] = EL_AMOEBA_WET;
8124 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8125 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8126 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8127 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8129 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8130 (IS_FREE(x - 1, y + 1) ||
8131 Feld[x - 1][y + 1] == EL_ACID));
8132 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8133 (IS_FREE(x + 1, y + 1) ||
8134 Feld[x + 1][y + 1] == EL_ACID));
8135 boolean can_fall_any = (can_fall_left || can_fall_right);
8136 boolean can_fall_both = (can_fall_left && can_fall_right);
8137 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8139 #if USE_NEW_ALL_SLIPPERY
8140 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8142 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8143 can_fall_right = FALSE;
8144 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8145 can_fall_left = FALSE;
8146 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8147 can_fall_right = FALSE;
8148 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8149 can_fall_left = FALSE;
8151 can_fall_any = (can_fall_left || can_fall_right);
8152 can_fall_both = FALSE;
8155 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8157 if (slippery_type == SLIPPERY_ONLY_LEFT)
8158 can_fall_right = FALSE;
8159 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8160 can_fall_left = FALSE;
8161 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8162 can_fall_right = FALSE;
8163 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8164 can_fall_left = FALSE;
8166 can_fall_any = (can_fall_left || can_fall_right);
8167 can_fall_both = (can_fall_left && can_fall_right);
8171 #if USE_NEW_ALL_SLIPPERY
8173 #if USE_NEW_SP_SLIPPERY
8174 /* !!! better use the same properties as for custom elements here !!! */
8175 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8176 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8178 can_fall_right = FALSE; /* slip down on left side */
8179 can_fall_both = FALSE;
8184 #if USE_NEW_ALL_SLIPPERY
8187 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8188 can_fall_right = FALSE; /* slip down on left side */
8190 can_fall_left = !(can_fall_right = RND(2));
8192 can_fall_both = FALSE;
8197 if (game.emulation == EMU_BOULDERDASH ||
8198 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8199 can_fall_right = FALSE; /* slip down on left side */
8201 can_fall_left = !(can_fall_right = RND(2));
8203 can_fall_both = FALSE;
8209 /* if not determined otherwise, prefer left side for slipping down */
8210 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8211 started_moving = TRUE;
8215 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8217 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8220 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8221 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8222 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8223 int belt_dir = game.belt_dir[belt_nr];
8225 if ((belt_dir == MV_LEFT && left_is_free) ||
8226 (belt_dir == MV_RIGHT && right_is_free))
8228 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8230 InitMovingField(x, y, belt_dir);
8231 started_moving = TRUE;
8233 Pushed[x][y] = TRUE;
8234 Pushed[nextx][y] = TRUE;
8236 GfxAction[x][y] = ACTION_DEFAULT;
8240 MovDir[x][y] = 0; /* if element was moving, stop it */
8245 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8247 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8249 if (CAN_MOVE(element) && !started_moving)
8252 int move_pattern = element_info[element].move_pattern;
8257 if (MovDir[x][y] == MV_NONE)
8259 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8260 x, y, element, element_info[element].token_name);
8261 printf("StartMoving(): This should never happen!\n");
8266 Moving2Blocked(x, y, &newx, &newy);
8268 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8271 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8272 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8274 WasJustMoving[x][y] = 0;
8275 CheckCollision[x][y] = 0;
8277 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8279 if (Feld[x][y] != element) /* element has changed */
8283 if (!MovDelay[x][y]) /* start new movement phase */
8285 /* all objects that can change their move direction after each step
8286 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8288 if (element != EL_YAMYAM &&
8289 element != EL_DARK_YAMYAM &&
8290 element != EL_PACMAN &&
8291 !(move_pattern & MV_ANY_DIRECTION) &&
8292 move_pattern != MV_TURNING_LEFT &&
8293 move_pattern != MV_TURNING_RIGHT &&
8294 move_pattern != MV_TURNING_LEFT_RIGHT &&
8295 move_pattern != MV_TURNING_RIGHT_LEFT &&
8296 move_pattern != MV_TURNING_RANDOM)
8300 if (MovDelay[x][y] && (element == EL_BUG ||
8301 element == EL_SPACESHIP ||
8302 element == EL_SP_SNIKSNAK ||
8303 element == EL_SP_ELECTRON ||
8304 element == EL_MOLE))
8305 TEST_DrawLevelField(x, y);
8309 if (MovDelay[x][y]) /* wait some time before next movement */
8313 if (element == EL_ROBOT ||
8314 element == EL_YAMYAM ||
8315 element == EL_DARK_YAMYAM)
8317 DrawLevelElementAnimationIfNeeded(x, y, element);
8318 PlayLevelSoundAction(x, y, ACTION_WAITING);
8320 else if (element == EL_SP_ELECTRON)
8321 DrawLevelElementAnimationIfNeeded(x, y, element);
8322 else if (element == EL_DRAGON)
8325 int dir = MovDir[x][y];
8326 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8327 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8328 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8329 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8330 dir == MV_UP ? IMG_FLAMES_1_UP :
8331 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8332 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8334 GfxAction[x][y] = ACTION_ATTACKING;
8336 if (IS_PLAYER(x, y))
8337 DrawPlayerField(x, y);
8339 TEST_DrawLevelField(x, y);
8341 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8343 for (i = 1; i <= 3; i++)
8345 int xx = x + i * dx;
8346 int yy = y + i * dy;
8347 int sx = SCREENX(xx);
8348 int sy = SCREENY(yy);
8349 int flame_graphic = graphic + (i - 1);
8351 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8356 int flamed = MovingOrBlocked2Element(xx, yy);
8360 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8362 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8363 RemoveMovingField(xx, yy);
8365 RemoveField(xx, yy);
8367 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8370 RemoveMovingField(xx, yy);
8373 ChangeDelay[xx][yy] = 0;
8375 Feld[xx][yy] = EL_FLAMES;
8377 if (IN_SCR_FIELD(sx, sy))
8379 TEST_DrawLevelFieldCrumbled(xx, yy);
8380 DrawGraphic(sx, sy, flame_graphic, frame);
8385 if (Feld[xx][yy] == EL_FLAMES)
8386 Feld[xx][yy] = EL_EMPTY;
8387 TEST_DrawLevelField(xx, yy);
8392 if (MovDelay[x][y]) /* element still has to wait some time */
8394 PlayLevelSoundAction(x, y, ACTION_WAITING);
8400 /* now make next step */
8402 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8404 if (DONT_COLLIDE_WITH(element) &&
8405 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8406 !PLAYER_ENEMY_PROTECTED(newx, newy))
8408 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8413 else if (CAN_MOVE_INTO_ACID(element) &&
8414 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8415 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8416 (MovDir[x][y] == MV_DOWN ||
8417 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8419 SplashAcid(newx, newy);
8420 Store[x][y] = EL_ACID;
8422 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8424 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8425 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8426 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8427 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8430 TEST_DrawLevelField(x, y);
8432 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8433 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8434 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8436 local_player->friends_still_needed--;
8437 if (!local_player->friends_still_needed &&
8438 !local_player->GameOver && AllPlayersGone)
8439 PlayerWins(local_player);
8443 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8445 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8446 TEST_DrawLevelField(newx, newy);
8448 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8450 else if (!IS_FREE(newx, newy))
8452 GfxAction[x][y] = ACTION_WAITING;
8454 if (IS_PLAYER(x, y))
8455 DrawPlayerField(x, y);
8457 TEST_DrawLevelField(x, y);
8462 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8464 if (IS_FOOD_PIG(Feld[newx][newy]))
8466 if (IS_MOVING(newx, newy))
8467 RemoveMovingField(newx, newy);
8470 Feld[newx][newy] = EL_EMPTY;
8471 TEST_DrawLevelField(newx, newy);
8474 PlayLevelSound(x, y, SND_PIG_DIGGING);
8476 else if (!IS_FREE(newx, newy))
8478 if (IS_PLAYER(x, y))
8479 DrawPlayerField(x, y);
8481 TEST_DrawLevelField(x, y);
8486 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8488 if (Store[x][y] != EL_EMPTY)
8490 boolean can_clone = FALSE;
8493 /* check if element to clone is still there */
8494 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8496 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8504 /* cannot clone or target field not free anymore -- do not clone */
8505 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8506 Store[x][y] = EL_EMPTY;
8509 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8511 if (IS_MV_DIAGONAL(MovDir[x][y]))
8513 int diagonal_move_dir = MovDir[x][y];
8514 int stored = Store[x][y];
8515 int change_delay = 8;
8518 /* android is moving diagonally */
8520 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8522 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8523 GfxElement[x][y] = EL_EMC_ANDROID;
8524 GfxAction[x][y] = ACTION_SHRINKING;
8525 GfxDir[x][y] = diagonal_move_dir;
8526 ChangeDelay[x][y] = change_delay;
8528 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8531 DrawLevelGraphicAnimation(x, y, graphic);
8532 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8534 if (Feld[newx][newy] == EL_ACID)
8536 SplashAcid(newx, newy);
8541 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8543 Store[newx][newy] = EL_EMC_ANDROID;
8544 GfxElement[newx][newy] = EL_EMC_ANDROID;
8545 GfxAction[newx][newy] = ACTION_GROWING;
8546 GfxDir[newx][newy] = diagonal_move_dir;
8547 ChangeDelay[newx][newy] = change_delay;
8549 graphic = el_act_dir2img(GfxElement[newx][newy],
8550 GfxAction[newx][newy], GfxDir[newx][newy]);
8552 DrawLevelGraphicAnimation(newx, newy, graphic);
8553 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8559 Feld[newx][newy] = EL_EMPTY;
8560 TEST_DrawLevelField(newx, newy);
8562 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8565 else if (!IS_FREE(newx, newy))
8568 if (IS_PLAYER(x, y))
8569 DrawPlayerField(x, y);
8571 TEST_DrawLevelField(x, y);
8577 else if (IS_CUSTOM_ELEMENT(element) &&
8578 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8581 if (!DigFieldByCE(newx, newy, element))
8584 int new_element = Feld[newx][newy];
8586 if (!IS_FREE(newx, newy))
8588 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8589 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8592 /* no element can dig solid indestructible elements */
8593 if (IS_INDESTRUCTIBLE(new_element) &&
8594 !IS_DIGGABLE(new_element) &&
8595 !IS_COLLECTIBLE(new_element))
8598 if (AmoebaNr[newx][newy] &&
8599 (new_element == EL_AMOEBA_FULL ||
8600 new_element == EL_BD_AMOEBA ||
8601 new_element == EL_AMOEBA_GROWING))
8603 AmoebaCnt[AmoebaNr[newx][newy]]--;
8604 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8607 if (IS_MOVING(newx, newy))
8608 RemoveMovingField(newx, newy);
8611 RemoveField(newx, newy);
8612 TEST_DrawLevelField(newx, newy);
8615 /* if digged element was about to explode, prevent the explosion */
8616 ExplodeField[newx][newy] = EX_TYPE_NONE;
8618 PlayLevelSoundAction(x, y, action);
8621 Store[newx][newy] = EL_EMPTY;
8624 /* this makes it possible to leave the removed element again */
8625 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8626 Store[newx][newy] = new_element;
8628 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8630 int move_leave_element = element_info[element].move_leave_element;
8632 /* this makes it possible to leave the removed element again */
8633 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8634 new_element : move_leave_element);
8640 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8642 RunnerVisit[x][y] = FrameCounter;
8643 PlayerVisit[x][y] /= 8; /* expire player visit path */
8646 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8648 if (!IS_FREE(newx, newy))
8650 if (IS_PLAYER(x, y))
8651 DrawPlayerField(x, y);
8653 TEST_DrawLevelField(x, y);
8659 boolean wanna_flame = !RND(10);
8660 int dx = newx - x, dy = newy - y;
8661 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8662 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8663 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8664 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8665 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8666 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8669 IS_CLASSIC_ENEMY(element1) ||
8670 IS_CLASSIC_ENEMY(element2)) &&
8671 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8672 element1 != EL_FLAMES && element2 != EL_FLAMES)
8674 ResetGfxAnimation(x, y);
8675 GfxAction[x][y] = ACTION_ATTACKING;
8677 if (IS_PLAYER(x, y))
8678 DrawPlayerField(x, y);
8680 TEST_DrawLevelField(x, y);
8682 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8684 MovDelay[x][y] = 50;
8688 RemoveField(newx, newy);
8690 Feld[newx][newy] = EL_FLAMES;
8691 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8694 RemoveField(newx1, newy1);
8696 Feld[newx1][newy1] = EL_FLAMES;
8698 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8701 RemoveField(newx2, newy2);
8703 Feld[newx2][newy2] = EL_FLAMES;
8710 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8711 Feld[newx][newy] == EL_DIAMOND)
8713 if (IS_MOVING(newx, newy))
8714 RemoveMovingField(newx, newy);
8717 Feld[newx][newy] = EL_EMPTY;
8718 TEST_DrawLevelField(newx, newy);
8721 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8723 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8724 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8726 if (AmoebaNr[newx][newy])
8728 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8729 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8730 Feld[newx][newy] == EL_BD_AMOEBA)
8731 AmoebaCnt[AmoebaNr[newx][newy]]--;
8736 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8738 RemoveMovingField(newx, newy);
8741 if (IS_MOVING(newx, newy))
8743 RemoveMovingField(newx, newy);
8748 Feld[newx][newy] = EL_EMPTY;
8749 TEST_DrawLevelField(newx, newy);
8752 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8754 else if ((element == EL_PACMAN || element == EL_MOLE)
8755 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8757 if (AmoebaNr[newx][newy])
8759 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8760 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8761 Feld[newx][newy] == EL_BD_AMOEBA)
8762 AmoebaCnt[AmoebaNr[newx][newy]]--;
8765 if (element == EL_MOLE)
8767 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8768 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8770 ResetGfxAnimation(x, y);
8771 GfxAction[x][y] = ACTION_DIGGING;
8772 TEST_DrawLevelField(x, y);
8774 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8776 return; /* wait for shrinking amoeba */
8778 else /* element == EL_PACMAN */
8780 Feld[newx][newy] = EL_EMPTY;
8781 TEST_DrawLevelField(newx, newy);
8782 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8785 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8786 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8787 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8789 /* wait for shrinking amoeba to completely disappear */
8792 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8794 /* object was running against a wall */
8799 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8800 if (move_pattern & MV_ANY_DIRECTION &&
8801 move_pattern == MovDir[x][y])
8803 int blocking_element =
8804 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8806 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8809 element = Feld[x][y]; /* element might have changed */
8813 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8814 DrawLevelElementAnimation(x, y, element);
8816 if (DONT_TOUCH(element))
8817 TestIfBadThingTouchesPlayer(x, y);
8822 InitMovingField(x, y, MovDir[x][y]);
8824 PlayLevelSoundAction(x, y, ACTION_MOVING);
8828 ContinueMoving(x, y);
8831 void ContinueMoving(int x, int y)
8833 int element = Feld[x][y];
8834 struct ElementInfo *ei = &element_info[element];
8835 int direction = MovDir[x][y];
8836 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8837 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8838 int newx = x + dx, newy = y + dy;
8839 int stored = Store[x][y];
8840 int stored_new = Store[newx][newy];
8841 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8842 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8843 boolean last_line = (newy == lev_fieldy - 1);
8845 MovPos[x][y] += getElementMoveStepsize(x, y);
8847 if (pushed_by_player) /* special case: moving object pushed by player */
8848 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8850 if (ABS(MovPos[x][y]) < TILEX)
8853 int ee = Feld[x][y];
8854 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8855 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8857 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8858 x, y, ABS(MovPos[x][y]),
8860 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8863 TEST_DrawLevelField(x, y);
8865 return; /* element is still moving */
8868 /* element reached destination field */
8870 Feld[x][y] = EL_EMPTY;
8871 Feld[newx][newy] = element;
8872 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8874 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8876 element = Feld[newx][newy] = EL_ACID;
8878 else if (element == EL_MOLE)
8880 Feld[x][y] = EL_SAND;
8882 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8884 else if (element == EL_QUICKSAND_FILLING)
8886 element = Feld[newx][newy] = get_next_element(element);
8887 Store[newx][newy] = Store[x][y];
8889 else if (element == EL_QUICKSAND_EMPTYING)
8891 Feld[x][y] = get_next_element(element);
8892 element = Feld[newx][newy] = Store[x][y];
8894 else if (element == EL_QUICKSAND_FAST_FILLING)
8896 element = Feld[newx][newy] = get_next_element(element);
8897 Store[newx][newy] = Store[x][y];
8899 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8901 Feld[x][y] = get_next_element(element);
8902 element = Feld[newx][newy] = Store[x][y];
8904 else if (element == EL_MAGIC_WALL_FILLING)
8906 element = Feld[newx][newy] = get_next_element(element);
8907 if (!game.magic_wall_active)
8908 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8909 Store[newx][newy] = Store[x][y];
8911 else if (element == EL_MAGIC_WALL_EMPTYING)
8913 Feld[x][y] = get_next_element(element);
8914 if (!game.magic_wall_active)
8915 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8916 element = Feld[newx][newy] = Store[x][y];
8918 #if USE_NEW_CUSTOM_VALUE
8919 InitField(newx, newy, FALSE);
8922 else if (element == EL_BD_MAGIC_WALL_FILLING)
8924 element = Feld[newx][newy] = get_next_element(element);
8925 if (!game.magic_wall_active)
8926 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8927 Store[newx][newy] = Store[x][y];
8929 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8931 Feld[x][y] = get_next_element(element);
8932 if (!game.magic_wall_active)
8933 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8934 element = Feld[newx][newy] = Store[x][y];
8936 #if USE_NEW_CUSTOM_VALUE
8937 InitField(newx, newy, FALSE);
8940 else if (element == EL_DC_MAGIC_WALL_FILLING)
8942 element = Feld[newx][newy] = get_next_element(element);
8943 if (!game.magic_wall_active)
8944 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8945 Store[newx][newy] = Store[x][y];
8947 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8949 Feld[x][y] = get_next_element(element);
8950 if (!game.magic_wall_active)
8951 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8952 element = Feld[newx][newy] = Store[x][y];
8954 #if USE_NEW_CUSTOM_VALUE
8955 InitField(newx, newy, FALSE);
8958 else if (element == EL_AMOEBA_DROPPING)
8960 Feld[x][y] = get_next_element(element);
8961 element = Feld[newx][newy] = Store[x][y];
8963 else if (element == EL_SOKOBAN_OBJECT)
8966 Feld[x][y] = Back[x][y];
8968 if (Back[newx][newy])
8969 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8971 Back[x][y] = Back[newx][newy] = 0;
8974 Store[x][y] = EL_EMPTY;
8979 MovDelay[newx][newy] = 0;
8981 if (CAN_CHANGE_OR_HAS_ACTION(element))
8983 /* copy element change control values to new field */
8984 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8985 ChangePage[newx][newy] = ChangePage[x][y];
8986 ChangeCount[newx][newy] = ChangeCount[x][y];
8987 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8990 #if USE_NEW_CUSTOM_VALUE
8991 CustomValue[newx][newy] = CustomValue[x][y];
8994 ChangeDelay[x][y] = 0;
8995 ChangePage[x][y] = -1;
8996 ChangeCount[x][y] = 0;
8997 ChangeEvent[x][y] = -1;
8999 #if USE_NEW_CUSTOM_VALUE
9000 CustomValue[x][y] = 0;
9003 /* copy animation control values to new field */
9004 GfxFrame[newx][newy] = GfxFrame[x][y];
9005 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9006 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9007 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9009 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9011 /* some elements can leave other elements behind after moving */
9013 if (ei->move_leave_element != EL_EMPTY &&
9014 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9015 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9017 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9018 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9019 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9022 int move_leave_element = ei->move_leave_element;
9026 /* this makes it possible to leave the removed element again */
9027 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9028 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9030 /* this makes it possible to leave the removed element again */
9031 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9032 move_leave_element = stored;
9035 /* this makes it possible to leave the removed element again */
9036 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9037 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9038 move_leave_element = stored;
9041 Feld[x][y] = move_leave_element;
9043 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9044 MovDir[x][y] = direction;
9046 InitField(x, y, FALSE);
9048 if (GFX_CRUMBLED(Feld[x][y]))
9049 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9051 if (ELEM_IS_PLAYER(move_leave_element))
9052 RelocatePlayer(x, y, move_leave_element);
9055 /* do this after checking for left-behind element */
9056 ResetGfxAnimation(x, y); /* reset animation values for old field */
9058 if (!CAN_MOVE(element) ||
9059 (CAN_FALL(element) && direction == MV_DOWN &&
9060 (element == EL_SPRING ||
9061 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9062 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9063 GfxDir[x][y] = MovDir[newx][newy] = 0;
9065 TEST_DrawLevelField(x, y);
9066 TEST_DrawLevelField(newx, newy);
9068 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9070 /* prevent pushed element from moving on in pushed direction */
9071 if (pushed_by_player && CAN_MOVE(element) &&
9072 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9073 !(element_info[element].move_pattern & direction))
9074 TurnRound(newx, newy);
9076 /* prevent elements on conveyor belt from moving on in last direction */
9077 if (pushed_by_conveyor && CAN_FALL(element) &&
9078 direction & MV_HORIZONTAL)
9079 MovDir[newx][newy] = 0;
9081 if (!pushed_by_player)
9083 int nextx = newx + dx, nexty = newy + dy;
9084 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9086 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9088 if (CAN_FALL(element) && direction == MV_DOWN)
9089 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9091 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9092 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9094 #if USE_FIX_IMPACT_COLLISION
9095 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9096 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9100 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9102 TestIfBadThingTouchesPlayer(newx, newy);
9103 TestIfBadThingTouchesFriend(newx, newy);
9105 if (!IS_CUSTOM_ELEMENT(element))
9106 TestIfBadThingTouchesOtherBadThing(newx, newy);
9108 else if (element == EL_PENGUIN)
9109 TestIfFriendTouchesBadThing(newx, newy);
9111 if (DONT_GET_HIT_BY(element))
9113 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9116 /* give the player one last chance (one more frame) to move away */
9117 if (CAN_FALL(element) && direction == MV_DOWN &&
9118 (last_line || (!IS_FREE(x, newy + 1) &&
9119 (!IS_PLAYER(x, newy + 1) ||
9120 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9123 if (pushed_by_player && !game.use_change_when_pushing_bug)
9125 int push_side = MV_DIR_OPPOSITE(direction);
9126 struct PlayerInfo *player = PLAYERINFO(x, y);
9128 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9129 player->index_bit, push_side);
9130 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9131 player->index_bit, push_side);
9134 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9135 MovDelay[newx][newy] = 1;
9137 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9139 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9142 if (ChangePage[newx][newy] != -1) /* delayed change */
9144 int page = ChangePage[newx][newy];
9145 struct ElementChangeInfo *change = &ei->change_page[page];
9147 ChangePage[newx][newy] = -1;
9149 if (change->can_change)
9151 if (ChangeElement(newx, newy, element, page))
9153 if (change->post_change_function)
9154 change->post_change_function(newx, newy);
9158 if (change->has_action)
9159 ExecuteCustomElementAction(newx, newy, element, page);
9163 TestIfElementHitsCustomElement(newx, newy, direction);
9164 TestIfPlayerTouchesCustomElement(newx, newy);
9165 TestIfElementTouchesCustomElement(newx, newy);
9167 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9168 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9169 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9170 MV_DIR_OPPOSITE(direction));
9173 int AmoebeNachbarNr(int ax, int ay)
9176 int element = Feld[ax][ay];
9178 static int xy[4][2] =
9186 for (i = 0; i < NUM_DIRECTIONS; i++)
9188 int x = ax + xy[i][0];
9189 int y = ay + xy[i][1];
9191 if (!IN_LEV_FIELD(x, y))
9194 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9195 group_nr = AmoebaNr[x][y];
9201 void AmoebenVereinigen(int ax, int ay)
9203 int i, x, y, xx, yy;
9204 int new_group_nr = AmoebaNr[ax][ay];
9205 static int xy[4][2] =
9213 if (new_group_nr == 0)
9216 for (i = 0; i < NUM_DIRECTIONS; i++)
9221 if (!IN_LEV_FIELD(x, y))
9224 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9225 Feld[x][y] == EL_BD_AMOEBA ||
9226 Feld[x][y] == EL_AMOEBA_DEAD) &&
9227 AmoebaNr[x][y] != new_group_nr)
9229 int old_group_nr = AmoebaNr[x][y];
9231 if (old_group_nr == 0)
9234 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9235 AmoebaCnt[old_group_nr] = 0;
9236 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9237 AmoebaCnt2[old_group_nr] = 0;
9239 SCAN_PLAYFIELD(xx, yy)
9241 if (AmoebaNr[xx][yy] == old_group_nr)
9242 AmoebaNr[xx][yy] = new_group_nr;
9248 void AmoebeUmwandeln(int ax, int ay)
9252 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9254 int group_nr = AmoebaNr[ax][ay];
9259 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9260 printf("AmoebeUmwandeln(): This should never happen!\n");
9265 SCAN_PLAYFIELD(x, y)
9267 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9270 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9274 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9275 SND_AMOEBA_TURNING_TO_GEM :
9276 SND_AMOEBA_TURNING_TO_ROCK));
9281 static int xy[4][2] =
9289 for (i = 0; i < NUM_DIRECTIONS; i++)
9294 if (!IN_LEV_FIELD(x, y))
9297 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9299 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9300 SND_AMOEBA_TURNING_TO_GEM :
9301 SND_AMOEBA_TURNING_TO_ROCK));
9308 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9311 int group_nr = AmoebaNr[ax][ay];
9312 boolean done = FALSE;
9317 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9318 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9323 SCAN_PLAYFIELD(x, y)
9325 if (AmoebaNr[x][y] == group_nr &&
9326 (Feld[x][y] == EL_AMOEBA_DEAD ||
9327 Feld[x][y] == EL_BD_AMOEBA ||
9328 Feld[x][y] == EL_AMOEBA_GROWING))
9331 Feld[x][y] = new_element;
9332 InitField(x, y, FALSE);
9333 TEST_DrawLevelField(x, y);
9339 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9340 SND_BD_AMOEBA_TURNING_TO_ROCK :
9341 SND_BD_AMOEBA_TURNING_TO_GEM));
9344 void AmoebeWaechst(int x, int y)
9346 static unsigned int sound_delay = 0;
9347 static unsigned int sound_delay_value = 0;
9349 if (!MovDelay[x][y]) /* start new growing cycle */
9353 if (DelayReached(&sound_delay, sound_delay_value))
9355 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9356 sound_delay_value = 30;
9360 if (MovDelay[x][y]) /* wait some time before growing bigger */
9363 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9365 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9366 6 - MovDelay[x][y]);
9368 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9371 if (!MovDelay[x][y])
9373 Feld[x][y] = Store[x][y];
9375 TEST_DrawLevelField(x, y);
9380 void AmoebaDisappearing(int x, int y)
9382 static unsigned int sound_delay = 0;
9383 static unsigned int sound_delay_value = 0;
9385 if (!MovDelay[x][y]) /* start new shrinking cycle */
9389 if (DelayReached(&sound_delay, sound_delay_value))
9390 sound_delay_value = 30;
9393 if (MovDelay[x][y]) /* wait some time before shrinking */
9396 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9398 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9399 6 - MovDelay[x][y]);
9401 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9404 if (!MovDelay[x][y])
9406 Feld[x][y] = EL_EMPTY;
9407 TEST_DrawLevelField(x, y);
9409 /* don't let mole enter this field in this cycle;
9410 (give priority to objects falling to this field from above) */
9416 void AmoebeAbleger(int ax, int ay)
9419 int element = Feld[ax][ay];
9420 int graphic = el2img(element);
9421 int newax = ax, neway = ay;
9422 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9423 static int xy[4][2] =
9431 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9433 Feld[ax][ay] = EL_AMOEBA_DEAD;
9434 TEST_DrawLevelField(ax, ay);
9438 if (IS_ANIMATED(graphic))
9439 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9441 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9442 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9444 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9447 if (MovDelay[ax][ay])
9451 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9454 int x = ax + xy[start][0];
9455 int y = ay + xy[start][1];
9457 if (!IN_LEV_FIELD(x, y))
9460 if (IS_FREE(x, y) ||
9461 CAN_GROW_INTO(Feld[x][y]) ||
9462 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9463 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9469 if (newax == ax && neway == ay)
9472 else /* normal or "filled" (BD style) amoeba */
9475 boolean waiting_for_player = FALSE;
9477 for (i = 0; i < NUM_DIRECTIONS; i++)
9479 int j = (start + i) % 4;
9480 int x = ax + xy[j][0];
9481 int y = ay + xy[j][1];
9483 if (!IN_LEV_FIELD(x, y))
9486 if (IS_FREE(x, y) ||
9487 CAN_GROW_INTO(Feld[x][y]) ||
9488 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9489 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9495 else if (IS_PLAYER(x, y))
9496 waiting_for_player = TRUE;
9499 if (newax == ax && neway == ay) /* amoeba cannot grow */
9501 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9503 Feld[ax][ay] = EL_AMOEBA_DEAD;
9504 TEST_DrawLevelField(ax, ay);
9505 AmoebaCnt[AmoebaNr[ax][ay]]--;
9507 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9509 if (element == EL_AMOEBA_FULL)
9510 AmoebeUmwandeln(ax, ay);
9511 else if (element == EL_BD_AMOEBA)
9512 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9517 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9519 /* amoeba gets larger by growing in some direction */
9521 int new_group_nr = AmoebaNr[ax][ay];
9524 if (new_group_nr == 0)
9526 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9527 printf("AmoebeAbleger(): This should never happen!\n");
9532 AmoebaNr[newax][neway] = new_group_nr;
9533 AmoebaCnt[new_group_nr]++;
9534 AmoebaCnt2[new_group_nr]++;
9536 /* if amoeba touches other amoeba(s) after growing, unify them */
9537 AmoebenVereinigen(newax, neway);
9539 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9541 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9547 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9548 (neway == lev_fieldy - 1 && newax != ax))
9550 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9551 Store[newax][neway] = element;
9553 else if (neway == ay || element == EL_EMC_DRIPPER)
9555 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9557 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9561 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9562 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9563 Store[ax][ay] = EL_AMOEBA_DROP;
9564 ContinueMoving(ax, ay);
9568 TEST_DrawLevelField(newax, neway);
9571 void Life(int ax, int ay)
9575 int element = Feld[ax][ay];
9576 int graphic = el2img(element);
9577 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9579 boolean changed = FALSE;
9581 if (IS_ANIMATED(graphic))
9582 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9587 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9588 MovDelay[ax][ay] = life_time;
9590 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9593 if (MovDelay[ax][ay])
9597 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9599 int xx = ax+x1, yy = ay+y1;
9602 if (!IN_LEV_FIELD(xx, yy))
9605 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9607 int x = xx+x2, y = yy+y2;
9609 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9612 if (((Feld[x][y] == element ||
9613 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9615 (IS_FREE(x, y) && Stop[x][y]))
9619 if (xx == ax && yy == ay) /* field in the middle */
9621 if (nachbarn < life_parameter[0] ||
9622 nachbarn > life_parameter[1])
9624 Feld[xx][yy] = EL_EMPTY;
9626 TEST_DrawLevelField(xx, yy);
9627 Stop[xx][yy] = TRUE;
9631 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9632 { /* free border field */
9633 if (nachbarn >= life_parameter[2] &&
9634 nachbarn <= life_parameter[3])
9636 Feld[xx][yy] = element;
9637 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9639 TEST_DrawLevelField(xx, yy);
9640 Stop[xx][yy] = TRUE;
9647 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9648 SND_GAME_OF_LIFE_GROWING);
9651 static void InitRobotWheel(int x, int y)
9653 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9656 static void RunRobotWheel(int x, int y)
9658 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9661 static void StopRobotWheel(int x, int y)
9663 if (ZX == x && ZY == y)
9667 game.robot_wheel_active = FALSE;
9671 static void InitTimegateWheel(int x, int y)
9673 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9676 static void RunTimegateWheel(int x, int y)
9678 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9681 static void InitMagicBallDelay(int x, int y)
9684 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9686 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9690 static void ActivateMagicBall(int bx, int by)
9694 if (level.ball_random)
9696 int pos_border = RND(8); /* select one of the eight border elements */
9697 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9698 int xx = pos_content % 3;
9699 int yy = pos_content / 3;
9704 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9705 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9709 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9711 int xx = x - bx + 1;
9712 int yy = y - by + 1;
9714 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9715 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9719 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9722 void CheckExit(int x, int y)
9724 if (local_player->gems_still_needed > 0 ||
9725 local_player->sokobanfields_still_needed > 0 ||
9726 local_player->lights_still_needed > 0)
9728 int element = Feld[x][y];
9729 int graphic = el2img(element);
9731 if (IS_ANIMATED(graphic))
9732 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9737 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9740 Feld[x][y] = EL_EXIT_OPENING;
9742 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9745 void CheckExitEM(int x, int y)
9747 if (local_player->gems_still_needed > 0 ||
9748 local_player->sokobanfields_still_needed > 0 ||
9749 local_player->lights_still_needed > 0)
9751 int element = Feld[x][y];
9752 int graphic = el2img(element);
9754 if (IS_ANIMATED(graphic))
9755 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9760 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9763 Feld[x][y] = EL_EM_EXIT_OPENING;
9765 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9768 void CheckExitSteel(int x, int y)
9770 if (local_player->gems_still_needed > 0 ||
9771 local_player->sokobanfields_still_needed > 0 ||
9772 local_player->lights_still_needed > 0)
9774 int element = Feld[x][y];
9775 int graphic = el2img(element);
9777 if (IS_ANIMATED(graphic))
9778 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9783 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9786 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9788 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9791 void CheckExitSteelEM(int x, int y)
9793 if (local_player->gems_still_needed > 0 ||
9794 local_player->sokobanfields_still_needed > 0 ||
9795 local_player->lights_still_needed > 0)
9797 int element = Feld[x][y];
9798 int graphic = el2img(element);
9800 if (IS_ANIMATED(graphic))
9801 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9806 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9809 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9811 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9814 void CheckExitSP(int x, int y)
9816 if (local_player->gems_still_needed > 0)
9818 int element = Feld[x][y];
9819 int graphic = el2img(element);
9821 if (IS_ANIMATED(graphic))
9822 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9827 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9830 Feld[x][y] = EL_SP_EXIT_OPENING;
9832 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9835 static void CloseAllOpenTimegates()
9839 SCAN_PLAYFIELD(x, y)
9841 int element = Feld[x][y];
9843 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9845 Feld[x][y] = EL_TIMEGATE_CLOSING;
9847 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9852 void DrawTwinkleOnField(int x, int y)
9854 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9857 if (Feld[x][y] == EL_BD_DIAMOND)
9860 if (MovDelay[x][y] == 0) /* next animation frame */
9861 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9863 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9867 DrawLevelElementAnimation(x, y, Feld[x][y]);
9869 if (MovDelay[x][y] != 0)
9871 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9872 10 - MovDelay[x][y]);
9874 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9879 void MauerWaechst(int x, int y)
9883 if (!MovDelay[x][y]) /* next animation frame */
9884 MovDelay[x][y] = 3 * delay;
9886 if (MovDelay[x][y]) /* wait some time before next frame */
9890 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9892 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9893 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9895 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9898 if (!MovDelay[x][y])
9900 if (MovDir[x][y] == MV_LEFT)
9902 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9903 TEST_DrawLevelField(x - 1, y);
9905 else if (MovDir[x][y] == MV_RIGHT)
9907 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9908 TEST_DrawLevelField(x + 1, y);
9910 else if (MovDir[x][y] == MV_UP)
9912 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9913 TEST_DrawLevelField(x, y - 1);
9917 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9918 TEST_DrawLevelField(x, y + 1);
9921 Feld[x][y] = Store[x][y];
9923 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9924 TEST_DrawLevelField(x, y);
9929 void MauerAbleger(int ax, int ay)
9931 int element = Feld[ax][ay];
9932 int graphic = el2img(element);
9933 boolean oben_frei = FALSE, unten_frei = FALSE;
9934 boolean links_frei = FALSE, rechts_frei = FALSE;
9935 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9936 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9937 boolean new_wall = FALSE;
9939 if (IS_ANIMATED(graphic))
9940 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9942 if (!MovDelay[ax][ay]) /* start building new wall */
9943 MovDelay[ax][ay] = 6;
9945 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9948 if (MovDelay[ax][ay])
9952 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9954 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9956 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9958 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9961 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9962 element == EL_EXPANDABLE_WALL_ANY)
9966 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9967 Store[ax][ay-1] = element;
9968 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9969 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9970 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9971 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9976 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9977 Store[ax][ay+1] = element;
9978 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9979 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9980 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9981 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9986 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9987 element == EL_EXPANDABLE_WALL_ANY ||
9988 element == EL_EXPANDABLE_WALL ||
9989 element == EL_BD_EXPANDABLE_WALL)
9993 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9994 Store[ax-1][ay] = element;
9995 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9996 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9997 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9998 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10004 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10005 Store[ax+1][ay] = element;
10006 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10007 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10008 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10009 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10014 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10015 TEST_DrawLevelField(ax, ay);
10017 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10018 oben_massiv = TRUE;
10019 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10020 unten_massiv = TRUE;
10021 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10022 links_massiv = TRUE;
10023 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10024 rechts_massiv = TRUE;
10026 if (((oben_massiv && unten_massiv) ||
10027 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10028 element == EL_EXPANDABLE_WALL) &&
10029 ((links_massiv && rechts_massiv) ||
10030 element == EL_EXPANDABLE_WALL_VERTICAL))
10031 Feld[ax][ay] = EL_WALL;
10034 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10037 void MauerAblegerStahl(int ax, int ay)
10039 int element = Feld[ax][ay];
10040 int graphic = el2img(element);
10041 boolean oben_frei = FALSE, unten_frei = FALSE;
10042 boolean links_frei = FALSE, rechts_frei = FALSE;
10043 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10044 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10045 boolean new_wall = FALSE;
10047 if (IS_ANIMATED(graphic))
10048 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10050 if (!MovDelay[ax][ay]) /* start building new wall */
10051 MovDelay[ax][ay] = 6;
10053 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10055 MovDelay[ax][ay]--;
10056 if (MovDelay[ax][ay])
10060 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10062 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10064 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10066 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10067 rechts_frei = TRUE;
10069 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10070 element == EL_EXPANDABLE_STEELWALL_ANY)
10074 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10075 Store[ax][ay-1] = element;
10076 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10077 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10078 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10079 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10084 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10085 Store[ax][ay+1] = element;
10086 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10087 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10088 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10089 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10094 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10095 element == EL_EXPANDABLE_STEELWALL_ANY)
10099 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10100 Store[ax-1][ay] = element;
10101 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10102 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10103 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10104 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10110 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10111 Store[ax+1][ay] = element;
10112 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10113 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10114 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10115 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10120 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10121 oben_massiv = TRUE;
10122 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10123 unten_massiv = TRUE;
10124 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10125 links_massiv = TRUE;
10126 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10127 rechts_massiv = TRUE;
10129 if (((oben_massiv && unten_massiv) ||
10130 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10131 ((links_massiv && rechts_massiv) ||
10132 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10133 Feld[ax][ay] = EL_STEELWALL;
10136 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10139 void CheckForDragon(int x, int y)
10142 boolean dragon_found = FALSE;
10143 static int xy[4][2] =
10151 for (i = 0; i < NUM_DIRECTIONS; i++)
10153 for (j = 0; j < 4; j++)
10155 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10157 if (IN_LEV_FIELD(xx, yy) &&
10158 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10160 if (Feld[xx][yy] == EL_DRAGON)
10161 dragon_found = TRUE;
10170 for (i = 0; i < NUM_DIRECTIONS; i++)
10172 for (j = 0; j < 3; j++)
10174 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10176 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10178 Feld[xx][yy] = EL_EMPTY;
10179 TEST_DrawLevelField(xx, yy);
10188 static void InitBuggyBase(int x, int y)
10190 int element = Feld[x][y];
10191 int activating_delay = FRAMES_PER_SECOND / 4;
10193 ChangeDelay[x][y] =
10194 (element == EL_SP_BUGGY_BASE ?
10195 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10196 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10198 element == EL_SP_BUGGY_BASE_ACTIVE ?
10199 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10202 static void WarnBuggyBase(int x, int y)
10205 static int xy[4][2] =
10213 for (i = 0; i < NUM_DIRECTIONS; i++)
10215 int xx = x + xy[i][0];
10216 int yy = y + xy[i][1];
10218 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10220 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10227 static void InitTrap(int x, int y)
10229 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10232 static void ActivateTrap(int x, int y)
10234 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10237 static void ChangeActiveTrap(int x, int y)
10239 int graphic = IMG_TRAP_ACTIVE;
10241 /* if new animation frame was drawn, correct crumbled sand border */
10242 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10243 TEST_DrawLevelFieldCrumbled(x, y);
10246 static int getSpecialActionElement(int element, int number, int base_element)
10248 return (element != EL_EMPTY ? element :
10249 number != -1 ? base_element + number - 1 :
10253 static int getModifiedActionNumber(int value_old, int operator, int operand,
10254 int value_min, int value_max)
10256 int value_new = (operator == CA_MODE_SET ? operand :
10257 operator == CA_MODE_ADD ? value_old + operand :
10258 operator == CA_MODE_SUBTRACT ? value_old - operand :
10259 operator == CA_MODE_MULTIPLY ? value_old * operand :
10260 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10261 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10264 return (value_new < value_min ? value_min :
10265 value_new > value_max ? value_max :
10269 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10271 struct ElementInfo *ei = &element_info[element];
10272 struct ElementChangeInfo *change = &ei->change_page[page];
10273 int target_element = change->target_element;
10274 int action_type = change->action_type;
10275 int action_mode = change->action_mode;
10276 int action_arg = change->action_arg;
10277 int action_element = change->action_element;
10280 if (!change->has_action)
10283 /* ---------- determine action paramater values -------------------------- */
10285 int level_time_value =
10286 (level.time > 0 ? TimeLeft :
10289 int action_arg_element_raw =
10290 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10291 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10292 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10293 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10294 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10295 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10296 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10298 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10301 if (action_arg_element_raw == EL_GROUP_START)
10302 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10305 int action_arg_direction =
10306 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10307 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10308 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10309 change->actual_trigger_side :
10310 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10311 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10314 int action_arg_number_min =
10315 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10318 int action_arg_number_max =
10319 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10320 action_type == CA_SET_LEVEL_GEMS ? 999 :
10321 action_type == CA_SET_LEVEL_TIME ? 9999 :
10322 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10323 action_type == CA_SET_CE_VALUE ? 9999 :
10324 action_type == CA_SET_CE_SCORE ? 9999 :
10327 int action_arg_number_reset =
10328 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10329 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10330 action_type == CA_SET_LEVEL_TIME ? level.time :
10331 action_type == CA_SET_LEVEL_SCORE ? 0 :
10332 #if USE_NEW_CUSTOM_VALUE
10333 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10335 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10337 action_type == CA_SET_CE_SCORE ? 0 :
10340 int action_arg_number =
10341 (action_arg <= CA_ARG_MAX ? action_arg :
10342 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10343 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10344 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10345 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10346 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10347 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10348 #if USE_NEW_CUSTOM_VALUE
10349 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10351 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10353 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10354 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10355 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10356 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10357 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10358 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10359 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10360 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10361 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10362 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10363 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10364 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10365 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10366 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10369 int action_arg_number_old =
10370 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10371 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10372 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10373 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10374 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10377 int action_arg_number_new =
10378 getModifiedActionNumber(action_arg_number_old,
10379 action_mode, action_arg_number,
10380 action_arg_number_min, action_arg_number_max);
10383 int trigger_player_bits =
10384 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10385 change->actual_trigger_player_bits : change->trigger_player);
10387 int trigger_player_bits =
10388 (change->actual_trigger_player >= EL_PLAYER_1 &&
10389 change->actual_trigger_player <= EL_PLAYER_4 ?
10390 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10394 int action_arg_player_bits =
10395 (action_arg >= CA_ARG_PLAYER_1 &&
10396 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10397 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10398 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10401 /* ---------- execute action -------------------------------------------- */
10403 switch (action_type)
10410 /* ---------- level actions ------------------------------------------- */
10412 case CA_RESTART_LEVEL:
10414 game.restart_level = TRUE;
10419 case CA_SHOW_ENVELOPE:
10421 int element = getSpecialActionElement(action_arg_element,
10422 action_arg_number, EL_ENVELOPE_1);
10424 if (IS_ENVELOPE(element))
10425 local_player->show_envelope = element;
10430 case CA_SET_LEVEL_TIME:
10432 if (level.time > 0) /* only modify limited time value */
10434 TimeLeft = action_arg_number_new;
10437 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10439 DisplayGameControlValues();
10441 DrawGameValue_Time(TimeLeft);
10444 if (!TimeLeft && setup.time_limit)
10445 for (i = 0; i < MAX_PLAYERS; i++)
10446 KillPlayer(&stored_player[i]);
10452 case CA_SET_LEVEL_SCORE:
10454 local_player->score = action_arg_number_new;
10457 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10459 DisplayGameControlValues();
10461 DrawGameValue_Score(local_player->score);
10467 case CA_SET_LEVEL_GEMS:
10469 local_player->gems_still_needed = action_arg_number_new;
10472 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10474 DisplayGameControlValues();
10476 DrawGameValue_Emeralds(local_player->gems_still_needed);
10482 #if !USE_PLAYER_GRAVITY
10483 case CA_SET_LEVEL_GRAVITY:
10485 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10486 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10487 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10493 case CA_SET_LEVEL_WIND:
10495 game.wind_direction = action_arg_direction;
10500 case CA_SET_LEVEL_RANDOM_SEED:
10503 /* ensure that setting a new random seed while playing is predictable */
10504 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10506 InitRND(action_arg_number_new);
10510 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10518 for (i = 0; i < 9; i++)
10519 printf("%d, ", RND(2));
10527 /* ---------- player actions ------------------------------------------ */
10529 case CA_MOVE_PLAYER:
10531 /* automatically move to the next field in specified direction */
10532 for (i = 0; i < MAX_PLAYERS; i++)
10533 if (trigger_player_bits & (1 << i))
10534 stored_player[i].programmed_action = action_arg_direction;
10539 case CA_EXIT_PLAYER:
10541 for (i = 0; i < MAX_PLAYERS; i++)
10542 if (action_arg_player_bits & (1 << i))
10543 PlayerWins(&stored_player[i]);
10548 case CA_KILL_PLAYER:
10550 for (i = 0; i < MAX_PLAYERS; i++)
10551 if (action_arg_player_bits & (1 << i))
10552 KillPlayer(&stored_player[i]);
10557 case CA_SET_PLAYER_KEYS:
10559 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10560 int element = getSpecialActionElement(action_arg_element,
10561 action_arg_number, EL_KEY_1);
10563 if (IS_KEY(element))
10565 for (i = 0; i < MAX_PLAYERS; i++)
10567 if (trigger_player_bits & (1 << i))
10569 stored_player[i].key[KEY_NR(element)] = key_state;
10571 DrawGameDoorValues();
10579 case CA_SET_PLAYER_SPEED:
10582 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10585 for (i = 0; i < MAX_PLAYERS; i++)
10587 if (trigger_player_bits & (1 << i))
10589 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10591 if (action_arg == CA_ARG_SPEED_FASTER &&
10592 stored_player[i].cannot_move)
10594 action_arg_number = STEPSIZE_VERY_SLOW;
10596 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10597 action_arg == CA_ARG_SPEED_FASTER)
10599 action_arg_number = 2;
10600 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10603 else if (action_arg == CA_ARG_NUMBER_RESET)
10605 action_arg_number = level.initial_player_stepsize[i];
10609 getModifiedActionNumber(move_stepsize,
10612 action_arg_number_min,
10613 action_arg_number_max);
10615 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10622 case CA_SET_PLAYER_SHIELD:
10624 for (i = 0; i < MAX_PLAYERS; i++)
10626 if (trigger_player_bits & (1 << i))
10628 if (action_arg == CA_ARG_SHIELD_OFF)
10630 stored_player[i].shield_normal_time_left = 0;
10631 stored_player[i].shield_deadly_time_left = 0;
10633 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10635 stored_player[i].shield_normal_time_left = 999999;
10637 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10639 stored_player[i].shield_normal_time_left = 999999;
10640 stored_player[i].shield_deadly_time_left = 999999;
10648 #if USE_PLAYER_GRAVITY
10649 case CA_SET_PLAYER_GRAVITY:
10651 for (i = 0; i < MAX_PLAYERS; i++)
10653 if (trigger_player_bits & (1 << i))
10655 stored_player[i].gravity =
10656 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10657 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10658 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10659 stored_player[i].gravity);
10667 case CA_SET_PLAYER_ARTWORK:
10669 for (i = 0; i < MAX_PLAYERS; i++)
10671 if (trigger_player_bits & (1 << i))
10673 int artwork_element = action_arg_element;
10675 if (action_arg == CA_ARG_ELEMENT_RESET)
10677 (level.use_artwork_element[i] ? level.artwork_element[i] :
10678 stored_player[i].element_nr);
10680 #if USE_GFX_RESET_PLAYER_ARTWORK
10681 if (stored_player[i].artwork_element != artwork_element)
10682 stored_player[i].Frame = 0;
10685 stored_player[i].artwork_element = artwork_element;
10687 SetPlayerWaiting(&stored_player[i], FALSE);
10689 /* set number of special actions for bored and sleeping animation */
10690 stored_player[i].num_special_action_bored =
10691 get_num_special_action(artwork_element,
10692 ACTION_BORING_1, ACTION_BORING_LAST);
10693 stored_player[i].num_special_action_sleeping =
10694 get_num_special_action(artwork_element,
10695 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10702 case CA_SET_PLAYER_INVENTORY:
10704 for (i = 0; i < MAX_PLAYERS; i++)
10706 struct PlayerInfo *player = &stored_player[i];
10709 if (trigger_player_bits & (1 << i))
10711 int inventory_element = action_arg_element;
10713 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10714 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10715 action_arg == CA_ARG_ELEMENT_ACTION)
10717 int element = inventory_element;
10718 int collect_count = element_info[element].collect_count_initial;
10720 if (!IS_CUSTOM_ELEMENT(element))
10723 if (collect_count == 0)
10724 player->inventory_infinite_element = element;
10726 for (k = 0; k < collect_count; k++)
10727 if (player->inventory_size < MAX_INVENTORY_SIZE)
10728 player->inventory_element[player->inventory_size++] =
10731 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10732 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10733 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10735 if (player->inventory_infinite_element != EL_UNDEFINED &&
10736 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10737 action_arg_element_raw))
10738 player->inventory_infinite_element = EL_UNDEFINED;
10740 for (k = 0, j = 0; j < player->inventory_size; j++)
10742 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10743 action_arg_element_raw))
10744 player->inventory_element[k++] = player->inventory_element[j];
10747 player->inventory_size = k;
10749 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10751 if (player->inventory_size > 0)
10753 for (j = 0; j < player->inventory_size - 1; j++)
10754 player->inventory_element[j] = player->inventory_element[j + 1];
10756 player->inventory_size--;
10759 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10761 if (player->inventory_size > 0)
10762 player->inventory_size--;
10764 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10766 player->inventory_infinite_element = EL_UNDEFINED;
10767 player->inventory_size = 0;
10769 else if (action_arg == CA_ARG_INVENTORY_RESET)
10771 player->inventory_infinite_element = EL_UNDEFINED;
10772 player->inventory_size = 0;
10774 if (level.use_initial_inventory[i])
10776 for (j = 0; j < level.initial_inventory_size[i]; j++)
10778 int element = level.initial_inventory_content[i][j];
10779 int collect_count = element_info[element].collect_count_initial;
10781 if (!IS_CUSTOM_ELEMENT(element))
10784 if (collect_count == 0)
10785 player->inventory_infinite_element = element;
10787 for (k = 0; k < collect_count; k++)
10788 if (player->inventory_size < MAX_INVENTORY_SIZE)
10789 player->inventory_element[player->inventory_size++] =
10800 /* ---------- CE actions ---------------------------------------------- */
10802 case CA_SET_CE_VALUE:
10804 #if USE_NEW_CUSTOM_VALUE
10805 int last_ce_value = CustomValue[x][y];
10807 CustomValue[x][y] = action_arg_number_new;
10809 if (CustomValue[x][y] != last_ce_value)
10811 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10812 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10814 if (CustomValue[x][y] == 0)
10816 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10817 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10825 case CA_SET_CE_SCORE:
10827 #if USE_NEW_CUSTOM_VALUE
10828 int last_ce_score = ei->collect_score;
10830 ei->collect_score = action_arg_number_new;
10832 if (ei->collect_score != last_ce_score)
10834 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10835 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10837 if (ei->collect_score == 0)
10841 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10842 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10845 This is a very special case that seems to be a mixture between
10846 CheckElementChange() and CheckTriggeredElementChange(): while
10847 the first one only affects single elements that are triggered
10848 directly, the second one affects multiple elements in the playfield
10849 that are triggered indirectly by another element. This is a third
10850 case: Changing the CE score always affects multiple identical CEs,
10851 so every affected CE must be checked, not only the single CE for
10852 which the CE score was changed in the first place (as every instance
10853 of that CE shares the same CE score, and therefore also can change)!
10855 SCAN_PLAYFIELD(xx, yy)
10857 if (Feld[xx][yy] == element)
10858 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10859 CE_SCORE_GETS_ZERO);
10868 case CA_SET_CE_ARTWORK:
10870 int artwork_element = action_arg_element;
10871 boolean reset_frame = FALSE;
10874 if (action_arg == CA_ARG_ELEMENT_RESET)
10875 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10878 if (ei->gfx_element != artwork_element)
10879 reset_frame = TRUE;
10881 ei->gfx_element = artwork_element;
10883 SCAN_PLAYFIELD(xx, yy)
10885 if (Feld[xx][yy] == element)
10889 ResetGfxAnimation(xx, yy);
10890 ResetRandomAnimationValue(xx, yy);
10893 TEST_DrawLevelField(xx, yy);
10900 /* ---------- engine actions ------------------------------------------ */
10902 case CA_SET_ENGINE_SCAN_MODE:
10904 InitPlayfieldScanMode(action_arg);
10914 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10916 int old_element = Feld[x][y];
10917 int new_element = GetElementFromGroupElement(element);
10918 int previous_move_direction = MovDir[x][y];
10919 #if USE_NEW_CUSTOM_VALUE
10920 int last_ce_value = CustomValue[x][y];
10922 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10923 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10924 boolean add_player_onto_element = (new_element_is_player &&
10925 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10926 /* this breaks SnakeBite when a snake is
10927 halfway through a door that closes */
10928 /* NOW FIXED AT LEVEL INIT IN files.c */
10929 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10931 IS_WALKABLE(old_element));
10934 /* check if element under the player changes from accessible to unaccessible
10935 (needed for special case of dropping element which then changes) */
10936 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10937 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10945 if (!add_player_onto_element)
10947 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10948 RemoveMovingField(x, y);
10952 Feld[x][y] = new_element;
10954 #if !USE_GFX_RESET_GFX_ANIMATION
10955 ResetGfxAnimation(x, y);
10956 ResetRandomAnimationValue(x, y);
10959 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10960 MovDir[x][y] = previous_move_direction;
10962 #if USE_NEW_CUSTOM_VALUE
10963 if (element_info[new_element].use_last_ce_value)
10964 CustomValue[x][y] = last_ce_value;
10967 InitField_WithBug1(x, y, FALSE);
10969 new_element = Feld[x][y]; /* element may have changed */
10971 #if USE_GFX_RESET_GFX_ANIMATION
10972 ResetGfxAnimation(x, y);
10973 ResetRandomAnimationValue(x, y);
10976 TEST_DrawLevelField(x, y);
10978 if (GFX_CRUMBLED(new_element))
10979 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10983 /* check if element under the player changes from accessible to unaccessible
10984 (needed for special case of dropping element which then changes) */
10985 /* (must be checked after creating new element for walkable group elements) */
10986 #if USE_FIX_KILLED_BY_NON_WALKABLE
10987 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10988 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10995 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10996 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11005 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11006 if (new_element_is_player)
11007 RelocatePlayer(x, y, new_element);
11010 ChangeCount[x][y]++; /* count number of changes in the same frame */
11012 TestIfBadThingTouchesPlayer(x, y);
11013 TestIfPlayerTouchesCustomElement(x, y);
11014 TestIfElementTouchesCustomElement(x, y);
11017 static void CreateField(int x, int y, int element)
11019 CreateFieldExt(x, y, element, FALSE);
11022 static void CreateElementFromChange(int x, int y, int element)
11024 element = GET_VALID_RUNTIME_ELEMENT(element);
11026 #if USE_STOP_CHANGED_ELEMENTS
11027 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11029 int old_element = Feld[x][y];
11031 /* prevent changed element from moving in same engine frame
11032 unless both old and new element can either fall or move */
11033 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11034 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11039 CreateFieldExt(x, y, element, TRUE);
11042 static boolean ChangeElement(int x, int y, int element, int page)
11044 struct ElementInfo *ei = &element_info[element];
11045 struct ElementChangeInfo *change = &ei->change_page[page];
11046 int ce_value = CustomValue[x][y];
11047 int ce_score = ei->collect_score;
11048 int target_element;
11049 int old_element = Feld[x][y];
11051 /* always use default change event to prevent running into a loop */
11052 if (ChangeEvent[x][y] == -1)
11053 ChangeEvent[x][y] = CE_DELAY;
11055 if (ChangeEvent[x][y] == CE_DELAY)
11057 /* reset actual trigger element, trigger player and action element */
11058 change->actual_trigger_element = EL_EMPTY;
11059 change->actual_trigger_player = EL_EMPTY;
11060 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11061 change->actual_trigger_side = CH_SIDE_NONE;
11062 change->actual_trigger_ce_value = 0;
11063 change->actual_trigger_ce_score = 0;
11066 /* do not change elements more than a specified maximum number of changes */
11067 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11070 ChangeCount[x][y]++; /* count number of changes in the same frame */
11072 if (change->explode)
11079 if (change->use_target_content)
11081 boolean complete_replace = TRUE;
11082 boolean can_replace[3][3];
11085 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11088 boolean is_walkable;
11089 boolean is_diggable;
11090 boolean is_collectible;
11091 boolean is_removable;
11092 boolean is_destructible;
11093 int ex = x + xx - 1;
11094 int ey = y + yy - 1;
11095 int content_element = change->target_content.e[xx][yy];
11098 can_replace[xx][yy] = TRUE;
11100 if (ex == x && ey == y) /* do not check changing element itself */
11103 if (content_element == EL_EMPTY_SPACE)
11105 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11110 if (!IN_LEV_FIELD(ex, ey))
11112 can_replace[xx][yy] = FALSE;
11113 complete_replace = FALSE;
11120 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11121 e = MovingOrBlocked2Element(ex, ey);
11123 is_empty = (IS_FREE(ex, ey) ||
11124 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11126 is_walkable = (is_empty || IS_WALKABLE(e));
11127 is_diggable = (is_empty || IS_DIGGABLE(e));
11128 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11129 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11130 is_removable = (is_diggable || is_collectible);
11132 can_replace[xx][yy] =
11133 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11134 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11135 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11136 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11137 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11138 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11139 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11141 if (!can_replace[xx][yy])
11142 complete_replace = FALSE;
11145 if (!change->only_if_complete || complete_replace)
11147 boolean something_has_changed = FALSE;
11149 if (change->only_if_complete && change->use_random_replace &&
11150 RND(100) < change->random_percentage)
11153 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11155 int ex = x + xx - 1;
11156 int ey = y + yy - 1;
11157 int content_element;
11159 if (can_replace[xx][yy] && (!change->use_random_replace ||
11160 RND(100) < change->random_percentage))
11162 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11163 RemoveMovingField(ex, ey);
11165 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11167 content_element = change->target_content.e[xx][yy];
11168 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11169 ce_value, ce_score);
11171 CreateElementFromChange(ex, ey, target_element);
11173 something_has_changed = TRUE;
11175 /* for symmetry reasons, freeze newly created border elements */
11176 if (ex != x || ey != y)
11177 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11181 if (something_has_changed)
11183 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11184 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11190 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11191 ce_value, ce_score);
11193 if (element == EL_DIAGONAL_GROWING ||
11194 element == EL_DIAGONAL_SHRINKING)
11196 target_element = Store[x][y];
11198 Store[x][y] = EL_EMPTY;
11201 CreateElementFromChange(x, y, target_element);
11203 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11204 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11207 /* this uses direct change before indirect change */
11208 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11213 #if USE_NEW_DELAYED_ACTION
11215 static void HandleElementChange(int x, int y, int page)
11217 int element = MovingOrBlocked2Element(x, y);
11218 struct ElementInfo *ei = &element_info[element];
11219 struct ElementChangeInfo *change = &ei->change_page[page];
11220 boolean handle_action_before_change = FALSE;
11223 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11224 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11227 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11228 x, y, element, element_info[element].token_name);
11229 printf("HandleElementChange(): This should never happen!\n");
11234 /* this can happen with classic bombs on walkable, changing elements */
11235 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11238 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11239 ChangeDelay[x][y] = 0;
11245 if (ChangeDelay[x][y] == 0) /* initialize element change */
11247 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11249 if (change->can_change)
11252 /* !!! not clear why graphic animation should be reset at all here !!! */
11253 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11254 #if USE_GFX_RESET_WHEN_NOT_MOVING
11255 /* when a custom element is about to change (for example by change delay),
11256 do not reset graphic animation when the custom element is moving */
11257 if (!IS_MOVING(x, y))
11260 ResetGfxAnimation(x, y);
11261 ResetRandomAnimationValue(x, y);
11265 if (change->pre_change_function)
11266 change->pre_change_function(x, y);
11270 ChangeDelay[x][y]--;
11272 if (ChangeDelay[x][y] != 0) /* continue element change */
11274 if (change->can_change)
11276 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11278 if (IS_ANIMATED(graphic))
11279 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11281 if (change->change_function)
11282 change->change_function(x, y);
11285 else /* finish element change */
11287 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11289 page = ChangePage[x][y];
11290 ChangePage[x][y] = -1;
11292 change = &ei->change_page[page];
11295 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11297 ChangeDelay[x][y] = 1; /* try change after next move step */
11298 ChangePage[x][y] = page; /* remember page to use for change */
11304 /* special case: set new level random seed before changing element */
11305 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11306 handle_action_before_change = TRUE;
11308 if (change->has_action && handle_action_before_change)
11309 ExecuteCustomElementAction(x, y, element, page);
11312 if (change->can_change)
11314 if (ChangeElement(x, y, element, page))
11316 if (change->post_change_function)
11317 change->post_change_function(x, y);
11321 if (change->has_action && !handle_action_before_change)
11322 ExecuteCustomElementAction(x, y, element, page);
11328 static void HandleElementChange(int x, int y, int page)
11330 int element = MovingOrBlocked2Element(x, y);
11331 struct ElementInfo *ei = &element_info[element];
11332 struct ElementChangeInfo *change = &ei->change_page[page];
11335 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11338 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11339 x, y, element, element_info[element].token_name);
11340 printf("HandleElementChange(): This should never happen!\n");
11345 /* this can happen with classic bombs on walkable, changing elements */
11346 if (!CAN_CHANGE(element))
11349 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11350 ChangeDelay[x][y] = 0;
11356 if (ChangeDelay[x][y] == 0) /* initialize element change */
11358 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11360 ResetGfxAnimation(x, y);
11361 ResetRandomAnimationValue(x, y);
11363 if (change->pre_change_function)
11364 change->pre_change_function(x, y);
11367 ChangeDelay[x][y]--;
11369 if (ChangeDelay[x][y] != 0) /* continue element change */
11371 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11373 if (IS_ANIMATED(graphic))
11374 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11376 if (change->change_function)
11377 change->change_function(x, y);
11379 else /* finish element change */
11381 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11383 page = ChangePage[x][y];
11384 ChangePage[x][y] = -1;
11386 change = &ei->change_page[page];
11389 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11391 ChangeDelay[x][y] = 1; /* try change after next move step */
11392 ChangePage[x][y] = page; /* remember page to use for change */
11397 if (ChangeElement(x, y, element, page))
11399 if (change->post_change_function)
11400 change->post_change_function(x, y);
11407 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11408 int trigger_element,
11410 int trigger_player,
11414 boolean change_done_any = FALSE;
11415 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11418 if (!(trigger_events[trigger_element][trigger_event]))
11422 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11423 trigger_event, recursion_loop_depth, recursion_loop_detected,
11424 recursion_loop_element, EL_NAME(recursion_loop_element));
11427 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11429 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11431 int element = EL_CUSTOM_START + i;
11432 boolean change_done = FALSE;
11435 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11436 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11439 for (p = 0; p < element_info[element].num_change_pages; p++)
11441 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11443 if (change->can_change_or_has_action &&
11444 change->has_event[trigger_event] &&
11445 change->trigger_side & trigger_side &&
11446 change->trigger_player & trigger_player &&
11447 change->trigger_page & trigger_page_bits &&
11448 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11450 change->actual_trigger_element = trigger_element;
11451 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11452 change->actual_trigger_player_bits = trigger_player;
11453 change->actual_trigger_side = trigger_side;
11454 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11455 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11458 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11459 element, EL_NAME(element), p);
11462 if ((change->can_change && !change_done) || change->has_action)
11466 SCAN_PLAYFIELD(x, y)
11468 if (Feld[x][y] == element)
11470 if (change->can_change && !change_done)
11472 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11473 /* if element already changed in this frame, not only prevent
11474 another element change (checked in ChangeElement()), but
11475 also prevent additional element actions for this element */
11477 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11478 !level.use_action_after_change_bug)
11483 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11484 element, EL_NAME(element), p);
11487 ChangeDelay[x][y] = 1;
11488 ChangeEvent[x][y] = trigger_event;
11490 HandleElementChange(x, y, p);
11492 #if USE_NEW_DELAYED_ACTION
11493 else if (change->has_action)
11495 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11496 /* if element already changed in this frame, not only prevent
11497 another element change (checked in ChangeElement()), but
11498 also prevent additional element actions for this element */
11500 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11501 !level.use_action_after_change_bug)
11507 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11508 element, EL_NAME(element), p);
11511 ExecuteCustomElementAction(x, y, element, p);
11512 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11515 if (change->has_action)
11517 ExecuteCustomElementAction(x, y, element, p);
11518 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11524 if (change->can_change)
11526 change_done = TRUE;
11527 change_done_any = TRUE;
11530 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11531 element, EL_NAME(element), p);
11540 RECURSION_LOOP_DETECTION_END();
11542 return change_done_any;
11545 static boolean CheckElementChangeExt(int x, int y,
11547 int trigger_element,
11549 int trigger_player,
11552 boolean change_done = FALSE;
11555 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11556 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11559 if (Feld[x][y] == EL_BLOCKED)
11561 Blocked2Moving(x, y, &x, &y);
11562 element = Feld[x][y];
11566 /* check if element has already changed */
11567 if (Feld[x][y] != element)
11570 /* check if element has already changed or is about to change after moving */
11571 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11572 Feld[x][y] != element) ||
11574 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11575 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11576 ChangePage[x][y] != -1)))
11581 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11582 trigger_event, recursion_loop_depth, recursion_loop_detected,
11583 recursion_loop_element, EL_NAME(recursion_loop_element));
11586 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11589 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11592 for (p = 0; p < element_info[element].num_change_pages; p++)
11594 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11596 /* check trigger element for all events where the element that is checked
11597 for changing interacts with a directly adjacent element -- this is
11598 different to element changes that affect other elements to change on the
11599 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11600 boolean check_trigger_element =
11601 (trigger_event == CE_TOUCHING_X ||
11602 trigger_event == CE_HITTING_X ||
11603 trigger_event == CE_HIT_BY_X ||
11605 /* this one was forgotten until 3.2.3 */
11606 trigger_event == CE_DIGGING_X);
11609 if (change->can_change_or_has_action &&
11610 change->has_event[trigger_event] &&
11611 change->trigger_side & trigger_side &&
11612 change->trigger_player & trigger_player &&
11613 (!check_trigger_element ||
11614 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11616 change->actual_trigger_element = trigger_element;
11617 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11618 change->actual_trigger_player_bits = trigger_player;
11619 change->actual_trigger_side = trigger_side;
11620 change->actual_trigger_ce_value = CustomValue[x][y];
11621 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11623 /* special case: trigger element not at (x,y) position for some events */
11624 if (check_trigger_element)
11636 { 0, 0 }, { 0, 0 }, { 0, 0 },
11640 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11641 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11643 change->actual_trigger_ce_value = CustomValue[xx][yy];
11644 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11647 if (change->can_change && !change_done)
11649 ChangeDelay[x][y] = 1;
11650 ChangeEvent[x][y] = trigger_event;
11652 HandleElementChange(x, y, p);
11654 change_done = TRUE;
11656 #if USE_NEW_DELAYED_ACTION
11657 else if (change->has_action)
11659 ExecuteCustomElementAction(x, y, element, p);
11660 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11663 if (change->has_action)
11665 ExecuteCustomElementAction(x, y, element, p);
11666 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11672 RECURSION_LOOP_DETECTION_END();
11674 return change_done;
11677 static void PlayPlayerSound(struct PlayerInfo *player)
11679 int jx = player->jx, jy = player->jy;
11680 int sound_element = player->artwork_element;
11681 int last_action = player->last_action_waiting;
11682 int action = player->action_waiting;
11684 if (player->is_waiting)
11686 if (action != last_action)
11687 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11689 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11693 if (action != last_action)
11694 StopSound(element_info[sound_element].sound[last_action]);
11696 if (last_action == ACTION_SLEEPING)
11697 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11701 static void PlayAllPlayersSound()
11705 for (i = 0; i < MAX_PLAYERS; i++)
11706 if (stored_player[i].active)
11707 PlayPlayerSound(&stored_player[i]);
11710 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11712 boolean last_waiting = player->is_waiting;
11713 int move_dir = player->MovDir;
11715 player->dir_waiting = move_dir;
11716 player->last_action_waiting = player->action_waiting;
11720 if (!last_waiting) /* not waiting -> waiting */
11722 player->is_waiting = TRUE;
11724 player->frame_counter_bored =
11726 game.player_boring_delay_fixed +
11727 GetSimpleRandom(game.player_boring_delay_random);
11728 player->frame_counter_sleeping =
11730 game.player_sleeping_delay_fixed +
11731 GetSimpleRandom(game.player_sleeping_delay_random);
11733 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11736 if (game.player_sleeping_delay_fixed +
11737 game.player_sleeping_delay_random > 0 &&
11738 player->anim_delay_counter == 0 &&
11739 player->post_delay_counter == 0 &&
11740 FrameCounter >= player->frame_counter_sleeping)
11741 player->is_sleeping = TRUE;
11742 else if (game.player_boring_delay_fixed +
11743 game.player_boring_delay_random > 0 &&
11744 FrameCounter >= player->frame_counter_bored)
11745 player->is_bored = TRUE;
11747 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11748 player->is_bored ? ACTION_BORING :
11751 if (player->is_sleeping && player->use_murphy)
11753 /* special case for sleeping Murphy when leaning against non-free tile */
11755 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11756 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11757 !IS_MOVING(player->jx - 1, player->jy)))
11758 move_dir = MV_LEFT;
11759 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11760 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11761 !IS_MOVING(player->jx + 1, player->jy)))
11762 move_dir = MV_RIGHT;
11764 player->is_sleeping = FALSE;
11766 player->dir_waiting = move_dir;
11769 if (player->is_sleeping)
11771 if (player->num_special_action_sleeping > 0)
11773 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11775 int last_special_action = player->special_action_sleeping;
11776 int num_special_action = player->num_special_action_sleeping;
11777 int special_action =
11778 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11779 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11780 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11781 last_special_action + 1 : ACTION_SLEEPING);
11782 int special_graphic =
11783 el_act_dir2img(player->artwork_element, special_action, move_dir);
11785 player->anim_delay_counter =
11786 graphic_info[special_graphic].anim_delay_fixed +
11787 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11788 player->post_delay_counter =
11789 graphic_info[special_graphic].post_delay_fixed +
11790 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11792 player->special_action_sleeping = special_action;
11795 if (player->anim_delay_counter > 0)
11797 player->action_waiting = player->special_action_sleeping;
11798 player->anim_delay_counter--;
11800 else if (player->post_delay_counter > 0)
11802 player->post_delay_counter--;
11806 else if (player->is_bored)
11808 if (player->num_special_action_bored > 0)
11810 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11812 int special_action =
11813 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11814 int special_graphic =
11815 el_act_dir2img(player->artwork_element, special_action, move_dir);
11817 player->anim_delay_counter =
11818 graphic_info[special_graphic].anim_delay_fixed +
11819 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11820 player->post_delay_counter =
11821 graphic_info[special_graphic].post_delay_fixed +
11822 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11824 player->special_action_bored = special_action;
11827 if (player->anim_delay_counter > 0)
11829 player->action_waiting = player->special_action_bored;
11830 player->anim_delay_counter--;
11832 else if (player->post_delay_counter > 0)
11834 player->post_delay_counter--;
11839 else if (last_waiting) /* waiting -> not waiting */
11841 player->is_waiting = FALSE;
11842 player->is_bored = FALSE;
11843 player->is_sleeping = FALSE;
11845 player->frame_counter_bored = -1;
11846 player->frame_counter_sleeping = -1;
11848 player->anim_delay_counter = 0;
11849 player->post_delay_counter = 0;
11851 player->dir_waiting = player->MovDir;
11852 player->action_waiting = ACTION_DEFAULT;
11854 player->special_action_bored = ACTION_DEFAULT;
11855 player->special_action_sleeping = ACTION_DEFAULT;
11859 static void CheckSingleStepMode(struct PlayerInfo *player)
11861 if (tape.single_step && tape.recording && !tape.pausing)
11863 /* as it is called "single step mode", just return to pause mode when the
11864 player stopped moving after one tile (or never starts moving at all) */
11865 if (!player->is_moving && !player->is_pushing)
11867 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11868 SnapField(player, 0, 0); /* stop snapping */
11873 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11875 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11876 int left = player_action & JOY_LEFT;
11877 int right = player_action & JOY_RIGHT;
11878 int up = player_action & JOY_UP;
11879 int down = player_action & JOY_DOWN;
11880 int button1 = player_action & JOY_BUTTON_1;
11881 int button2 = player_action & JOY_BUTTON_2;
11882 int dx = (left ? -1 : right ? 1 : 0);
11883 int dy = (up ? -1 : down ? 1 : 0);
11885 if (!player->active || tape.pausing)
11891 snapped = SnapField(player, dx, dy);
11895 dropped = DropElement(player);
11897 moved = MovePlayer(player, dx, dy);
11900 CheckSingleStepMode(player);
11902 SetPlayerWaiting(player, FALSE);
11904 return player_action;
11908 /* no actions for this player (no input at player's configured device) */
11910 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11911 SnapField(player, 0, 0);
11912 CheckGravityMovementWhenNotMoving(player);
11914 if (player->MovPos == 0)
11915 SetPlayerWaiting(player, TRUE);
11917 if (player->MovPos == 0) /* needed for tape.playing */
11918 player->is_moving = FALSE;
11920 player->is_dropping = FALSE;
11921 player->is_dropping_pressed = FALSE;
11922 player->drop_pressed_delay = 0;
11924 CheckSingleStepMode(player);
11930 static void CheckLevelTime()
11934 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11935 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11937 if (level.native_em_level->lev->home == 0) /* all players at home */
11939 PlayerWins(local_player);
11941 AllPlayersGone = TRUE;
11943 level.native_em_level->lev->home = -1;
11946 if (level.native_em_level->ply[0]->alive == 0 &&
11947 level.native_em_level->ply[1]->alive == 0 &&
11948 level.native_em_level->ply[2]->alive == 0 &&
11949 level.native_em_level->ply[3]->alive == 0) /* all dead */
11950 AllPlayersGone = TRUE;
11952 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11954 if (game_sp.LevelSolved &&
11955 !game_sp.GameOver) /* game won */
11957 PlayerWins(local_player);
11959 game_sp.GameOver = TRUE;
11961 AllPlayersGone = TRUE;
11964 if (game_sp.GameOver) /* game lost */
11965 AllPlayersGone = TRUE;
11968 if (TimeFrames >= FRAMES_PER_SECOND)
11973 for (i = 0; i < MAX_PLAYERS; i++)
11975 struct PlayerInfo *player = &stored_player[i];
11977 if (SHIELD_ON(player))
11979 player->shield_normal_time_left--;
11981 if (player->shield_deadly_time_left > 0)
11982 player->shield_deadly_time_left--;
11986 if (!local_player->LevelSolved && !level.use_step_counter)
11994 if (TimeLeft <= 10 && setup.time_limit)
11995 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11998 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11999 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12001 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12003 /* (already called by UpdateAndDisplayGameControlValues() below) */
12004 // DisplayGameControlValues();
12006 DrawGameValue_Time(TimeLeft);
12009 if (!TimeLeft && setup.time_limit)
12011 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12012 level.native_em_level->lev->killed_out_of_time = TRUE;
12014 for (i = 0; i < MAX_PLAYERS; i++)
12015 KillPlayer(&stored_player[i]);
12019 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12021 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12023 /* (already called by UpdateAndDisplayGameControlValues() below) */
12024 // DisplayGameControlValues();
12027 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12028 DrawGameValue_Time(TimePlayed);
12031 level.native_em_level->lev->time =
12032 (game.no_time_limit ? TimePlayed : TimeLeft);
12035 if (tape.recording || tape.playing)
12036 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12040 UpdateAndDisplayGameControlValues();
12042 UpdateGameDoorValues();
12043 DrawGameDoorValues();
12047 void AdvanceFrameAndPlayerCounters(int player_nr)
12051 /* advance frame counters (global frame counter and time frame counter) */
12055 /* advance player counters (counters for move delay, move animation etc.) */
12056 for (i = 0; i < MAX_PLAYERS; i++)
12058 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12059 int move_delay_value = stored_player[i].move_delay_value;
12060 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12062 if (!advance_player_counters) /* not all players may be affected */
12065 #if USE_NEW_PLAYER_ANIM
12066 if (move_frames == 0) /* less than one move per game frame */
12068 int stepsize = TILEX / move_delay_value;
12069 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12070 int count = (stored_player[i].is_moving ?
12071 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12073 if (count % delay == 0)
12078 stored_player[i].Frame += move_frames;
12080 if (stored_player[i].MovPos != 0)
12081 stored_player[i].StepFrame += move_frames;
12083 if (stored_player[i].move_delay > 0)
12084 stored_player[i].move_delay--;
12086 /* due to bugs in previous versions, counter must count up, not down */
12087 if (stored_player[i].push_delay != -1)
12088 stored_player[i].push_delay++;
12090 if (stored_player[i].drop_delay > 0)
12091 stored_player[i].drop_delay--;
12093 if (stored_player[i].is_dropping_pressed)
12094 stored_player[i].drop_pressed_delay++;
12098 void StartGameActions(boolean init_network_game, boolean record_tape,
12101 unsigned int new_random_seed = InitRND(random_seed);
12104 TapeStartRecording(new_random_seed);
12106 #if defined(NETWORK_AVALIABLE)
12107 if (init_network_game)
12109 SendToServer_StartPlaying();
12120 static unsigned int game_frame_delay = 0;
12121 unsigned int game_frame_delay_value;
12122 byte *recorded_player_action;
12123 byte summarized_player_action = 0;
12124 byte tape_action[MAX_PLAYERS];
12127 /* detect endless loops, caused by custom element programming */
12128 if (recursion_loop_detected && recursion_loop_depth == 0)
12130 char *message = getStringCat3("Internal Error ! Element ",
12131 EL_NAME(recursion_loop_element),
12132 " caused endless loop ! Quit the game ?");
12134 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12135 EL_NAME(recursion_loop_element));
12137 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12139 recursion_loop_detected = FALSE; /* if game should be continued */
12146 if (game.restart_level)
12147 StartGameActions(options.network, setup.autorecord, level.random_seed);
12149 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12150 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12152 if (level.native_em_level->lev->home == 0) /* all players at home */
12154 PlayerWins(local_player);
12156 AllPlayersGone = TRUE;
12158 level.native_em_level->lev->home = -1;
12161 if (level.native_em_level->ply[0]->alive == 0 &&
12162 level.native_em_level->ply[1]->alive == 0 &&
12163 level.native_em_level->ply[2]->alive == 0 &&
12164 level.native_em_level->ply[3]->alive == 0) /* all dead */
12165 AllPlayersGone = TRUE;
12167 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12169 if (game_sp.LevelSolved &&
12170 !game_sp.GameOver) /* game won */
12172 PlayerWins(local_player);
12174 game_sp.GameOver = TRUE;
12176 AllPlayersGone = TRUE;
12179 if (game_sp.GameOver) /* game lost */
12180 AllPlayersGone = TRUE;
12183 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12186 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12189 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12192 game_frame_delay_value =
12193 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12195 if (tape.playing && tape.warp_forward && !tape.pausing)
12196 game_frame_delay_value = 0;
12198 /* ---------- main game synchronization point ---------- */
12200 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12202 if (network_playing && !network_player_action_received)
12204 /* try to get network player actions in time */
12206 #if defined(NETWORK_AVALIABLE)
12207 /* last chance to get network player actions without main loop delay */
12208 HandleNetworking();
12211 /* game was quit by network peer */
12212 if (game_status != GAME_MODE_PLAYING)
12215 if (!network_player_action_received)
12216 return; /* failed to get network player actions in time */
12218 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12224 /* at this point we know that we really continue executing the game */
12226 network_player_action_received = FALSE;
12228 /* when playing tape, read previously recorded player input from tape data */
12229 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12232 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12237 if (tape.set_centered_player)
12239 game.centered_player_nr_next = tape.centered_player_nr_next;
12240 game.set_centered_player = TRUE;
12243 for (i = 0; i < MAX_PLAYERS; i++)
12245 summarized_player_action |= stored_player[i].action;
12247 if (!network_playing)
12248 stored_player[i].effective_action = stored_player[i].action;
12251 #if defined(NETWORK_AVALIABLE)
12252 if (network_playing)
12253 SendToServer_MovePlayer(summarized_player_action);
12256 if (!options.network && !setup.team_mode)
12257 local_player->effective_action = summarized_player_action;
12259 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12261 for (i = 0; i < MAX_PLAYERS; i++)
12262 stored_player[i].effective_action =
12263 (i == game.centered_player_nr ? summarized_player_action : 0);
12266 if (recorded_player_action != NULL)
12267 for (i = 0; i < MAX_PLAYERS; i++)
12268 stored_player[i].effective_action = recorded_player_action[i];
12270 for (i = 0; i < MAX_PLAYERS; i++)
12272 tape_action[i] = stored_player[i].effective_action;
12274 /* (this can only happen in the R'n'D game engine) */
12275 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12276 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12279 /* only record actions from input devices, but not programmed actions */
12280 if (tape.recording)
12281 TapeRecordAction(tape_action);
12283 #if USE_NEW_PLAYER_ASSIGNMENTS
12285 byte mapped_action[MAX_PLAYERS];
12287 for (i = 0; i < MAX_PLAYERS; i++)
12288 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12290 for (i = 0; i < MAX_PLAYERS; i++)
12291 stored_player[i].effective_action = mapped_action[i];
12295 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12297 GameActions_EM_Main();
12299 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12301 GameActions_SP_Main();
12309 void GameActions_EM_Main()
12311 byte effective_action[MAX_PLAYERS];
12312 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12315 for (i = 0; i < MAX_PLAYERS; i++)
12316 effective_action[i] = stored_player[i].effective_action;
12318 GameActions_EM(effective_action, warp_mode);
12322 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12325 void GameActions_SP_Main()
12327 byte effective_action[MAX_PLAYERS];
12328 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12331 for (i = 0; i < MAX_PLAYERS; i++)
12332 effective_action[i] = stored_player[i].effective_action;
12334 GameActions_SP(effective_action, warp_mode);
12338 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12341 void GameActions_RND()
12343 int magic_wall_x = 0, magic_wall_y = 0;
12344 int i, x, y, element, graphic;
12346 InitPlayfieldScanModeVars();
12348 #if USE_ONE_MORE_CHANGE_PER_FRAME
12349 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12351 SCAN_PLAYFIELD(x, y)
12353 ChangeCount[x][y] = 0;
12354 ChangeEvent[x][y] = -1;
12359 if (game.set_centered_player)
12361 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12363 /* switching to "all players" only possible if all players fit to screen */
12364 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12366 game.centered_player_nr_next = game.centered_player_nr;
12367 game.set_centered_player = FALSE;
12370 /* do not switch focus to non-existing (or non-active) player */
12371 if (game.centered_player_nr_next >= 0 &&
12372 !stored_player[game.centered_player_nr_next].active)
12374 game.centered_player_nr_next = game.centered_player_nr;
12375 game.set_centered_player = FALSE;
12379 if (game.set_centered_player &&
12380 ScreenMovPos == 0) /* screen currently aligned at tile position */
12384 if (game.centered_player_nr_next == -1)
12386 setScreenCenteredToAllPlayers(&sx, &sy);
12390 sx = stored_player[game.centered_player_nr_next].jx;
12391 sy = stored_player[game.centered_player_nr_next].jy;
12394 game.centered_player_nr = game.centered_player_nr_next;
12395 game.set_centered_player = FALSE;
12397 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12398 DrawGameDoorValues();
12401 for (i = 0; i < MAX_PLAYERS; i++)
12403 int actual_player_action = stored_player[i].effective_action;
12406 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12407 - rnd_equinox_tetrachloride 048
12408 - rnd_equinox_tetrachloride_ii 096
12409 - rnd_emanuel_schmieg 002
12410 - doctor_sloan_ww 001, 020
12412 if (stored_player[i].MovPos == 0)
12413 CheckGravityMovement(&stored_player[i]);
12416 /* overwrite programmed action with tape action */
12417 if (stored_player[i].programmed_action)
12418 actual_player_action = stored_player[i].programmed_action;
12420 PlayerActions(&stored_player[i], actual_player_action);
12422 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12425 ScrollScreen(NULL, SCROLL_GO_ON);
12427 /* for backwards compatibility, the following code emulates a fixed bug that
12428 occured when pushing elements (causing elements that just made their last
12429 pushing step to already (if possible) make their first falling step in the
12430 same game frame, which is bad); this code is also needed to use the famous
12431 "spring push bug" which is used in older levels and might be wanted to be
12432 used also in newer levels, but in this case the buggy pushing code is only
12433 affecting the "spring" element and no other elements */
12435 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12437 for (i = 0; i < MAX_PLAYERS; i++)
12439 struct PlayerInfo *player = &stored_player[i];
12440 int x = player->jx;
12441 int y = player->jy;
12443 if (player->active && player->is_pushing && player->is_moving &&
12445 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12446 Feld[x][y] == EL_SPRING))
12448 ContinueMoving(x, y);
12450 /* continue moving after pushing (this is actually a bug) */
12451 if (!IS_MOVING(x, y))
12452 Stop[x][y] = FALSE;
12458 debug_print_timestamp(0, "start main loop profiling");
12461 SCAN_PLAYFIELD(x, y)
12463 ChangeCount[x][y] = 0;
12464 ChangeEvent[x][y] = -1;
12466 /* this must be handled before main playfield loop */
12467 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12470 if (MovDelay[x][y] <= 0)
12474 #if USE_NEW_SNAP_DELAY
12475 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12478 if (MovDelay[x][y] <= 0)
12481 TEST_DrawLevelField(x, y);
12483 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12489 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12491 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12492 printf("GameActions(): This should never happen!\n");
12494 ChangePage[x][y] = -1;
12498 Stop[x][y] = FALSE;
12499 if (WasJustMoving[x][y] > 0)
12500 WasJustMoving[x][y]--;
12501 if (WasJustFalling[x][y] > 0)
12502 WasJustFalling[x][y]--;
12503 if (CheckCollision[x][y] > 0)
12504 CheckCollision[x][y]--;
12505 if (CheckImpact[x][y] > 0)
12506 CheckImpact[x][y]--;
12510 /* reset finished pushing action (not done in ContinueMoving() to allow
12511 continuous pushing animation for elements with zero push delay) */
12512 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12514 ResetGfxAnimation(x, y);
12515 TEST_DrawLevelField(x, y);
12519 if (IS_BLOCKED(x, y))
12523 Blocked2Moving(x, y, &oldx, &oldy);
12524 if (!IS_MOVING(oldx, oldy))
12526 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12527 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12528 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12529 printf("GameActions(): This should never happen!\n");
12536 debug_print_timestamp(0, "- time for pre-main loop:");
12539 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12540 SCAN_PLAYFIELD(x, y)
12542 element = Feld[x][y];
12543 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12548 int element2 = element;
12549 int graphic2 = graphic;
12551 int element2 = Feld[x][y];
12552 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12554 int last_gfx_frame = GfxFrame[x][y];
12556 if (graphic_info[graphic2].anim_global_sync)
12557 GfxFrame[x][y] = FrameCounter;
12558 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12559 GfxFrame[x][y] = CustomValue[x][y];
12560 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12561 GfxFrame[x][y] = element_info[element2].collect_score;
12562 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12563 GfxFrame[x][y] = ChangeDelay[x][y];
12565 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12566 DrawLevelGraphicAnimation(x, y, graphic2);
12569 ResetGfxFrame(x, y, TRUE);
12573 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12574 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12575 ResetRandomAnimationValue(x, y);
12579 SetRandomAnimationValue(x, y);
12583 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12586 #endif // -------------------- !!! TEST ONLY !!! --------------------
12589 debug_print_timestamp(0, "- time for TEST loop: -->");
12592 SCAN_PLAYFIELD(x, y)
12594 element = Feld[x][y];
12595 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12597 ResetGfxFrame(x, y, TRUE);
12599 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12600 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12601 ResetRandomAnimationValue(x, y);
12603 SetRandomAnimationValue(x, y);
12605 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12607 if (IS_INACTIVE(element))
12609 if (IS_ANIMATED(graphic))
12610 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12615 /* this may take place after moving, so 'element' may have changed */
12616 if (IS_CHANGING(x, y) &&
12617 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12619 int page = element_info[element].event_page_nr[CE_DELAY];
12622 HandleElementChange(x, y, page);
12624 if (CAN_CHANGE(element))
12625 HandleElementChange(x, y, page);
12627 if (HAS_ACTION(element))
12628 ExecuteCustomElementAction(x, y, element, page);
12631 element = Feld[x][y];
12632 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12635 #if 0 // ---------------------------------------------------------------------
12637 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12641 element = Feld[x][y];
12642 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12644 if (IS_ANIMATED(graphic) &&
12645 !IS_MOVING(x, y) &&
12647 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12649 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12650 TEST_DrawTwinkleOnField(x, y);
12652 else if (IS_MOVING(x, y))
12653 ContinueMoving(x, y);
12660 case EL_EM_EXIT_OPEN:
12661 case EL_SP_EXIT_OPEN:
12662 case EL_STEEL_EXIT_OPEN:
12663 case EL_EM_STEEL_EXIT_OPEN:
12664 case EL_SP_TERMINAL:
12665 case EL_SP_TERMINAL_ACTIVE:
12666 case EL_EXTRA_TIME:
12667 case EL_SHIELD_NORMAL:
12668 case EL_SHIELD_DEADLY:
12669 if (IS_ANIMATED(graphic))
12670 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12673 case EL_DYNAMITE_ACTIVE:
12674 case EL_EM_DYNAMITE_ACTIVE:
12675 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12676 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12677 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12678 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12679 case EL_SP_DISK_RED_ACTIVE:
12680 CheckDynamite(x, y);
12683 case EL_AMOEBA_GROWING:
12684 AmoebeWaechst(x, y);
12687 case EL_AMOEBA_SHRINKING:
12688 AmoebaDisappearing(x, y);
12691 #if !USE_NEW_AMOEBA_CODE
12692 case EL_AMOEBA_WET:
12693 case EL_AMOEBA_DRY:
12694 case EL_AMOEBA_FULL:
12696 case EL_EMC_DRIPPER:
12697 AmoebeAbleger(x, y);
12701 case EL_GAME_OF_LIFE:
12706 case EL_EXIT_CLOSED:
12710 case EL_EM_EXIT_CLOSED:
12714 case EL_STEEL_EXIT_CLOSED:
12715 CheckExitSteel(x, y);
12718 case EL_EM_STEEL_EXIT_CLOSED:
12719 CheckExitSteelEM(x, y);
12722 case EL_SP_EXIT_CLOSED:
12726 case EL_EXPANDABLE_WALL_GROWING:
12727 case EL_EXPANDABLE_STEELWALL_GROWING:
12728 MauerWaechst(x, y);
12731 case EL_EXPANDABLE_WALL:
12732 case EL_EXPANDABLE_WALL_HORIZONTAL:
12733 case EL_EXPANDABLE_WALL_VERTICAL:
12734 case EL_EXPANDABLE_WALL_ANY:
12735 case EL_BD_EXPANDABLE_WALL:
12736 MauerAbleger(x, y);
12739 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12740 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12741 case EL_EXPANDABLE_STEELWALL_ANY:
12742 MauerAblegerStahl(x, y);
12746 CheckForDragon(x, y);
12752 case EL_ELEMENT_SNAPPING:
12753 case EL_DIAGONAL_SHRINKING:
12754 case EL_DIAGONAL_GROWING:
12757 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12759 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12764 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12765 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12770 #else // ---------------------------------------------------------------------
12772 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12776 element = Feld[x][y];
12777 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12779 if (IS_ANIMATED(graphic) &&
12780 !IS_MOVING(x, y) &&
12782 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12784 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12785 TEST_DrawTwinkleOnField(x, y);
12787 else if ((element == EL_ACID ||
12788 element == EL_EXIT_OPEN ||
12789 element == EL_EM_EXIT_OPEN ||
12790 element == EL_SP_EXIT_OPEN ||
12791 element == EL_STEEL_EXIT_OPEN ||
12792 element == EL_EM_STEEL_EXIT_OPEN ||
12793 element == EL_SP_TERMINAL ||
12794 element == EL_SP_TERMINAL_ACTIVE ||
12795 element == EL_EXTRA_TIME ||
12796 element == EL_SHIELD_NORMAL ||
12797 element == EL_SHIELD_DEADLY) &&
12798 IS_ANIMATED(graphic))
12799 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12800 else if (IS_MOVING(x, y))
12801 ContinueMoving(x, y);
12802 else if (IS_ACTIVE_BOMB(element))
12803 CheckDynamite(x, y);
12804 else if (element == EL_AMOEBA_GROWING)
12805 AmoebeWaechst(x, y);
12806 else if (element == EL_AMOEBA_SHRINKING)
12807 AmoebaDisappearing(x, y);
12809 #if !USE_NEW_AMOEBA_CODE
12810 else if (IS_AMOEBALIVE(element))
12811 AmoebeAbleger(x, y);
12814 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12816 else if (element == EL_EXIT_CLOSED)
12818 else if (element == EL_EM_EXIT_CLOSED)
12820 else if (element == EL_STEEL_EXIT_CLOSED)
12821 CheckExitSteel(x, y);
12822 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12823 CheckExitSteelEM(x, y);
12824 else if (element == EL_SP_EXIT_CLOSED)
12826 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12827 element == EL_EXPANDABLE_STEELWALL_GROWING)
12828 MauerWaechst(x, y);
12829 else if (element == EL_EXPANDABLE_WALL ||
12830 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12831 element == EL_EXPANDABLE_WALL_VERTICAL ||
12832 element == EL_EXPANDABLE_WALL_ANY ||
12833 element == EL_BD_EXPANDABLE_WALL)
12834 MauerAbleger(x, y);
12835 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12836 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12837 element == EL_EXPANDABLE_STEELWALL_ANY)
12838 MauerAblegerStahl(x, y);
12839 else if (element == EL_FLAMES)
12840 CheckForDragon(x, y);
12841 else if (element == EL_EXPLOSION)
12842 ; /* drawing of correct explosion animation is handled separately */
12843 else if (element == EL_ELEMENT_SNAPPING ||
12844 element == EL_DIAGONAL_SHRINKING ||
12845 element == EL_DIAGONAL_GROWING)
12847 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12849 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12851 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12852 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12854 #endif // ---------------------------------------------------------------------
12856 if (IS_BELT_ACTIVE(element))
12857 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12859 if (game.magic_wall_active)
12861 int jx = local_player->jx, jy = local_player->jy;
12863 /* play the element sound at the position nearest to the player */
12864 if ((element == EL_MAGIC_WALL_FULL ||
12865 element == EL_MAGIC_WALL_ACTIVE ||
12866 element == EL_MAGIC_WALL_EMPTYING ||
12867 element == EL_BD_MAGIC_WALL_FULL ||
12868 element == EL_BD_MAGIC_WALL_ACTIVE ||
12869 element == EL_BD_MAGIC_WALL_EMPTYING ||
12870 element == EL_DC_MAGIC_WALL_FULL ||
12871 element == EL_DC_MAGIC_WALL_ACTIVE ||
12872 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12873 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12882 debug_print_timestamp(0, "- time for MAIN loop: -->");
12885 #if USE_NEW_AMOEBA_CODE
12886 /* new experimental amoeba growth stuff */
12887 if (!(FrameCounter % 8))
12889 static unsigned int random = 1684108901;
12891 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12893 x = RND(lev_fieldx);
12894 y = RND(lev_fieldy);
12895 element = Feld[x][y];
12897 if (!IS_PLAYER(x,y) &&
12898 (element == EL_EMPTY ||
12899 CAN_GROW_INTO(element) ||
12900 element == EL_QUICKSAND_EMPTY ||
12901 element == EL_QUICKSAND_FAST_EMPTY ||
12902 element == EL_ACID_SPLASH_LEFT ||
12903 element == EL_ACID_SPLASH_RIGHT))
12905 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12906 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12907 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12908 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12909 Feld[x][y] = EL_AMOEBA_DROP;
12912 random = random * 129 + 1;
12918 if (game.explosions_delayed)
12921 game.explosions_delayed = FALSE;
12923 SCAN_PLAYFIELD(x, y)
12925 element = Feld[x][y];
12927 if (ExplodeField[x][y])
12928 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12929 else if (element == EL_EXPLOSION)
12930 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12932 ExplodeField[x][y] = EX_TYPE_NONE;
12935 game.explosions_delayed = TRUE;
12938 if (game.magic_wall_active)
12940 if (!(game.magic_wall_time_left % 4))
12942 int element = Feld[magic_wall_x][magic_wall_y];
12944 if (element == EL_BD_MAGIC_WALL_FULL ||
12945 element == EL_BD_MAGIC_WALL_ACTIVE ||
12946 element == EL_BD_MAGIC_WALL_EMPTYING)
12947 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12948 else if (element == EL_DC_MAGIC_WALL_FULL ||
12949 element == EL_DC_MAGIC_WALL_ACTIVE ||
12950 element == EL_DC_MAGIC_WALL_EMPTYING)
12951 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12953 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12956 if (game.magic_wall_time_left > 0)
12958 game.magic_wall_time_left--;
12960 if (!game.magic_wall_time_left)
12962 SCAN_PLAYFIELD(x, y)
12964 element = Feld[x][y];
12966 if (element == EL_MAGIC_WALL_ACTIVE ||
12967 element == EL_MAGIC_WALL_FULL)
12969 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12970 TEST_DrawLevelField(x, y);
12972 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12973 element == EL_BD_MAGIC_WALL_FULL)
12975 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12976 TEST_DrawLevelField(x, y);
12978 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12979 element == EL_DC_MAGIC_WALL_FULL)
12981 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12982 TEST_DrawLevelField(x, y);
12986 game.magic_wall_active = FALSE;
12991 if (game.light_time_left > 0)
12993 game.light_time_left--;
12995 if (game.light_time_left == 0)
12996 RedrawAllLightSwitchesAndInvisibleElements();
12999 if (game.timegate_time_left > 0)
13001 game.timegate_time_left--;
13003 if (game.timegate_time_left == 0)
13004 CloseAllOpenTimegates();
13007 if (game.lenses_time_left > 0)
13009 game.lenses_time_left--;
13011 if (game.lenses_time_left == 0)
13012 RedrawAllInvisibleElementsForLenses();
13015 if (game.magnify_time_left > 0)
13017 game.magnify_time_left--;
13019 if (game.magnify_time_left == 0)
13020 RedrawAllInvisibleElementsForMagnifier();
13023 for (i = 0; i < MAX_PLAYERS; i++)
13025 struct PlayerInfo *player = &stored_player[i];
13027 if (SHIELD_ON(player))
13029 if (player->shield_deadly_time_left)
13030 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13031 else if (player->shield_normal_time_left)
13032 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13036 #if USE_DELAYED_GFX_REDRAW
13037 SCAN_PLAYFIELD(x, y)
13040 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13042 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13043 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13046 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13047 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13049 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13050 DrawLevelField(x, y);
13052 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13053 DrawLevelFieldCrumbled(x, y);
13055 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13056 DrawLevelFieldCrumbledNeighbours(x, y);
13058 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13059 DrawTwinkleOnField(x, y);
13062 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13069 PlayAllPlayersSound();
13071 if (options.debug) /* calculate frames per second */
13073 static unsigned int fps_counter = 0;
13074 static int fps_frames = 0;
13075 unsigned int fps_delay_ms = Counter() - fps_counter;
13079 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13081 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13084 fps_counter = Counter();
13087 redraw_mask |= REDRAW_FPS;
13090 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13092 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13094 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13096 local_player->show_envelope = 0;
13100 debug_print_timestamp(0, "stop main loop profiling ");
13101 printf("----------------------------------------------------------\n");
13104 /* use random number generator in every frame to make it less predictable */
13105 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13109 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13111 int min_x = x, min_y = y, max_x = x, max_y = y;
13114 for (i = 0; i < MAX_PLAYERS; i++)
13116 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13118 if (!stored_player[i].active || &stored_player[i] == player)
13121 min_x = MIN(min_x, jx);
13122 min_y = MIN(min_y, jy);
13123 max_x = MAX(max_x, jx);
13124 max_y = MAX(max_y, jy);
13127 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13130 static boolean AllPlayersInVisibleScreen()
13134 for (i = 0; i < MAX_PLAYERS; i++)
13136 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13138 if (!stored_player[i].active)
13141 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13148 void ScrollLevel(int dx, int dy)
13151 /* (directly solved in BlitBitmap() now) */
13152 static Bitmap *bitmap_db_field2 = NULL;
13153 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13160 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13161 /* only horizontal XOR vertical scroll direction allowed */
13162 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13167 /* (directly solved in BlitBitmap() now) */
13168 if (bitmap_db_field2 == NULL)
13169 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13171 /* needed when blitting directly to same bitmap -- should not be needed with
13172 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13173 BlitBitmap(drawto_field, bitmap_db_field2,
13174 FX + TILEX * (dx == -1) - softscroll_offset,
13175 FY + TILEY * (dy == -1) - softscroll_offset,
13176 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13177 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13178 FX + TILEX * (dx == 1) - softscroll_offset,
13179 FY + TILEY * (dy == 1) - softscroll_offset);
13180 BlitBitmap(bitmap_db_field2, drawto_field,
13181 FX + TILEX * (dx == 1) - softscroll_offset,
13182 FY + TILEY * (dy == 1) - softscroll_offset,
13183 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13184 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13185 FX + TILEX * (dx == 1) - softscroll_offset,
13186 FY + TILEY * (dy == 1) - softscroll_offset);
13191 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13192 int xsize = (BX2 - BX1 + 1);
13193 int ysize = (BY2 - BY1 + 1);
13194 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13195 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13196 int step = (start < end ? +1 : -1);
13198 for (i = start; i != end; i += step)
13200 BlitBitmap(drawto_field, drawto_field,
13201 FX + TILEX * (dx != 0 ? i + step : 0),
13202 FY + TILEY * (dy != 0 ? i + step : 0),
13203 TILEX * (dx != 0 ? 1 : xsize),
13204 TILEY * (dy != 0 ? 1 : ysize),
13205 FX + TILEX * (dx != 0 ? i : 0),
13206 FY + TILEY * (dy != 0 ? i : 0));
13213 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13215 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13219 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13221 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13226 BlitBitmap(drawto_field, drawto_field,
13227 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13228 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13229 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13230 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13231 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13232 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13234 BlitBitmap(drawto_field, drawto_field,
13235 FX + TILEX * (dx == -1) - softscroll_offset,
13236 FY + TILEY * (dy == -1) - softscroll_offset,
13237 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13238 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13239 FX + TILEX * (dx == 1) - softscroll_offset,
13240 FY + TILEY * (dy == 1) - softscroll_offset);
13248 x = (dx == 1 ? BX1 : BX2);
13249 for (y = BY1; y <= BY2; y++)
13250 DrawScreenField(x, y);
13255 y = (dy == 1 ? BY1 : BY2);
13256 for (x = BX1; x <= BX2; x++)
13257 DrawScreenField(x, y);
13260 redraw_mask |= REDRAW_FIELD;
13263 static boolean canFallDown(struct PlayerInfo *player)
13265 int jx = player->jx, jy = player->jy;
13267 return (IN_LEV_FIELD(jx, jy + 1) &&
13268 (IS_FREE(jx, jy + 1) ||
13269 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13270 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13271 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13274 static boolean canPassField(int x, int y, int move_dir)
13276 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13277 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13278 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13279 int nextx = x + dx;
13280 int nexty = y + dy;
13281 int element = Feld[x][y];
13283 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13284 !CAN_MOVE(element) &&
13285 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13286 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13287 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13290 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13292 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13293 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13294 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13298 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13299 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13300 (IS_DIGGABLE(Feld[newx][newy]) ||
13301 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13302 canPassField(newx, newy, move_dir)));
13305 static void CheckGravityMovement(struct PlayerInfo *player)
13307 #if USE_PLAYER_GRAVITY
13308 if (player->gravity && !player->programmed_action)
13310 if (game.gravity && !player->programmed_action)
13313 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13314 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13315 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13316 int jx = player->jx, jy = player->jy;
13317 boolean player_is_moving_to_valid_field =
13318 (!player_is_snapping &&
13319 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13320 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13321 boolean player_can_fall_down = canFallDown(player);
13323 if (player_can_fall_down &&
13324 !player_is_moving_to_valid_field)
13325 player->programmed_action = MV_DOWN;
13329 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13331 return CheckGravityMovement(player);
13333 #if USE_PLAYER_GRAVITY
13334 if (player->gravity && !player->programmed_action)
13336 if (game.gravity && !player->programmed_action)
13339 int jx = player->jx, jy = player->jy;
13340 boolean field_under_player_is_free =
13341 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13342 boolean player_is_standing_on_valid_field =
13343 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13344 (IS_WALKABLE(Feld[jx][jy]) &&
13345 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13347 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13348 player->programmed_action = MV_DOWN;
13353 MovePlayerOneStep()
13354 -----------------------------------------------------------------------------
13355 dx, dy: direction (non-diagonal) to try to move the player to
13356 real_dx, real_dy: direction as read from input device (can be diagonal)
13359 boolean MovePlayerOneStep(struct PlayerInfo *player,
13360 int dx, int dy, int real_dx, int real_dy)
13362 int jx = player->jx, jy = player->jy;
13363 int new_jx = jx + dx, new_jy = jy + dy;
13364 #if !USE_FIXED_DONT_RUN_INTO
13368 boolean player_can_move = !player->cannot_move;
13370 if (!player->active || (!dx && !dy))
13371 return MP_NO_ACTION;
13373 player->MovDir = (dx < 0 ? MV_LEFT :
13374 dx > 0 ? MV_RIGHT :
13376 dy > 0 ? MV_DOWN : MV_NONE);
13378 if (!IN_LEV_FIELD(new_jx, new_jy))
13379 return MP_NO_ACTION;
13381 if (!player_can_move)
13383 if (player->MovPos == 0)
13385 player->is_moving = FALSE;
13386 player->is_digging = FALSE;
13387 player->is_collecting = FALSE;
13388 player->is_snapping = FALSE;
13389 player->is_pushing = FALSE;
13394 if (!options.network && game.centered_player_nr == -1 &&
13395 !AllPlayersInSight(player, new_jx, new_jy))
13396 return MP_NO_ACTION;
13398 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13399 return MP_NO_ACTION;
13402 #if !USE_FIXED_DONT_RUN_INTO
13403 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13405 /* (moved to DigField()) */
13406 if (player_can_move && DONT_RUN_INTO(element))
13408 if (element == EL_ACID && dx == 0 && dy == 1)
13410 SplashAcid(new_jx, new_jy);
13411 Feld[jx][jy] = EL_PLAYER_1;
13412 InitMovingField(jx, jy, MV_DOWN);
13413 Store[jx][jy] = EL_ACID;
13414 ContinueMoving(jx, jy);
13415 BuryPlayer(player);
13418 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13424 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13425 if (can_move != MP_MOVING)
13428 /* check if DigField() has caused relocation of the player */
13429 if (player->jx != jx || player->jy != jy)
13430 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13432 StorePlayer[jx][jy] = 0;
13433 player->last_jx = jx;
13434 player->last_jy = jy;
13435 player->jx = new_jx;
13436 player->jy = new_jy;
13437 StorePlayer[new_jx][new_jy] = player->element_nr;
13439 if (player->move_delay_value_next != -1)
13441 player->move_delay_value = player->move_delay_value_next;
13442 player->move_delay_value_next = -1;
13446 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13448 player->step_counter++;
13450 PlayerVisit[jx][jy] = FrameCounter;
13452 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13453 player->is_moving = TRUE;
13457 /* should better be called in MovePlayer(), but this breaks some tapes */
13458 ScrollPlayer(player, SCROLL_INIT);
13464 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13466 int jx = player->jx, jy = player->jy;
13467 int old_jx = jx, old_jy = jy;
13468 int moved = MP_NO_ACTION;
13470 if (!player->active)
13475 if (player->MovPos == 0)
13477 player->is_moving = FALSE;
13478 player->is_digging = FALSE;
13479 player->is_collecting = FALSE;
13480 player->is_snapping = FALSE;
13481 player->is_pushing = FALSE;
13487 if (player->move_delay > 0)
13490 player->move_delay = -1; /* set to "uninitialized" value */
13492 /* store if player is automatically moved to next field */
13493 player->is_auto_moving = (player->programmed_action != MV_NONE);
13495 /* remove the last programmed player action */
13496 player->programmed_action = 0;
13498 if (player->MovPos)
13500 /* should only happen if pre-1.2 tape recordings are played */
13501 /* this is only for backward compatibility */
13503 int original_move_delay_value = player->move_delay_value;
13506 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13510 /* scroll remaining steps with finest movement resolution */
13511 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13513 while (player->MovPos)
13515 ScrollPlayer(player, SCROLL_GO_ON);
13516 ScrollScreen(NULL, SCROLL_GO_ON);
13518 AdvanceFrameAndPlayerCounters(player->index_nr);
13524 player->move_delay_value = original_move_delay_value;
13527 player->is_active = FALSE;
13529 if (player->last_move_dir & MV_HORIZONTAL)
13531 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13532 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13536 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13537 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13540 #if USE_FIXED_BORDER_RUNNING_GFX
13541 if (!moved && !player->is_active)
13543 player->is_moving = FALSE;
13544 player->is_digging = FALSE;
13545 player->is_collecting = FALSE;
13546 player->is_snapping = FALSE;
13547 player->is_pushing = FALSE;
13555 if (moved & MP_MOVING && !ScreenMovPos &&
13556 (player->index_nr == game.centered_player_nr ||
13557 game.centered_player_nr == -1))
13559 if (moved & MP_MOVING && !ScreenMovPos &&
13560 (player == local_player || !options.network))
13563 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13564 int offset = game.scroll_delay_value;
13566 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13568 /* actual player has left the screen -- scroll in that direction */
13569 if (jx != old_jx) /* player has moved horizontally */
13570 scroll_x += (jx - old_jx);
13571 else /* player has moved vertically */
13572 scroll_y += (jy - old_jy);
13576 if (jx != old_jx) /* player has moved horizontally */
13578 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13579 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13580 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13582 /* don't scroll over playfield boundaries */
13583 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13584 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13586 /* don't scroll more than one field at a time */
13587 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13589 /* don't scroll against the player's moving direction */
13590 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13591 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13592 scroll_x = old_scroll_x;
13594 else /* player has moved vertically */
13596 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13597 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13598 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13600 /* don't scroll over playfield boundaries */
13601 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13602 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13604 /* don't scroll more than one field at a time */
13605 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13607 /* don't scroll against the player's moving direction */
13608 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13609 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13610 scroll_y = old_scroll_y;
13614 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13617 if (!options.network && game.centered_player_nr == -1 &&
13618 !AllPlayersInVisibleScreen())
13620 scroll_x = old_scroll_x;
13621 scroll_y = old_scroll_y;
13625 if (!options.network && !AllPlayersInVisibleScreen())
13627 scroll_x = old_scroll_x;
13628 scroll_y = old_scroll_y;
13633 ScrollScreen(player, SCROLL_INIT);
13634 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13639 player->StepFrame = 0;
13641 if (moved & MP_MOVING)
13643 if (old_jx != jx && old_jy == jy)
13644 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13645 else if (old_jx == jx && old_jy != jy)
13646 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13648 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13650 player->last_move_dir = player->MovDir;
13651 player->is_moving = TRUE;
13652 player->is_snapping = FALSE;
13653 player->is_switching = FALSE;
13654 player->is_dropping = FALSE;
13655 player->is_dropping_pressed = FALSE;
13656 player->drop_pressed_delay = 0;
13659 /* should better be called here than above, but this breaks some tapes */
13660 ScrollPlayer(player, SCROLL_INIT);
13665 CheckGravityMovementWhenNotMoving(player);
13667 player->is_moving = FALSE;
13669 /* at this point, the player is allowed to move, but cannot move right now
13670 (e.g. because of something blocking the way) -- ensure that the player
13671 is also allowed to move in the next frame (in old versions before 3.1.1,
13672 the player was forced to wait again for eight frames before next try) */
13674 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13675 player->move_delay = 0; /* allow direct movement in the next frame */
13678 if (player->move_delay == -1) /* not yet initialized by DigField() */
13679 player->move_delay = player->move_delay_value;
13681 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13683 TestIfPlayerTouchesBadThing(jx, jy);
13684 TestIfPlayerTouchesCustomElement(jx, jy);
13687 if (!player->active)
13688 RemovePlayer(player);
13693 void ScrollPlayer(struct PlayerInfo *player, int mode)
13695 int jx = player->jx, jy = player->jy;
13696 int last_jx = player->last_jx, last_jy = player->last_jy;
13697 int move_stepsize = TILEX / player->move_delay_value;
13699 #if USE_NEW_PLAYER_SPEED
13700 if (!player->active)
13703 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13706 if (!player->active || player->MovPos == 0)
13710 if (mode == SCROLL_INIT)
13712 player->actual_frame_counter = FrameCounter;
13713 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13715 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13716 Feld[last_jx][last_jy] == EL_EMPTY)
13718 int last_field_block_delay = 0; /* start with no blocking at all */
13719 int block_delay_adjustment = player->block_delay_adjustment;
13721 /* if player blocks last field, add delay for exactly one move */
13722 if (player->block_last_field)
13724 last_field_block_delay += player->move_delay_value;
13726 /* when blocking enabled, prevent moving up despite gravity */
13727 #if USE_PLAYER_GRAVITY
13728 if (player->gravity && player->MovDir == MV_UP)
13729 block_delay_adjustment = -1;
13731 if (game.gravity && player->MovDir == MV_UP)
13732 block_delay_adjustment = -1;
13736 /* add block delay adjustment (also possible when not blocking) */
13737 last_field_block_delay += block_delay_adjustment;
13739 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13740 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13743 #if USE_NEW_PLAYER_SPEED
13744 if (player->MovPos != 0) /* player has not yet reached destination */
13750 else if (!FrameReached(&player->actual_frame_counter, 1))
13753 #if USE_NEW_PLAYER_SPEED
13754 if (player->MovPos != 0)
13756 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13757 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13759 /* before DrawPlayer() to draw correct player graphic for this case */
13760 if (player->MovPos == 0)
13761 CheckGravityMovement(player);
13764 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13765 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13767 /* before DrawPlayer() to draw correct player graphic for this case */
13768 if (player->MovPos == 0)
13769 CheckGravityMovement(player);
13772 if (player->MovPos == 0) /* player reached destination field */
13774 if (player->move_delay_reset_counter > 0)
13776 player->move_delay_reset_counter--;
13778 if (player->move_delay_reset_counter == 0)
13780 /* continue with normal speed after quickly moving through gate */
13781 HALVE_PLAYER_SPEED(player);
13783 /* be able to make the next move without delay */
13784 player->move_delay = 0;
13788 player->last_jx = jx;
13789 player->last_jy = jy;
13791 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13792 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13794 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13796 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13797 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13799 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13801 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13802 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13804 DrawPlayer(player); /* needed here only to cleanup last field */
13805 RemovePlayer(player);
13807 if (local_player->friends_still_needed == 0 ||
13808 IS_SP_ELEMENT(Feld[jx][jy]))
13809 PlayerWins(player);
13812 /* this breaks one level: "machine", level 000 */
13814 int move_direction = player->MovDir;
13815 int enter_side = MV_DIR_OPPOSITE(move_direction);
13816 int leave_side = move_direction;
13817 int old_jx = last_jx;
13818 int old_jy = last_jy;
13819 int old_element = Feld[old_jx][old_jy];
13820 int new_element = Feld[jx][jy];
13822 if (IS_CUSTOM_ELEMENT(old_element))
13823 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13825 player->index_bit, leave_side);
13827 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13828 CE_PLAYER_LEAVES_X,
13829 player->index_bit, leave_side);
13831 if (IS_CUSTOM_ELEMENT(new_element))
13832 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13833 player->index_bit, enter_side);
13835 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13836 CE_PLAYER_ENTERS_X,
13837 player->index_bit, enter_side);
13839 #if USE_FIX_CE_ACTION_WITH_PLAYER
13840 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13841 CE_MOVE_OF_X, move_direction);
13843 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13844 CE_MOVE_OF_X, move_direction);
13848 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13850 TestIfPlayerTouchesBadThing(jx, jy);
13851 TestIfPlayerTouchesCustomElement(jx, jy);
13853 /* needed because pushed element has not yet reached its destination,
13854 so it would trigger a change event at its previous field location */
13855 if (!player->is_pushing)
13856 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13858 if (!player->active)
13859 RemovePlayer(player);
13862 if (!local_player->LevelSolved && level.use_step_counter)
13872 if (TimeLeft <= 10 && setup.time_limit)
13873 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13876 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13878 DisplayGameControlValues();
13880 DrawGameValue_Time(TimeLeft);
13883 if (!TimeLeft && setup.time_limit)
13884 for (i = 0; i < MAX_PLAYERS; i++)
13885 KillPlayer(&stored_player[i]);
13888 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13890 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13892 DisplayGameControlValues();
13895 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13896 DrawGameValue_Time(TimePlayed);
13900 if (tape.single_step && tape.recording && !tape.pausing &&
13901 !player->programmed_action)
13902 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13906 void ScrollScreen(struct PlayerInfo *player, int mode)
13908 static unsigned int screen_frame_counter = 0;
13910 if (mode == SCROLL_INIT)
13912 /* set scrolling step size according to actual player's moving speed */
13913 ScrollStepSize = TILEX / player->move_delay_value;
13915 screen_frame_counter = FrameCounter;
13916 ScreenMovDir = player->MovDir;
13917 ScreenMovPos = player->MovPos;
13918 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13921 else if (!FrameReached(&screen_frame_counter, 1))
13926 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13927 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13928 redraw_mask |= REDRAW_FIELD;
13931 ScreenMovDir = MV_NONE;
13934 void TestIfPlayerTouchesCustomElement(int x, int y)
13936 static int xy[4][2] =
13943 static int trigger_sides[4][2] =
13945 /* center side border side */
13946 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13947 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13948 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13949 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13951 static int touch_dir[4] =
13953 MV_LEFT | MV_RIGHT,
13958 int center_element = Feld[x][y]; /* should always be non-moving! */
13961 for (i = 0; i < NUM_DIRECTIONS; i++)
13963 int xx = x + xy[i][0];
13964 int yy = y + xy[i][1];
13965 int center_side = trigger_sides[i][0];
13966 int border_side = trigger_sides[i][1];
13967 int border_element;
13969 if (!IN_LEV_FIELD(xx, yy))
13972 if (IS_PLAYER(x, y)) /* player found at center element */
13974 struct PlayerInfo *player = PLAYERINFO(x, y);
13976 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13977 border_element = Feld[xx][yy]; /* may be moving! */
13978 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13979 border_element = Feld[xx][yy];
13980 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13981 border_element = MovingOrBlocked2Element(xx, yy);
13983 continue; /* center and border element do not touch */
13985 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13986 player->index_bit, border_side);
13987 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13988 CE_PLAYER_TOUCHES_X,
13989 player->index_bit, border_side);
13991 #if USE_FIX_CE_ACTION_WITH_PLAYER
13993 /* use player element that is initially defined in the level playfield,
13994 not the player element that corresponds to the runtime player number
13995 (example: a level that contains EL_PLAYER_3 as the only player would
13996 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13997 int player_element = PLAYERINFO(x, y)->initial_element;
13999 CheckElementChangeBySide(xx, yy, border_element, player_element,
14000 CE_TOUCHING_X, border_side);
14004 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14006 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14008 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14010 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14011 continue; /* center and border element do not touch */
14014 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14015 player->index_bit, center_side);
14016 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14017 CE_PLAYER_TOUCHES_X,
14018 player->index_bit, center_side);
14020 #if USE_FIX_CE_ACTION_WITH_PLAYER
14022 /* use player element that is initially defined in the level playfield,
14023 not the player element that corresponds to the runtime player number
14024 (example: a level that contains EL_PLAYER_3 as the only player would
14025 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14026 int player_element = PLAYERINFO(xx, yy)->initial_element;
14028 CheckElementChangeBySide(x, y, center_element, player_element,
14029 CE_TOUCHING_X, center_side);
14038 #if USE_ELEMENT_TOUCHING_BUGFIX
14040 void TestIfElementTouchesCustomElement(int x, int y)
14042 static int xy[4][2] =
14049 static int trigger_sides[4][2] =
14051 /* center side border side */
14052 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14053 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14054 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14055 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14057 static int touch_dir[4] =
14059 MV_LEFT | MV_RIGHT,
14064 boolean change_center_element = FALSE;
14065 int center_element = Feld[x][y]; /* should always be non-moving! */
14066 int border_element_old[NUM_DIRECTIONS];
14069 for (i = 0; i < NUM_DIRECTIONS; i++)
14071 int xx = x + xy[i][0];
14072 int yy = y + xy[i][1];
14073 int border_element;
14075 border_element_old[i] = -1;
14077 if (!IN_LEV_FIELD(xx, yy))
14080 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14081 border_element = Feld[xx][yy]; /* may be moving! */
14082 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14083 border_element = Feld[xx][yy];
14084 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14085 border_element = MovingOrBlocked2Element(xx, yy);
14087 continue; /* center and border element do not touch */
14089 border_element_old[i] = border_element;
14092 for (i = 0; i < NUM_DIRECTIONS; i++)
14094 int xx = x + xy[i][0];
14095 int yy = y + xy[i][1];
14096 int center_side = trigger_sides[i][0];
14097 int border_element = border_element_old[i];
14099 if (border_element == -1)
14102 /* check for change of border element */
14103 CheckElementChangeBySide(xx, yy, border_element, center_element,
14104 CE_TOUCHING_X, center_side);
14106 /* (center element cannot be player, so we dont have to check this here) */
14109 for (i = 0; i < NUM_DIRECTIONS; i++)
14111 int xx = x + xy[i][0];
14112 int yy = y + xy[i][1];
14113 int border_side = trigger_sides[i][1];
14114 int border_element = border_element_old[i];
14116 if (border_element == -1)
14119 /* check for change of center element (but change it only once) */
14120 if (!change_center_element)
14121 change_center_element =
14122 CheckElementChangeBySide(x, y, center_element, border_element,
14123 CE_TOUCHING_X, border_side);
14125 #if USE_FIX_CE_ACTION_WITH_PLAYER
14126 if (IS_PLAYER(xx, yy))
14128 /* use player element that is initially defined in the level playfield,
14129 not the player element that corresponds to the runtime player number
14130 (example: a level that contains EL_PLAYER_3 as the only player would
14131 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14132 int player_element = PLAYERINFO(xx, yy)->initial_element;
14134 CheckElementChangeBySide(x, y, center_element, player_element,
14135 CE_TOUCHING_X, border_side);
14143 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14145 static int xy[4][2] =
14152 static int trigger_sides[4][2] =
14154 /* center side border side */
14155 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14156 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14157 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14158 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14160 static int touch_dir[4] =
14162 MV_LEFT | MV_RIGHT,
14167 boolean change_center_element = FALSE;
14168 int center_element = Feld[x][y]; /* should always be non-moving! */
14171 for (i = 0; i < NUM_DIRECTIONS; i++)
14173 int xx = x + xy[i][0];
14174 int yy = y + xy[i][1];
14175 int center_side = trigger_sides[i][0];
14176 int border_side = trigger_sides[i][1];
14177 int border_element;
14179 if (!IN_LEV_FIELD(xx, yy))
14182 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14183 border_element = Feld[xx][yy]; /* may be moving! */
14184 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14185 border_element = Feld[xx][yy];
14186 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14187 border_element = MovingOrBlocked2Element(xx, yy);
14189 continue; /* center and border element do not touch */
14191 /* check for change of center element (but change it only once) */
14192 if (!change_center_element)
14193 change_center_element =
14194 CheckElementChangeBySide(x, y, center_element, border_element,
14195 CE_TOUCHING_X, border_side);
14197 /* check for change of border element */
14198 CheckElementChangeBySide(xx, yy, border_element, center_element,
14199 CE_TOUCHING_X, center_side);
14205 void TestIfElementHitsCustomElement(int x, int y, int direction)
14207 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14208 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14209 int hitx = x + dx, hity = y + dy;
14210 int hitting_element = Feld[x][y];
14211 int touched_element;
14213 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14216 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14217 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14219 if (IN_LEV_FIELD(hitx, hity))
14221 int opposite_direction = MV_DIR_OPPOSITE(direction);
14222 int hitting_side = direction;
14223 int touched_side = opposite_direction;
14224 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14225 MovDir[hitx][hity] != direction ||
14226 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14232 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14233 CE_HITTING_X, touched_side);
14235 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14236 CE_HIT_BY_X, hitting_side);
14238 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14239 CE_HIT_BY_SOMETHING, opposite_direction);
14241 #if USE_FIX_CE_ACTION_WITH_PLAYER
14242 if (IS_PLAYER(hitx, hity))
14244 /* use player element that is initially defined in the level playfield,
14245 not the player element that corresponds to the runtime player number
14246 (example: a level that contains EL_PLAYER_3 as the only player would
14247 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14248 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14250 CheckElementChangeBySide(x, y, hitting_element, player_element,
14251 CE_HITTING_X, touched_side);
14257 /* "hitting something" is also true when hitting the playfield border */
14258 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14259 CE_HITTING_SOMETHING, direction);
14263 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14265 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14266 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14267 int hitx = x + dx, hity = y + dy;
14268 int hitting_element = Feld[x][y];
14269 int touched_element;
14271 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14272 !IS_FREE(hitx, hity) &&
14273 (!IS_MOVING(hitx, hity) ||
14274 MovDir[hitx][hity] != direction ||
14275 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14278 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14282 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14286 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14287 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14289 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14290 EP_CAN_SMASH_EVERYTHING, direction);
14292 if (IN_LEV_FIELD(hitx, hity))
14294 int opposite_direction = MV_DIR_OPPOSITE(direction);
14295 int hitting_side = direction;
14296 int touched_side = opposite_direction;
14298 int touched_element = MovingOrBlocked2Element(hitx, hity);
14301 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14302 MovDir[hitx][hity] != direction ||
14303 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14312 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14313 CE_SMASHED_BY_SOMETHING, opposite_direction);
14315 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14316 CE_OTHER_IS_SMASHING, touched_side);
14318 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14319 CE_OTHER_GETS_SMASHED, hitting_side);
14325 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14327 int i, kill_x = -1, kill_y = -1;
14329 int bad_element = -1;
14330 static int test_xy[4][2] =
14337 static int test_dir[4] =
14345 for (i = 0; i < NUM_DIRECTIONS; i++)
14347 int test_x, test_y, test_move_dir, test_element;
14349 test_x = good_x + test_xy[i][0];
14350 test_y = good_y + test_xy[i][1];
14352 if (!IN_LEV_FIELD(test_x, test_y))
14356 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14358 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14360 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14361 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14363 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14364 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14368 bad_element = test_element;
14374 if (kill_x != -1 || kill_y != -1)
14376 if (IS_PLAYER(good_x, good_y))
14378 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14380 if (player->shield_deadly_time_left > 0 &&
14381 !IS_INDESTRUCTIBLE(bad_element))
14382 Bang(kill_x, kill_y);
14383 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14384 KillPlayer(player);
14387 Bang(good_x, good_y);
14391 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14393 int i, kill_x = -1, kill_y = -1;
14394 int bad_element = Feld[bad_x][bad_y];
14395 static int test_xy[4][2] =
14402 static int touch_dir[4] =
14404 MV_LEFT | MV_RIGHT,
14409 static int test_dir[4] =
14417 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14420 for (i = 0; i < NUM_DIRECTIONS; i++)
14422 int test_x, test_y, test_move_dir, test_element;
14424 test_x = bad_x + test_xy[i][0];
14425 test_y = bad_y + test_xy[i][1];
14427 if (!IN_LEV_FIELD(test_x, test_y))
14431 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14433 test_element = Feld[test_x][test_y];
14435 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14436 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14438 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14439 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14441 /* good thing is player or penguin that does not move away */
14442 if (IS_PLAYER(test_x, test_y))
14444 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14446 if (bad_element == EL_ROBOT && player->is_moving)
14447 continue; /* robot does not kill player if he is moving */
14449 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14451 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14452 continue; /* center and border element do not touch */
14460 else if (test_element == EL_PENGUIN)
14470 if (kill_x != -1 || kill_y != -1)
14472 if (IS_PLAYER(kill_x, kill_y))
14474 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14476 if (player->shield_deadly_time_left > 0 &&
14477 !IS_INDESTRUCTIBLE(bad_element))
14478 Bang(bad_x, bad_y);
14479 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14480 KillPlayer(player);
14483 Bang(kill_x, kill_y);
14487 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14489 int bad_element = Feld[bad_x][bad_y];
14490 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14491 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14492 int test_x = bad_x + dx, test_y = bad_y + dy;
14493 int test_move_dir, test_element;
14494 int kill_x = -1, kill_y = -1;
14496 if (!IN_LEV_FIELD(test_x, test_y))
14500 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14502 test_element = Feld[test_x][test_y];
14504 if (test_move_dir != bad_move_dir)
14506 /* good thing can be player or penguin that does not move away */
14507 if (IS_PLAYER(test_x, test_y))
14509 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14511 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14512 player as being hit when he is moving towards the bad thing, because
14513 the "get hit by" condition would be lost after the player stops) */
14514 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14515 return; /* player moves away from bad thing */
14520 else if (test_element == EL_PENGUIN)
14527 if (kill_x != -1 || kill_y != -1)
14529 if (IS_PLAYER(kill_x, kill_y))
14531 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14533 if (player->shield_deadly_time_left > 0 &&
14534 !IS_INDESTRUCTIBLE(bad_element))
14535 Bang(bad_x, bad_y);
14536 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14537 KillPlayer(player);
14540 Bang(kill_x, kill_y);
14544 void TestIfPlayerTouchesBadThing(int x, int y)
14546 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14549 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14551 TestIfGoodThingHitsBadThing(x, y, move_dir);
14554 void TestIfBadThingTouchesPlayer(int x, int y)
14556 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14559 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14561 TestIfBadThingHitsGoodThing(x, y, move_dir);
14564 void TestIfFriendTouchesBadThing(int x, int y)
14566 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14569 void TestIfBadThingTouchesFriend(int x, int y)
14571 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14574 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14576 int i, kill_x = bad_x, kill_y = bad_y;
14577 static int xy[4][2] =
14585 for (i = 0; i < NUM_DIRECTIONS; i++)
14589 x = bad_x + xy[i][0];
14590 y = bad_y + xy[i][1];
14591 if (!IN_LEV_FIELD(x, y))
14594 element = Feld[x][y];
14595 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14596 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14604 if (kill_x != bad_x || kill_y != bad_y)
14605 Bang(bad_x, bad_y);
14608 void KillPlayer(struct PlayerInfo *player)
14610 int jx = player->jx, jy = player->jy;
14612 if (!player->active)
14616 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14617 player->killed, player->active, player->reanimated);
14620 /* the following code was introduced to prevent an infinite loop when calling
14622 -> CheckTriggeredElementChangeExt()
14623 -> ExecuteCustomElementAction()
14625 -> (infinitely repeating the above sequence of function calls)
14626 which occurs when killing the player while having a CE with the setting
14627 "kill player X when explosion of <player X>"; the solution using a new
14628 field "player->killed" was chosen for backwards compatibility, although
14629 clever use of the fields "player->active" etc. would probably also work */
14631 if (player->killed)
14635 player->killed = TRUE;
14637 /* remove accessible field at the player's position */
14638 Feld[jx][jy] = EL_EMPTY;
14640 /* deactivate shield (else Bang()/Explode() would not work right) */
14641 player->shield_normal_time_left = 0;
14642 player->shield_deadly_time_left = 0;
14645 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14646 player->killed, player->active, player->reanimated);
14652 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14653 player->killed, player->active, player->reanimated);
14656 #if USE_PLAYER_REANIMATION
14658 if (player->reanimated) /* killed player may have been reanimated */
14659 player->killed = player->reanimated = FALSE;
14661 BuryPlayer(player);
14663 if (player->killed) /* player may have been reanimated */
14664 BuryPlayer(player);
14667 BuryPlayer(player);
14671 static void KillPlayerUnlessEnemyProtected(int x, int y)
14673 if (!PLAYER_ENEMY_PROTECTED(x, y))
14674 KillPlayer(PLAYERINFO(x, y));
14677 static void KillPlayerUnlessExplosionProtected(int x, int y)
14679 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14680 KillPlayer(PLAYERINFO(x, y));
14683 void BuryPlayer(struct PlayerInfo *player)
14685 int jx = player->jx, jy = player->jy;
14687 if (!player->active)
14690 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14691 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14693 player->GameOver = TRUE;
14694 RemovePlayer(player);
14697 void RemovePlayer(struct PlayerInfo *player)
14699 int jx = player->jx, jy = player->jy;
14700 int i, found = FALSE;
14702 player->present = FALSE;
14703 player->active = FALSE;
14705 if (!ExplodeField[jx][jy])
14706 StorePlayer[jx][jy] = 0;
14708 if (player->is_moving)
14709 TEST_DrawLevelField(player->last_jx, player->last_jy);
14711 for (i = 0; i < MAX_PLAYERS; i++)
14712 if (stored_player[i].active)
14716 AllPlayersGone = TRUE;
14722 #if USE_NEW_SNAP_DELAY
14723 static void setFieldForSnapping(int x, int y, int element, int direction)
14725 struct ElementInfo *ei = &element_info[element];
14726 int direction_bit = MV_DIR_TO_BIT(direction);
14727 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14728 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14729 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14731 Feld[x][y] = EL_ELEMENT_SNAPPING;
14732 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14734 ResetGfxAnimation(x, y);
14736 GfxElement[x][y] = element;
14737 GfxAction[x][y] = action;
14738 GfxDir[x][y] = direction;
14739 GfxFrame[x][y] = -1;
14744 =============================================================================
14745 checkDiagonalPushing()
14746 -----------------------------------------------------------------------------
14747 check if diagonal input device direction results in pushing of object
14748 (by checking if the alternative direction is walkable, diggable, ...)
14749 =============================================================================
14752 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14753 int x, int y, int real_dx, int real_dy)
14755 int jx, jy, dx, dy, xx, yy;
14757 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14760 /* diagonal direction: check alternative direction */
14765 xx = jx + (dx == 0 ? real_dx : 0);
14766 yy = jy + (dy == 0 ? real_dy : 0);
14768 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14772 =============================================================================
14774 -----------------------------------------------------------------------------
14775 x, y: field next to player (non-diagonal) to try to dig to
14776 real_dx, real_dy: direction as read from input device (can be diagonal)
14777 =============================================================================
14780 static int DigField(struct PlayerInfo *player,
14781 int oldx, int oldy, int x, int y,
14782 int real_dx, int real_dy, int mode)
14784 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14785 boolean player_was_pushing = player->is_pushing;
14786 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14787 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14788 int jx = oldx, jy = oldy;
14789 int dx = x - jx, dy = y - jy;
14790 int nextx = x + dx, nexty = y + dy;
14791 int move_direction = (dx == -1 ? MV_LEFT :
14792 dx == +1 ? MV_RIGHT :
14794 dy == +1 ? MV_DOWN : MV_NONE);
14795 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14796 int dig_side = MV_DIR_OPPOSITE(move_direction);
14797 int old_element = Feld[jx][jy];
14798 #if USE_FIXED_DONT_RUN_INTO
14799 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14805 if (is_player) /* function can also be called by EL_PENGUIN */
14807 if (player->MovPos == 0)
14809 player->is_digging = FALSE;
14810 player->is_collecting = FALSE;
14813 if (player->MovPos == 0) /* last pushing move finished */
14814 player->is_pushing = FALSE;
14816 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14818 player->is_switching = FALSE;
14819 player->push_delay = -1;
14821 return MP_NO_ACTION;
14825 #if !USE_FIXED_DONT_RUN_INTO
14826 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14827 return MP_NO_ACTION;
14830 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14831 old_element = Back[jx][jy];
14833 /* in case of element dropped at player position, check background */
14834 else if (Back[jx][jy] != EL_EMPTY &&
14835 game.engine_version >= VERSION_IDENT(2,2,0,0))
14836 old_element = Back[jx][jy];
14838 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14839 return MP_NO_ACTION; /* field has no opening in this direction */
14841 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14842 return MP_NO_ACTION; /* field has no opening in this direction */
14844 #if USE_FIXED_DONT_RUN_INTO
14845 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14849 Feld[jx][jy] = player->artwork_element;
14850 InitMovingField(jx, jy, MV_DOWN);
14851 Store[jx][jy] = EL_ACID;
14852 ContinueMoving(jx, jy);
14853 BuryPlayer(player);
14855 return MP_DONT_RUN_INTO;
14859 #if USE_FIXED_DONT_RUN_INTO
14860 if (player_can_move && DONT_RUN_INTO(element))
14862 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14864 return MP_DONT_RUN_INTO;
14868 #if USE_FIXED_DONT_RUN_INTO
14869 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14870 return MP_NO_ACTION;
14873 #if !USE_FIXED_DONT_RUN_INTO
14874 element = Feld[x][y];
14877 collect_count = element_info[element].collect_count_initial;
14879 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14880 return MP_NO_ACTION;
14882 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14883 player_can_move = player_can_move_or_snap;
14885 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14886 game.engine_version >= VERSION_IDENT(2,2,0,0))
14888 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14889 player->index_bit, dig_side);
14890 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14891 player->index_bit, dig_side);
14893 if (element == EL_DC_LANDMINE)
14896 if (Feld[x][y] != element) /* field changed by snapping */
14899 return MP_NO_ACTION;
14902 #if USE_PLAYER_GRAVITY
14903 if (player->gravity && is_player && !player->is_auto_moving &&
14904 canFallDown(player) && move_direction != MV_DOWN &&
14905 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14906 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14908 if (game.gravity && is_player && !player->is_auto_moving &&
14909 canFallDown(player) && move_direction != MV_DOWN &&
14910 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14911 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14914 if (player_can_move &&
14915 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14917 int sound_element = SND_ELEMENT(element);
14918 int sound_action = ACTION_WALKING;
14920 if (IS_RND_GATE(element))
14922 if (!player->key[RND_GATE_NR(element)])
14923 return MP_NO_ACTION;
14925 else if (IS_RND_GATE_GRAY(element))
14927 if (!player->key[RND_GATE_GRAY_NR(element)])
14928 return MP_NO_ACTION;
14930 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14932 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14933 return MP_NO_ACTION;
14935 else if (element == EL_EXIT_OPEN ||
14936 element == EL_EM_EXIT_OPEN ||
14938 element == EL_EM_EXIT_OPENING ||
14940 element == EL_STEEL_EXIT_OPEN ||
14941 element == EL_EM_STEEL_EXIT_OPEN ||
14943 element == EL_EM_STEEL_EXIT_OPENING ||
14945 element == EL_SP_EXIT_OPEN ||
14946 element == EL_SP_EXIT_OPENING)
14948 sound_action = ACTION_PASSING; /* player is passing exit */
14950 else if (element == EL_EMPTY)
14952 sound_action = ACTION_MOVING; /* nothing to walk on */
14955 /* play sound from background or player, whatever is available */
14956 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14957 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14959 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14961 else if (player_can_move &&
14962 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14964 if (!ACCESS_FROM(element, opposite_direction))
14965 return MP_NO_ACTION; /* field not accessible from this direction */
14967 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14968 return MP_NO_ACTION;
14970 if (IS_EM_GATE(element))
14972 if (!player->key[EM_GATE_NR(element)])
14973 return MP_NO_ACTION;
14975 else if (IS_EM_GATE_GRAY(element))
14977 if (!player->key[EM_GATE_GRAY_NR(element)])
14978 return MP_NO_ACTION;
14980 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14982 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14983 return MP_NO_ACTION;
14985 else if (IS_EMC_GATE(element))
14987 if (!player->key[EMC_GATE_NR(element)])
14988 return MP_NO_ACTION;
14990 else if (IS_EMC_GATE_GRAY(element))
14992 if (!player->key[EMC_GATE_GRAY_NR(element)])
14993 return MP_NO_ACTION;
14995 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14997 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14998 return MP_NO_ACTION;
15000 else if (element == EL_DC_GATE_WHITE ||
15001 element == EL_DC_GATE_WHITE_GRAY ||
15002 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15004 if (player->num_white_keys == 0)
15005 return MP_NO_ACTION;
15007 player->num_white_keys--;
15009 else if (IS_SP_PORT(element))
15011 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15012 element == EL_SP_GRAVITY_PORT_RIGHT ||
15013 element == EL_SP_GRAVITY_PORT_UP ||
15014 element == EL_SP_GRAVITY_PORT_DOWN)
15015 #if USE_PLAYER_GRAVITY
15016 player->gravity = !player->gravity;
15018 game.gravity = !game.gravity;
15020 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15021 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15022 element == EL_SP_GRAVITY_ON_PORT_UP ||
15023 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15024 #if USE_PLAYER_GRAVITY
15025 player->gravity = TRUE;
15027 game.gravity = TRUE;
15029 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15030 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15031 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15032 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15033 #if USE_PLAYER_GRAVITY
15034 player->gravity = FALSE;
15036 game.gravity = FALSE;
15040 /* automatically move to the next field with double speed */
15041 player->programmed_action = move_direction;
15043 if (player->move_delay_reset_counter == 0)
15045 player->move_delay_reset_counter = 2; /* two double speed steps */
15047 DOUBLE_PLAYER_SPEED(player);
15050 PlayLevelSoundAction(x, y, ACTION_PASSING);
15052 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15056 if (mode != DF_SNAP)
15058 GfxElement[x][y] = GFX_ELEMENT(element);
15059 player->is_digging = TRUE;
15062 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15064 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15065 player->index_bit, dig_side);
15067 if (mode == DF_SNAP)
15069 #if USE_NEW_SNAP_DELAY
15070 if (level.block_snap_field)
15071 setFieldForSnapping(x, y, element, move_direction);
15073 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15075 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15078 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15079 player->index_bit, dig_side);
15082 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15086 if (is_player && mode != DF_SNAP)
15088 GfxElement[x][y] = element;
15089 player->is_collecting = TRUE;
15092 if (element == EL_SPEED_PILL)
15094 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15096 else if (element == EL_EXTRA_TIME && level.time > 0)
15098 TimeLeft += level.extra_time;
15101 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15103 DisplayGameControlValues();
15105 DrawGameValue_Time(TimeLeft);
15108 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15110 player->shield_normal_time_left += level.shield_normal_time;
15111 if (element == EL_SHIELD_DEADLY)
15112 player->shield_deadly_time_left += level.shield_deadly_time;
15114 else if (element == EL_DYNAMITE ||
15115 element == EL_EM_DYNAMITE ||
15116 element == EL_SP_DISK_RED)
15118 if (player->inventory_size < MAX_INVENTORY_SIZE)
15119 player->inventory_element[player->inventory_size++] = element;
15121 DrawGameDoorValues();
15123 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15125 player->dynabomb_count++;
15126 player->dynabombs_left++;
15128 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15130 player->dynabomb_size++;
15132 else if (element == EL_DYNABOMB_INCREASE_POWER)
15134 player->dynabomb_xl = TRUE;
15136 else if (IS_KEY(element))
15138 player->key[KEY_NR(element)] = TRUE;
15140 DrawGameDoorValues();
15142 else if (element == EL_DC_KEY_WHITE)
15144 player->num_white_keys++;
15146 /* display white keys? */
15147 /* DrawGameDoorValues(); */
15149 else if (IS_ENVELOPE(element))
15151 player->show_envelope = element;
15153 else if (element == EL_EMC_LENSES)
15155 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15157 RedrawAllInvisibleElementsForLenses();
15159 else if (element == EL_EMC_MAGNIFIER)
15161 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15163 RedrawAllInvisibleElementsForMagnifier();
15165 else if (IS_DROPPABLE(element) ||
15166 IS_THROWABLE(element)) /* can be collected and dropped */
15170 if (collect_count == 0)
15171 player->inventory_infinite_element = element;
15173 for (i = 0; i < collect_count; i++)
15174 if (player->inventory_size < MAX_INVENTORY_SIZE)
15175 player->inventory_element[player->inventory_size++] = element;
15177 DrawGameDoorValues();
15179 else if (collect_count > 0)
15181 local_player->gems_still_needed -= collect_count;
15182 if (local_player->gems_still_needed < 0)
15183 local_player->gems_still_needed = 0;
15186 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15188 DisplayGameControlValues();
15190 DrawGameValue_Emeralds(local_player->gems_still_needed);
15194 RaiseScoreElement(element);
15195 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15198 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15199 player->index_bit, dig_side);
15201 if (mode == DF_SNAP)
15203 #if USE_NEW_SNAP_DELAY
15204 if (level.block_snap_field)
15205 setFieldForSnapping(x, y, element, move_direction);
15207 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15209 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15212 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15213 player->index_bit, dig_side);
15216 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15218 if (mode == DF_SNAP && element != EL_BD_ROCK)
15219 return MP_NO_ACTION;
15221 if (CAN_FALL(element) && dy)
15222 return MP_NO_ACTION;
15224 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15225 !(element == EL_SPRING && level.use_spring_bug))
15226 return MP_NO_ACTION;
15228 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15229 ((move_direction & MV_VERTICAL &&
15230 ((element_info[element].move_pattern & MV_LEFT &&
15231 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15232 (element_info[element].move_pattern & MV_RIGHT &&
15233 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15234 (move_direction & MV_HORIZONTAL &&
15235 ((element_info[element].move_pattern & MV_UP &&
15236 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15237 (element_info[element].move_pattern & MV_DOWN &&
15238 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15239 return MP_NO_ACTION;
15241 /* do not push elements already moving away faster than player */
15242 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15243 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15244 return MP_NO_ACTION;
15246 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15248 if (player->push_delay_value == -1 || !player_was_pushing)
15249 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15251 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15253 if (player->push_delay_value == -1)
15254 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15256 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15258 if (!player->is_pushing)
15259 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15262 player->is_pushing = TRUE;
15263 player->is_active = TRUE;
15265 if (!(IN_LEV_FIELD(nextx, nexty) &&
15266 (IS_FREE(nextx, nexty) ||
15267 (IS_SB_ELEMENT(element) &&
15268 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15269 (IS_CUSTOM_ELEMENT(element) &&
15270 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15271 return MP_NO_ACTION;
15273 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15274 return MP_NO_ACTION;
15276 if (player->push_delay == -1) /* new pushing; restart delay */
15277 player->push_delay = 0;
15279 if (player->push_delay < player->push_delay_value &&
15280 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15281 element != EL_SPRING && element != EL_BALLOON)
15283 /* make sure that there is no move delay before next try to push */
15284 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15285 player->move_delay = 0;
15287 return MP_NO_ACTION;
15290 if (IS_CUSTOM_ELEMENT(element) &&
15291 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15293 if (!DigFieldByCE(nextx, nexty, element))
15294 return MP_NO_ACTION;
15297 if (IS_SB_ELEMENT(element))
15299 if (element == EL_SOKOBAN_FIELD_FULL)
15301 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15302 local_player->sokobanfields_still_needed++;
15305 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15307 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15308 local_player->sokobanfields_still_needed--;
15311 Feld[x][y] = EL_SOKOBAN_OBJECT;
15313 if (Back[x][y] == Back[nextx][nexty])
15314 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15315 else if (Back[x][y] != 0)
15316 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15319 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15323 if (local_player->sokobanfields_still_needed == 0 &&
15324 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15326 if (local_player->sokobanfields_still_needed == 0 &&
15327 game.emulation == EMU_SOKOBAN)
15330 PlayerWins(player);
15332 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15336 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15338 InitMovingField(x, y, move_direction);
15339 GfxAction[x][y] = ACTION_PUSHING;
15341 if (mode == DF_SNAP)
15342 ContinueMoving(x, y);
15344 MovPos[x][y] = (dx != 0 ? dx : dy);
15346 Pushed[x][y] = TRUE;
15347 Pushed[nextx][nexty] = TRUE;
15349 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15350 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15352 player->push_delay_value = -1; /* get new value later */
15354 /* check for element change _after_ element has been pushed */
15355 if (game.use_change_when_pushing_bug)
15357 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15358 player->index_bit, dig_side);
15359 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15360 player->index_bit, dig_side);
15363 else if (IS_SWITCHABLE(element))
15365 if (PLAYER_SWITCHING(player, x, y))
15367 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15368 player->index_bit, dig_side);
15373 player->is_switching = TRUE;
15374 player->switch_x = x;
15375 player->switch_y = y;
15377 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15379 if (element == EL_ROBOT_WHEEL)
15381 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15385 game.robot_wheel_active = TRUE;
15387 TEST_DrawLevelField(x, y);
15389 else if (element == EL_SP_TERMINAL)
15393 SCAN_PLAYFIELD(xx, yy)
15395 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15397 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15398 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15401 else if (IS_BELT_SWITCH(element))
15403 ToggleBeltSwitch(x, y);
15405 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15406 element == EL_SWITCHGATE_SWITCH_DOWN ||
15407 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15408 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15410 ToggleSwitchgateSwitch(x, y);
15412 else if (element == EL_LIGHT_SWITCH ||
15413 element == EL_LIGHT_SWITCH_ACTIVE)
15415 ToggleLightSwitch(x, y);
15417 else if (element == EL_TIMEGATE_SWITCH ||
15418 element == EL_DC_TIMEGATE_SWITCH)
15420 ActivateTimegateSwitch(x, y);
15422 else if (element == EL_BALLOON_SWITCH_LEFT ||
15423 element == EL_BALLOON_SWITCH_RIGHT ||
15424 element == EL_BALLOON_SWITCH_UP ||
15425 element == EL_BALLOON_SWITCH_DOWN ||
15426 element == EL_BALLOON_SWITCH_NONE ||
15427 element == EL_BALLOON_SWITCH_ANY)
15429 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15430 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15431 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15432 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15433 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15436 else if (element == EL_LAMP)
15438 Feld[x][y] = EL_LAMP_ACTIVE;
15439 local_player->lights_still_needed--;
15441 ResetGfxAnimation(x, y);
15442 TEST_DrawLevelField(x, y);
15444 else if (element == EL_TIME_ORB_FULL)
15446 Feld[x][y] = EL_TIME_ORB_EMPTY;
15448 if (level.time > 0 || level.use_time_orb_bug)
15450 TimeLeft += level.time_orb_time;
15451 game.no_time_limit = FALSE;
15454 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15456 DisplayGameControlValues();
15458 DrawGameValue_Time(TimeLeft);
15462 ResetGfxAnimation(x, y);
15463 TEST_DrawLevelField(x, y);
15465 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15466 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15470 game.ball_state = !game.ball_state;
15472 SCAN_PLAYFIELD(xx, yy)
15474 int e = Feld[xx][yy];
15476 if (game.ball_state)
15478 if (e == EL_EMC_MAGIC_BALL)
15479 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15480 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15481 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15485 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15486 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15487 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15488 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15493 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15494 player->index_bit, dig_side);
15496 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15497 player->index_bit, dig_side);
15499 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15500 player->index_bit, dig_side);
15506 if (!PLAYER_SWITCHING(player, x, y))
15508 player->is_switching = TRUE;
15509 player->switch_x = x;
15510 player->switch_y = y;
15512 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15513 player->index_bit, dig_side);
15514 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15515 player->index_bit, dig_side);
15517 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15518 player->index_bit, dig_side);
15519 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15520 player->index_bit, dig_side);
15523 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15524 player->index_bit, dig_side);
15525 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15526 player->index_bit, dig_side);
15528 return MP_NO_ACTION;
15531 player->push_delay = -1;
15533 if (is_player) /* function can also be called by EL_PENGUIN */
15535 if (Feld[x][y] != element) /* really digged/collected something */
15537 player->is_collecting = !player->is_digging;
15538 player->is_active = TRUE;
15545 static boolean DigFieldByCE(int x, int y, int digging_element)
15547 int element = Feld[x][y];
15549 if (!IS_FREE(x, y))
15551 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15552 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15555 /* no element can dig solid indestructible elements */
15556 if (IS_INDESTRUCTIBLE(element) &&
15557 !IS_DIGGABLE(element) &&
15558 !IS_COLLECTIBLE(element))
15561 if (AmoebaNr[x][y] &&
15562 (element == EL_AMOEBA_FULL ||
15563 element == EL_BD_AMOEBA ||
15564 element == EL_AMOEBA_GROWING))
15566 AmoebaCnt[AmoebaNr[x][y]]--;
15567 AmoebaCnt2[AmoebaNr[x][y]]--;
15570 if (IS_MOVING(x, y))
15571 RemoveMovingField(x, y);
15575 TEST_DrawLevelField(x, y);
15578 /* if digged element was about to explode, prevent the explosion */
15579 ExplodeField[x][y] = EX_TYPE_NONE;
15581 PlayLevelSoundAction(x, y, action);
15584 Store[x][y] = EL_EMPTY;
15587 /* this makes it possible to leave the removed element again */
15588 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15589 Store[x][y] = element;
15591 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15593 int move_leave_element = element_info[digging_element].move_leave_element;
15595 /* this makes it possible to leave the removed element again */
15596 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15597 element : move_leave_element);
15604 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15606 int jx = player->jx, jy = player->jy;
15607 int x = jx + dx, y = jy + dy;
15608 int snap_direction = (dx == -1 ? MV_LEFT :
15609 dx == +1 ? MV_RIGHT :
15611 dy == +1 ? MV_DOWN : MV_NONE);
15612 boolean can_continue_snapping = (level.continuous_snapping &&
15613 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15615 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15618 if (!player->active || !IN_LEV_FIELD(x, y))
15626 if (player->MovPos == 0)
15627 player->is_pushing = FALSE;
15629 player->is_snapping = FALSE;
15631 if (player->MovPos == 0)
15633 player->is_moving = FALSE;
15634 player->is_digging = FALSE;
15635 player->is_collecting = FALSE;
15641 #if USE_NEW_CONTINUOUS_SNAPPING
15642 /* prevent snapping with already pressed snap key when not allowed */
15643 if (player->is_snapping && !can_continue_snapping)
15646 if (player->is_snapping)
15650 player->MovDir = snap_direction;
15652 if (player->MovPos == 0)
15654 player->is_moving = FALSE;
15655 player->is_digging = FALSE;
15656 player->is_collecting = FALSE;
15659 player->is_dropping = FALSE;
15660 player->is_dropping_pressed = FALSE;
15661 player->drop_pressed_delay = 0;
15663 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15666 player->is_snapping = TRUE;
15667 player->is_active = TRUE;
15669 if (player->MovPos == 0)
15671 player->is_moving = FALSE;
15672 player->is_digging = FALSE;
15673 player->is_collecting = FALSE;
15676 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15677 TEST_DrawLevelField(player->last_jx, player->last_jy);
15679 TEST_DrawLevelField(x, y);
15684 static boolean DropElement(struct PlayerInfo *player)
15686 int old_element, new_element;
15687 int dropx = player->jx, dropy = player->jy;
15688 int drop_direction = player->MovDir;
15689 int drop_side = drop_direction;
15691 int drop_element = get_next_dropped_element(player);
15693 int drop_element = (player->inventory_size > 0 ?
15694 player->inventory_element[player->inventory_size - 1] :
15695 player->inventory_infinite_element != EL_UNDEFINED ?
15696 player->inventory_infinite_element :
15697 player->dynabombs_left > 0 ?
15698 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15702 player->is_dropping_pressed = TRUE;
15704 /* do not drop an element on top of another element; when holding drop key
15705 pressed without moving, dropped element must move away before the next
15706 element can be dropped (this is especially important if the next element
15707 is dynamite, which can be placed on background for historical reasons) */
15708 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15711 if (IS_THROWABLE(drop_element))
15713 dropx += GET_DX_FROM_DIR(drop_direction);
15714 dropy += GET_DY_FROM_DIR(drop_direction);
15716 if (!IN_LEV_FIELD(dropx, dropy))
15720 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15721 new_element = drop_element; /* default: no change when dropping */
15723 /* check if player is active, not moving and ready to drop */
15724 if (!player->active || player->MovPos || player->drop_delay > 0)
15727 /* check if player has anything that can be dropped */
15728 if (new_element == EL_UNDEFINED)
15731 /* check if drop key was pressed long enough for EM style dynamite */
15732 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15735 /* check if anything can be dropped at the current position */
15736 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15739 /* collected custom elements can only be dropped on empty fields */
15740 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15743 if (old_element != EL_EMPTY)
15744 Back[dropx][dropy] = old_element; /* store old element on this field */
15746 ResetGfxAnimation(dropx, dropy);
15747 ResetRandomAnimationValue(dropx, dropy);
15749 if (player->inventory_size > 0 ||
15750 player->inventory_infinite_element != EL_UNDEFINED)
15752 if (player->inventory_size > 0)
15754 player->inventory_size--;
15756 DrawGameDoorValues();
15758 if (new_element == EL_DYNAMITE)
15759 new_element = EL_DYNAMITE_ACTIVE;
15760 else if (new_element == EL_EM_DYNAMITE)
15761 new_element = EL_EM_DYNAMITE_ACTIVE;
15762 else if (new_element == EL_SP_DISK_RED)
15763 new_element = EL_SP_DISK_RED_ACTIVE;
15766 Feld[dropx][dropy] = new_element;
15768 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15769 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15770 el2img(Feld[dropx][dropy]), 0);
15772 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15774 /* needed if previous element just changed to "empty" in the last frame */
15775 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15777 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15778 player->index_bit, drop_side);
15779 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15781 player->index_bit, drop_side);
15783 TestIfElementTouchesCustomElement(dropx, dropy);
15785 else /* player is dropping a dyna bomb */
15787 player->dynabombs_left--;
15789 Feld[dropx][dropy] = new_element;
15791 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15792 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15793 el2img(Feld[dropx][dropy]), 0);
15795 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15798 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15799 InitField_WithBug1(dropx, dropy, FALSE);
15801 new_element = Feld[dropx][dropy]; /* element might have changed */
15803 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15804 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15806 int move_direction, nextx, nexty;
15808 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15809 MovDir[dropx][dropy] = drop_direction;
15811 move_direction = MovDir[dropx][dropy];
15812 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15813 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15815 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15817 #if USE_FIX_IMPACT_COLLISION
15818 /* do not cause impact style collision by dropping elements that can fall */
15819 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15821 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15825 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15826 player->is_dropping = TRUE;
15828 player->drop_pressed_delay = 0;
15829 player->is_dropping_pressed = FALSE;
15831 player->drop_x = dropx;
15832 player->drop_y = dropy;
15837 /* ------------------------------------------------------------------------- */
15838 /* game sound playing functions */
15839 /* ------------------------------------------------------------------------- */
15841 static int *loop_sound_frame = NULL;
15842 static int *loop_sound_volume = NULL;
15844 void InitPlayLevelSound()
15846 int num_sounds = getSoundListSize();
15848 checked_free(loop_sound_frame);
15849 checked_free(loop_sound_volume);
15851 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15852 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15855 static void PlayLevelSound(int x, int y, int nr)
15857 int sx = SCREENX(x), sy = SCREENY(y);
15858 int volume, stereo_position;
15859 int max_distance = 8;
15860 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15862 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15863 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15866 if (!IN_LEV_FIELD(x, y) ||
15867 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15868 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15871 volume = SOUND_MAX_VOLUME;
15873 if (!IN_SCR_FIELD(sx, sy))
15875 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15876 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15878 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15881 stereo_position = (SOUND_MAX_LEFT +
15882 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15883 (SCR_FIELDX + 2 * max_distance));
15885 if (IS_LOOP_SOUND(nr))
15887 /* This assures that quieter loop sounds do not overwrite louder ones,
15888 while restarting sound volume comparison with each new game frame. */
15890 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15893 loop_sound_volume[nr] = volume;
15894 loop_sound_frame[nr] = FrameCounter;
15897 PlaySoundExt(nr, volume, stereo_position, type);
15900 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15902 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15903 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15904 y < LEVELY(BY1) ? LEVELY(BY1) :
15905 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15909 static void PlayLevelSoundAction(int x, int y, int action)
15911 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15914 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15916 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15918 if (sound_effect != SND_UNDEFINED)
15919 PlayLevelSound(x, y, sound_effect);
15922 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15925 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15927 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15928 PlayLevelSound(x, y, sound_effect);
15931 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15933 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15935 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15936 PlayLevelSound(x, y, sound_effect);
15939 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15941 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15943 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15944 StopSound(sound_effect);
15947 static void PlayLevelMusic()
15949 if (levelset.music[level_nr] != MUS_UNDEFINED)
15950 PlayMusic(levelset.music[level_nr]); /* from config file */
15952 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15955 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15957 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15958 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15959 int x = xx - 1 - offset;
15960 int y = yy - 1 - offset;
15965 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15969 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15973 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15977 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15981 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15985 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15989 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15992 case SAMPLE_android_clone:
15993 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15996 case SAMPLE_android_move:
15997 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16000 case SAMPLE_spring:
16001 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16005 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16009 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16012 case SAMPLE_eater_eat:
16013 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16017 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16020 case SAMPLE_collect:
16021 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16024 case SAMPLE_diamond:
16025 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16028 case SAMPLE_squash:
16029 /* !!! CHECK THIS !!! */
16031 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16033 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16037 case SAMPLE_wonderfall:
16038 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16042 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16046 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16050 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16054 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16058 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16062 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16065 case SAMPLE_wonder:
16066 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16070 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16073 case SAMPLE_exit_open:
16074 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16077 case SAMPLE_exit_leave:
16078 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16081 case SAMPLE_dynamite:
16082 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16086 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16090 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16094 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16098 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16102 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16106 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16110 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16115 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16117 int element = map_element_SP_to_RND(element_sp);
16118 int action = map_action_SP_to_RND(action_sp);
16119 int offset = (setup.sp_show_border_elements ? 0 : 1);
16120 int x = xx - offset;
16121 int y = yy - offset;
16124 printf("::: %d -> %d\n", element_sp, action_sp);
16127 PlayLevelSoundElementAction(x, y, element, action);
16130 void RaiseScore(int value)
16132 local_player->score += value;
16135 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16137 DisplayGameControlValues();
16139 DrawGameValue_Score(local_player->score);
16143 void RaiseScoreElement(int element)
16148 case EL_BD_DIAMOND:
16149 case EL_EMERALD_YELLOW:
16150 case EL_EMERALD_RED:
16151 case EL_EMERALD_PURPLE:
16152 case EL_SP_INFOTRON:
16153 RaiseScore(level.score[SC_EMERALD]);
16156 RaiseScore(level.score[SC_DIAMOND]);
16159 RaiseScore(level.score[SC_CRYSTAL]);
16162 RaiseScore(level.score[SC_PEARL]);
16165 case EL_BD_BUTTERFLY:
16166 case EL_SP_ELECTRON:
16167 RaiseScore(level.score[SC_BUG]);
16170 case EL_BD_FIREFLY:
16171 case EL_SP_SNIKSNAK:
16172 RaiseScore(level.score[SC_SPACESHIP]);
16175 case EL_DARK_YAMYAM:
16176 RaiseScore(level.score[SC_YAMYAM]);
16179 RaiseScore(level.score[SC_ROBOT]);
16182 RaiseScore(level.score[SC_PACMAN]);
16185 RaiseScore(level.score[SC_NUT]);
16188 case EL_EM_DYNAMITE:
16189 case EL_SP_DISK_RED:
16190 case EL_DYNABOMB_INCREASE_NUMBER:
16191 case EL_DYNABOMB_INCREASE_SIZE:
16192 case EL_DYNABOMB_INCREASE_POWER:
16193 RaiseScore(level.score[SC_DYNAMITE]);
16195 case EL_SHIELD_NORMAL:
16196 case EL_SHIELD_DEADLY:
16197 RaiseScore(level.score[SC_SHIELD]);
16199 case EL_EXTRA_TIME:
16200 RaiseScore(level.extra_time_score);
16214 case EL_DC_KEY_WHITE:
16215 RaiseScore(level.score[SC_KEY]);
16218 RaiseScore(element_info[element].collect_score);
16223 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16225 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16227 #if defined(NETWORK_AVALIABLE)
16228 if (options.network)
16229 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16238 FadeSkipNextFadeIn();
16240 fading = fading_none;
16244 OpenDoor(DOOR_CLOSE_1);
16247 game_status = GAME_MODE_MAIN;
16250 DrawAndFadeInMainMenu(REDRAW_FIELD);
16258 FadeOut(REDRAW_FIELD);
16261 game_status = GAME_MODE_MAIN;
16263 DrawAndFadeInMainMenu(REDRAW_FIELD);
16267 else /* continue playing the game */
16269 if (tape.playing && tape.deactivate_display)
16270 TapeDeactivateDisplayOff(TRUE);
16272 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16274 if (tape.playing && tape.deactivate_display)
16275 TapeDeactivateDisplayOn();
16279 void RequestQuitGame(boolean ask_if_really_quit)
16281 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16282 boolean skip_request = AllPlayersGone || quick_quit;
16284 RequestQuitGameExt(skip_request, quick_quit,
16285 "Do you really want to quit the game ?");
16289 /* ------------------------------------------------------------------------- */
16290 /* random generator functions */
16291 /* ------------------------------------------------------------------------- */
16293 unsigned int InitEngineRandom_RND(int seed)
16295 game.num_random_calls = 0;
16298 unsigned int rnd_seed = InitEngineRandom(seed);
16300 printf("::: START RND: %d\n", rnd_seed);
16305 return InitEngineRandom(seed);
16311 unsigned int RND(int max)
16315 game.num_random_calls++;
16317 return GetEngineRandom(max);
16324 /* ------------------------------------------------------------------------- */
16325 /* game engine snapshot handling functions */
16326 /* ------------------------------------------------------------------------- */
16328 struct EngineSnapshotInfo
16330 /* runtime values for custom element collect score */
16331 int collect_score[NUM_CUSTOM_ELEMENTS];
16333 /* runtime values for group element choice position */
16334 int choice_pos[NUM_GROUP_ELEMENTS];
16336 /* runtime values for belt position animations */
16337 int belt_graphic[4][NUM_BELT_PARTS];
16338 int belt_anim_mode[4][NUM_BELT_PARTS];
16341 static struct EngineSnapshotInfo engine_snapshot_rnd;
16342 static char *snapshot_level_identifier = NULL;
16343 static int snapshot_level_nr = -1;
16345 static void SaveEngineSnapshotValues_RND()
16347 static int belt_base_active_element[4] =
16349 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16350 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16351 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16352 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16356 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16358 int element = EL_CUSTOM_START + i;
16360 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16363 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16365 int element = EL_GROUP_START + i;
16367 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16370 for (i = 0; i < 4; i++)
16372 for (j = 0; j < NUM_BELT_PARTS; j++)
16374 int element = belt_base_active_element[i] + j;
16375 int graphic = el2img(element);
16376 int anim_mode = graphic_info[graphic].anim_mode;
16378 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16379 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16384 static void LoadEngineSnapshotValues_RND()
16386 unsigned int num_random_calls = game.num_random_calls;
16389 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16391 int element = EL_CUSTOM_START + i;
16393 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16396 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16398 int element = EL_GROUP_START + i;
16400 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16403 for (i = 0; i < 4; i++)
16405 for (j = 0; j < NUM_BELT_PARTS; j++)
16407 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16408 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16410 graphic_info[graphic].anim_mode = anim_mode;
16414 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16416 InitRND(tape.random_seed);
16417 for (i = 0; i < num_random_calls; i++)
16421 if (game.num_random_calls != num_random_calls)
16423 Error(ERR_INFO, "number of random calls out of sync");
16424 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16425 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16426 Error(ERR_EXIT, "this should not happen -- please debug");
16430 void SaveEngineSnapshot()
16432 /* do not save snapshots from editor */
16433 if (level_editor_test_game)
16436 /* free previous snapshot buffers, if needed */
16437 FreeEngineSnapshotBuffers();
16439 /* copy some special values to a structure better suited for the snapshot */
16441 SaveEngineSnapshotValues_RND();
16442 SaveEngineSnapshotValues_EM();
16443 SaveEngineSnapshotValues_SP();
16445 /* save values stored in special snapshot structure */
16447 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16448 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16449 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16451 /* save further RND engine values */
16453 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16454 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16455 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16457 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16458 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16459 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16460 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16462 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16463 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16464 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16465 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16466 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16468 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16469 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16470 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16472 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16474 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16476 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16477 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16479 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16480 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16481 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16482 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16483 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16484 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16485 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16486 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16487 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16488 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16489 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16490 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16491 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16492 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16493 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16494 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16495 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16496 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16498 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16499 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16501 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16502 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16503 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16505 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16506 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16508 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16509 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16510 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16511 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16512 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16514 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16515 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16517 /* save level identification information */
16519 setString(&snapshot_level_identifier, leveldir_current->identifier);
16520 snapshot_level_nr = level_nr;
16523 ListNode *node = engine_snapshot_list_rnd;
16526 while (node != NULL)
16528 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16533 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16537 void LoadEngineSnapshot()
16539 /* restore generically stored snapshot buffers */
16541 LoadEngineSnapshotBuffers();
16543 /* restore special values from snapshot structure */
16545 LoadEngineSnapshotValues_RND();
16546 LoadEngineSnapshotValues_EM();
16547 LoadEngineSnapshotValues_SP();
16550 boolean CheckEngineSnapshot()
16552 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16553 snapshot_level_nr == level_nr);
16557 /* ---------- new game button stuff ---------------------------------------- */
16565 } gamebutton_info[NUM_GAME_BUTTONS] =
16568 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16569 GAME_CTRL_ID_STOP, "stop game"
16572 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16573 GAME_CTRL_ID_PAUSE, "pause game"
16576 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16577 GAME_CTRL_ID_PLAY, "play game"
16580 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16581 SOUND_CTRL_ID_MUSIC, "background music on/off"
16584 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16585 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16588 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16589 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16593 void CreateGameButtons()
16597 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16599 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16600 struct Rect *pos = gamebutton_info[i].pos;
16601 struct GadgetInfo *gi;
16604 unsigned int event_mask;
16605 int gd_x = gfx->src_x;
16606 int gd_y = gfx->src_y;
16607 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16608 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16609 int gd_xa = gfx->src_x + gfx->active_xoffset;
16610 int gd_ya = gfx->src_y + gfx->active_yoffset;
16611 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16612 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16615 if (id == GAME_CTRL_ID_STOP ||
16616 id == GAME_CTRL_ID_PAUSE ||
16617 id == GAME_CTRL_ID_PLAY)
16619 button_type = GD_TYPE_NORMAL_BUTTON;
16621 event_mask = GD_EVENT_RELEASED;
16625 button_type = GD_TYPE_CHECK_BUTTON;
16627 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16628 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16629 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16630 event_mask = GD_EVENT_PRESSED;
16633 gi = CreateGadget(GDI_CUSTOM_ID, id,
16634 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16635 GDI_X, DX + pos->x,
16636 GDI_Y, DY + pos->y,
16637 GDI_WIDTH, gfx->width,
16638 GDI_HEIGHT, gfx->height,
16639 GDI_TYPE, button_type,
16640 GDI_STATE, GD_BUTTON_UNPRESSED,
16641 GDI_CHECKED, checked,
16642 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16643 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16644 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16645 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16646 GDI_DIRECT_DRAW, FALSE,
16647 GDI_EVENT_MASK, event_mask,
16648 GDI_CALLBACK_ACTION, HandleGameButtons,
16652 Error(ERR_EXIT, "cannot create gadget");
16654 game_gadget[id] = gi;
16658 void FreeGameButtons()
16662 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16663 FreeGadget(game_gadget[i]);
16666 static void MapGameButtons()
16670 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16671 MapGadget(game_gadget[i]);
16674 void UnmapGameButtons()
16678 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16679 UnmapGadget(game_gadget[i]);
16682 void RedrawGameButtons()
16686 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16687 RedrawGadget(game_gadget[i]);
16690 static void HandleGameButtonsExt(int id)
16692 if (game_status != GAME_MODE_PLAYING)
16697 case GAME_CTRL_ID_STOP:
16701 RequestQuitGame(TRUE);
16704 case GAME_CTRL_ID_PAUSE:
16705 if (options.network)
16707 #if defined(NETWORK_AVALIABLE)
16709 SendToServer_ContinuePlaying();
16711 SendToServer_PausePlaying();
16715 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16718 case GAME_CTRL_ID_PLAY:
16721 #if defined(NETWORK_AVALIABLE)
16722 if (options.network)
16723 SendToServer_ContinuePlaying();
16727 tape.pausing = FALSE;
16728 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16733 case SOUND_CTRL_ID_MUSIC:
16734 if (setup.sound_music)
16736 setup.sound_music = FALSE;
16740 else if (audio.music_available)
16742 setup.sound = setup.sound_music = TRUE;
16744 SetAudioMode(setup.sound);
16750 case SOUND_CTRL_ID_LOOPS:
16751 if (setup.sound_loops)
16752 setup.sound_loops = FALSE;
16753 else if (audio.loops_available)
16755 setup.sound = setup.sound_loops = TRUE;
16757 SetAudioMode(setup.sound);
16761 case SOUND_CTRL_ID_SIMPLE:
16762 if (setup.sound_simple)
16763 setup.sound_simple = FALSE;
16764 else if (audio.sound_available)
16766 setup.sound = setup.sound_simple = TRUE;
16768 SetAudioMode(setup.sound);
16777 static void HandleGameButtons(struct GadgetInfo *gi)
16779 HandleGameButtonsExt(gi->custom_id);
16782 void HandleSoundButtonKeys(Key key)
16785 if (key == setup.shortcut.sound_simple)
16786 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16787 else if (key == setup.shortcut.sound_loops)
16788 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16789 else if (key == setup.shortcut.sound_music)
16790 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16792 if (key == setup.shortcut.sound_simple)
16793 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16794 else if (key == setup.shortcut.sound_loops)
16795 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16796 else if (key == setup.shortcut.sound_music)
16797 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);