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"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_STUFF ( 1)
35 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
36 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
37 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
38 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
39 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
40 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
41 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
42 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
43 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
44 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
45 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
46 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
47 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
48 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
49 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
50 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
51 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
52 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
53 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
55 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
57 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
59 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
61 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
64 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
65 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
66 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
67 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
69 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
71 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
73 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
75 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
77 #if USE_DELAYED_GFX_REDRAW
78 #define TEST_DrawLevelField(x, y) \
79 GfxRedraw[x][y] |= GFX_REDRAW_TILE
80 #define TEST_DrawLevelFieldCrumbled(x, y) \
81 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
82 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
83 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
84 #define TEST_DrawTwinkleOnField(x, y) \
85 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
87 #define TEST_DrawLevelField(x, y) \
89 #define TEST_DrawLevelFieldCrumbled(x, y) \
90 DrawLevelFieldCrumbled(x, y)
91 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
92 DrawLevelFieldCrumbledNeighbours(x, y)
93 #define TEST_DrawTwinkleOnField(x, y) \
94 DrawTwinkleOnField(x, y)
103 /* for MovePlayer() */
104 #define MP_NO_ACTION 0
107 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
109 /* for ScrollPlayer() */
110 #define SCROLL_INIT 0
111 #define SCROLL_GO_ON 1
113 /* for Bang()/Explode() */
114 #define EX_PHASE_START 0
115 #define EX_TYPE_NONE 0
116 #define EX_TYPE_NORMAL (1 << 0)
117 #define EX_TYPE_CENTER (1 << 1)
118 #define EX_TYPE_BORDER (1 << 2)
119 #define EX_TYPE_CROSS (1 << 3)
120 #define EX_TYPE_DYNA (1 << 4)
121 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
123 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
124 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
125 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
126 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
128 /* game panel display and control definitions */
129 #define GAME_PANEL_LEVEL_NUMBER 0
130 #define GAME_PANEL_GEMS 1
131 #define GAME_PANEL_INVENTORY_COUNT 2
132 #define GAME_PANEL_INVENTORY_FIRST_1 3
133 #define GAME_PANEL_INVENTORY_FIRST_2 4
134 #define GAME_PANEL_INVENTORY_FIRST_3 5
135 #define GAME_PANEL_INVENTORY_FIRST_4 6
136 #define GAME_PANEL_INVENTORY_FIRST_5 7
137 #define GAME_PANEL_INVENTORY_FIRST_6 8
138 #define GAME_PANEL_INVENTORY_FIRST_7 9
139 #define GAME_PANEL_INVENTORY_FIRST_8 10
140 #define GAME_PANEL_INVENTORY_LAST_1 11
141 #define GAME_PANEL_INVENTORY_LAST_2 12
142 #define GAME_PANEL_INVENTORY_LAST_3 13
143 #define GAME_PANEL_INVENTORY_LAST_4 14
144 #define GAME_PANEL_INVENTORY_LAST_5 15
145 #define GAME_PANEL_INVENTORY_LAST_6 16
146 #define GAME_PANEL_INVENTORY_LAST_7 17
147 #define GAME_PANEL_INVENTORY_LAST_8 18
148 #define GAME_PANEL_KEY_1 19
149 #define GAME_PANEL_KEY_2 20
150 #define GAME_PANEL_KEY_3 21
151 #define GAME_PANEL_KEY_4 22
152 #define GAME_PANEL_KEY_5 23
153 #define GAME_PANEL_KEY_6 24
154 #define GAME_PANEL_KEY_7 25
155 #define GAME_PANEL_KEY_8 26
156 #define GAME_PANEL_KEY_WHITE 27
157 #define GAME_PANEL_KEY_WHITE_COUNT 28
158 #define GAME_PANEL_SCORE 29
159 #define GAME_PANEL_HIGHSCORE 30
160 #define GAME_PANEL_TIME 31
161 #define GAME_PANEL_TIME_HH 32
162 #define GAME_PANEL_TIME_MM 33
163 #define GAME_PANEL_TIME_SS 34
164 #define GAME_PANEL_FRAME 35
165 #define GAME_PANEL_SHIELD_NORMAL 36
166 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
167 #define GAME_PANEL_SHIELD_DEADLY 38
168 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
169 #define GAME_PANEL_EXIT 40
170 #define GAME_PANEL_EMC_MAGIC_BALL 41
171 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
172 #define GAME_PANEL_LIGHT_SWITCH 43
173 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
174 #define GAME_PANEL_TIMEGATE_SWITCH 45
175 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
176 #define GAME_PANEL_SWITCHGATE_SWITCH 47
177 #define GAME_PANEL_EMC_LENSES 48
178 #define GAME_PANEL_EMC_LENSES_TIME 49
179 #define GAME_PANEL_EMC_MAGNIFIER 50
180 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
181 #define GAME_PANEL_BALLOON_SWITCH 52
182 #define GAME_PANEL_DYNABOMB_NUMBER 53
183 #define GAME_PANEL_DYNABOMB_SIZE 54
184 #define GAME_PANEL_DYNABOMB_POWER 55
185 #define GAME_PANEL_PENGUINS 56
186 #define GAME_PANEL_SOKOBAN_OBJECTS 57
187 #define GAME_PANEL_SOKOBAN_FIELDS 58
188 #define GAME_PANEL_ROBOT_WHEEL 59
189 #define GAME_PANEL_CONVEYOR_BELT_1 60
190 #define GAME_PANEL_CONVEYOR_BELT_2 61
191 #define GAME_PANEL_CONVEYOR_BELT_3 62
192 #define GAME_PANEL_CONVEYOR_BELT_4 63
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
194 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
195 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
196 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
197 #define GAME_PANEL_MAGIC_WALL 68
198 #define GAME_PANEL_MAGIC_WALL_TIME 69
199 #define GAME_PANEL_GRAVITY_STATE 70
200 #define GAME_PANEL_GRAPHIC_1 71
201 #define GAME_PANEL_GRAPHIC_2 72
202 #define GAME_PANEL_GRAPHIC_3 73
203 #define GAME_PANEL_GRAPHIC_4 74
204 #define GAME_PANEL_GRAPHIC_5 75
205 #define GAME_PANEL_GRAPHIC_6 76
206 #define GAME_PANEL_GRAPHIC_7 77
207 #define GAME_PANEL_GRAPHIC_8 78
208 #define GAME_PANEL_ELEMENT_1 79
209 #define GAME_PANEL_ELEMENT_2 80
210 #define GAME_PANEL_ELEMENT_3 81
211 #define GAME_PANEL_ELEMENT_4 82
212 #define GAME_PANEL_ELEMENT_5 83
213 #define GAME_PANEL_ELEMENT_6 84
214 #define GAME_PANEL_ELEMENT_7 85
215 #define GAME_PANEL_ELEMENT_8 86
216 #define GAME_PANEL_ELEMENT_COUNT_1 87
217 #define GAME_PANEL_ELEMENT_COUNT_2 88
218 #define GAME_PANEL_ELEMENT_COUNT_3 89
219 #define GAME_PANEL_ELEMENT_COUNT_4 90
220 #define GAME_PANEL_ELEMENT_COUNT_5 91
221 #define GAME_PANEL_ELEMENT_COUNT_6 92
222 #define GAME_PANEL_ELEMENT_COUNT_7 93
223 #define GAME_PANEL_ELEMENT_COUNT_8 94
224 #define GAME_PANEL_CE_SCORE_1 95
225 #define GAME_PANEL_CE_SCORE_2 96
226 #define GAME_PANEL_CE_SCORE_3 97
227 #define GAME_PANEL_CE_SCORE_4 98
228 #define GAME_PANEL_CE_SCORE_5 99
229 #define GAME_PANEL_CE_SCORE_6 100
230 #define GAME_PANEL_CE_SCORE_7 101
231 #define GAME_PANEL_CE_SCORE_8 102
232 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
233 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
234 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
235 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
236 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
237 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
238 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
239 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
240 #define GAME_PANEL_PLAYER_NAME 111
241 #define GAME_PANEL_LEVEL_NAME 112
242 #define GAME_PANEL_LEVEL_AUTHOR 113
244 #define NUM_GAME_PANEL_CONTROLS 114
246 struct GamePanelOrderInfo
252 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
254 struct GamePanelControlInfo
258 struct TextPosInfo *pos;
261 int value, last_value;
262 int frame, last_frame;
267 static struct GamePanelControlInfo game_panel_controls[] =
270 GAME_PANEL_LEVEL_NUMBER,
271 &game.panel.level_number,
280 GAME_PANEL_INVENTORY_COUNT,
281 &game.panel.inventory_count,
285 GAME_PANEL_INVENTORY_FIRST_1,
286 &game.panel.inventory_first[0],
290 GAME_PANEL_INVENTORY_FIRST_2,
291 &game.panel.inventory_first[1],
295 GAME_PANEL_INVENTORY_FIRST_3,
296 &game.panel.inventory_first[2],
300 GAME_PANEL_INVENTORY_FIRST_4,
301 &game.panel.inventory_first[3],
305 GAME_PANEL_INVENTORY_FIRST_5,
306 &game.panel.inventory_first[4],
310 GAME_PANEL_INVENTORY_FIRST_6,
311 &game.panel.inventory_first[5],
315 GAME_PANEL_INVENTORY_FIRST_7,
316 &game.panel.inventory_first[6],
320 GAME_PANEL_INVENTORY_FIRST_8,
321 &game.panel.inventory_first[7],
325 GAME_PANEL_INVENTORY_LAST_1,
326 &game.panel.inventory_last[0],
330 GAME_PANEL_INVENTORY_LAST_2,
331 &game.panel.inventory_last[1],
335 GAME_PANEL_INVENTORY_LAST_3,
336 &game.panel.inventory_last[2],
340 GAME_PANEL_INVENTORY_LAST_4,
341 &game.panel.inventory_last[3],
345 GAME_PANEL_INVENTORY_LAST_5,
346 &game.panel.inventory_last[4],
350 GAME_PANEL_INVENTORY_LAST_6,
351 &game.panel.inventory_last[5],
355 GAME_PANEL_INVENTORY_LAST_7,
356 &game.panel.inventory_last[6],
360 GAME_PANEL_INVENTORY_LAST_8,
361 &game.panel.inventory_last[7],
405 GAME_PANEL_KEY_WHITE,
406 &game.panel.key_white,
410 GAME_PANEL_KEY_WHITE_COUNT,
411 &game.panel.key_white_count,
420 GAME_PANEL_HIGHSCORE,
421 &game.panel.highscore,
450 GAME_PANEL_SHIELD_NORMAL,
451 &game.panel.shield_normal,
455 GAME_PANEL_SHIELD_NORMAL_TIME,
456 &game.panel.shield_normal_time,
460 GAME_PANEL_SHIELD_DEADLY,
461 &game.panel.shield_deadly,
465 GAME_PANEL_SHIELD_DEADLY_TIME,
466 &game.panel.shield_deadly_time,
475 GAME_PANEL_EMC_MAGIC_BALL,
476 &game.panel.emc_magic_ball,
480 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
481 &game.panel.emc_magic_ball_switch,
485 GAME_PANEL_LIGHT_SWITCH,
486 &game.panel.light_switch,
490 GAME_PANEL_LIGHT_SWITCH_TIME,
491 &game.panel.light_switch_time,
495 GAME_PANEL_TIMEGATE_SWITCH,
496 &game.panel.timegate_switch,
500 GAME_PANEL_TIMEGATE_SWITCH_TIME,
501 &game.panel.timegate_switch_time,
505 GAME_PANEL_SWITCHGATE_SWITCH,
506 &game.panel.switchgate_switch,
510 GAME_PANEL_EMC_LENSES,
511 &game.panel.emc_lenses,
515 GAME_PANEL_EMC_LENSES_TIME,
516 &game.panel.emc_lenses_time,
520 GAME_PANEL_EMC_MAGNIFIER,
521 &game.panel.emc_magnifier,
525 GAME_PANEL_EMC_MAGNIFIER_TIME,
526 &game.panel.emc_magnifier_time,
530 GAME_PANEL_BALLOON_SWITCH,
531 &game.panel.balloon_switch,
535 GAME_PANEL_DYNABOMB_NUMBER,
536 &game.panel.dynabomb_number,
540 GAME_PANEL_DYNABOMB_SIZE,
541 &game.panel.dynabomb_size,
545 GAME_PANEL_DYNABOMB_POWER,
546 &game.panel.dynabomb_power,
551 &game.panel.penguins,
555 GAME_PANEL_SOKOBAN_OBJECTS,
556 &game.panel.sokoban_objects,
560 GAME_PANEL_SOKOBAN_FIELDS,
561 &game.panel.sokoban_fields,
565 GAME_PANEL_ROBOT_WHEEL,
566 &game.panel.robot_wheel,
570 GAME_PANEL_CONVEYOR_BELT_1,
571 &game.panel.conveyor_belt[0],
575 GAME_PANEL_CONVEYOR_BELT_2,
576 &game.panel.conveyor_belt[1],
580 GAME_PANEL_CONVEYOR_BELT_3,
581 &game.panel.conveyor_belt[2],
585 GAME_PANEL_CONVEYOR_BELT_4,
586 &game.panel.conveyor_belt[3],
590 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
591 &game.panel.conveyor_belt_switch[0],
595 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
596 &game.panel.conveyor_belt_switch[1],
600 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
601 &game.panel.conveyor_belt_switch[2],
605 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
606 &game.panel.conveyor_belt_switch[3],
610 GAME_PANEL_MAGIC_WALL,
611 &game.panel.magic_wall,
615 GAME_PANEL_MAGIC_WALL_TIME,
616 &game.panel.magic_wall_time,
620 GAME_PANEL_GRAVITY_STATE,
621 &game.panel.gravity_state,
625 GAME_PANEL_GRAPHIC_1,
626 &game.panel.graphic[0],
630 GAME_PANEL_GRAPHIC_2,
631 &game.panel.graphic[1],
635 GAME_PANEL_GRAPHIC_3,
636 &game.panel.graphic[2],
640 GAME_PANEL_GRAPHIC_4,
641 &game.panel.graphic[3],
645 GAME_PANEL_GRAPHIC_5,
646 &game.panel.graphic[4],
650 GAME_PANEL_GRAPHIC_6,
651 &game.panel.graphic[5],
655 GAME_PANEL_GRAPHIC_7,
656 &game.panel.graphic[6],
660 GAME_PANEL_GRAPHIC_8,
661 &game.panel.graphic[7],
665 GAME_PANEL_ELEMENT_1,
666 &game.panel.element[0],
670 GAME_PANEL_ELEMENT_2,
671 &game.panel.element[1],
675 GAME_PANEL_ELEMENT_3,
676 &game.panel.element[2],
680 GAME_PANEL_ELEMENT_4,
681 &game.panel.element[3],
685 GAME_PANEL_ELEMENT_5,
686 &game.panel.element[4],
690 GAME_PANEL_ELEMENT_6,
691 &game.panel.element[5],
695 GAME_PANEL_ELEMENT_7,
696 &game.panel.element[6],
700 GAME_PANEL_ELEMENT_8,
701 &game.panel.element[7],
705 GAME_PANEL_ELEMENT_COUNT_1,
706 &game.panel.element_count[0],
710 GAME_PANEL_ELEMENT_COUNT_2,
711 &game.panel.element_count[1],
715 GAME_PANEL_ELEMENT_COUNT_3,
716 &game.panel.element_count[2],
720 GAME_PANEL_ELEMENT_COUNT_4,
721 &game.panel.element_count[3],
725 GAME_PANEL_ELEMENT_COUNT_5,
726 &game.panel.element_count[4],
730 GAME_PANEL_ELEMENT_COUNT_6,
731 &game.panel.element_count[5],
735 GAME_PANEL_ELEMENT_COUNT_7,
736 &game.panel.element_count[6],
740 GAME_PANEL_ELEMENT_COUNT_8,
741 &game.panel.element_count[7],
745 GAME_PANEL_CE_SCORE_1,
746 &game.panel.ce_score[0],
750 GAME_PANEL_CE_SCORE_2,
751 &game.panel.ce_score[1],
755 GAME_PANEL_CE_SCORE_3,
756 &game.panel.ce_score[2],
760 GAME_PANEL_CE_SCORE_4,
761 &game.panel.ce_score[3],
765 GAME_PANEL_CE_SCORE_5,
766 &game.panel.ce_score[4],
770 GAME_PANEL_CE_SCORE_6,
771 &game.panel.ce_score[5],
775 GAME_PANEL_CE_SCORE_7,
776 &game.panel.ce_score[6],
780 GAME_PANEL_CE_SCORE_8,
781 &game.panel.ce_score[7],
785 GAME_PANEL_CE_SCORE_1_ELEMENT,
786 &game.panel.ce_score_element[0],
790 GAME_PANEL_CE_SCORE_2_ELEMENT,
791 &game.panel.ce_score_element[1],
795 GAME_PANEL_CE_SCORE_3_ELEMENT,
796 &game.panel.ce_score_element[2],
800 GAME_PANEL_CE_SCORE_4_ELEMENT,
801 &game.panel.ce_score_element[3],
805 GAME_PANEL_CE_SCORE_5_ELEMENT,
806 &game.panel.ce_score_element[4],
810 GAME_PANEL_CE_SCORE_6_ELEMENT,
811 &game.panel.ce_score_element[5],
815 GAME_PANEL_CE_SCORE_7_ELEMENT,
816 &game.panel.ce_score_element[6],
820 GAME_PANEL_CE_SCORE_8_ELEMENT,
821 &game.panel.ce_score_element[7],
825 GAME_PANEL_PLAYER_NAME,
826 &game.panel.player_name,
830 GAME_PANEL_LEVEL_NAME,
831 &game.panel.level_name,
835 GAME_PANEL_LEVEL_AUTHOR,
836 &game.panel.level_author,
847 /* values for delayed check of falling and moving elements and for collision */
848 #define CHECK_DELAY_MOVING 3
849 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
850 #define CHECK_DELAY_COLLISION 2
851 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
853 /* values for initial player move delay (initial delay counter value) */
854 #define INITIAL_MOVE_DELAY_OFF -1
855 #define INITIAL_MOVE_DELAY_ON 0
857 /* values for player movement speed (which is in fact a delay value) */
858 #define MOVE_DELAY_MIN_SPEED 32
859 #define MOVE_DELAY_NORMAL_SPEED 8
860 #define MOVE_DELAY_HIGH_SPEED 4
861 #define MOVE_DELAY_MAX_SPEED 1
863 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
864 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
866 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
867 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
869 /* values for other actions */
870 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
871 #define MOVE_STEPSIZE_MIN (1)
872 #define MOVE_STEPSIZE_MAX (TILEX)
874 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
875 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
877 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
879 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
880 RND(element_info[e].push_delay_random))
881 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
882 RND(element_info[e].drop_delay_random))
883 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
884 RND(element_info[e].move_delay_random))
885 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
886 (element_info[e].move_delay_random))
887 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
888 RND(element_info[e].ce_value_random_initial))
889 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
890 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
891 RND((c)->delay_random * (c)->delay_frames))
892 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
893 RND((c)->delay_random))
896 #define GET_VALID_RUNTIME_ELEMENT(e) \
897 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
899 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
900 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
901 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
902 (be) + (e) - EL_SELF)
904 #define GET_PLAYER_FROM_BITS(p) \
905 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
907 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
908 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
909 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
910 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
911 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
912 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
913 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
914 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
915 RESOLVED_REFERENCE_ELEMENT(be, e) : \
918 #define CAN_GROW_INTO(e) \
919 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
921 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
922 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
925 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
926 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
927 (CAN_MOVE_INTO_ACID(e) && \
928 Feld[x][y] == EL_ACID) || \
931 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
932 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
933 (CAN_MOVE_INTO_ACID(e) && \
934 Feld[x][y] == EL_ACID) || \
937 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
938 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
940 (CAN_MOVE_INTO_ACID(e) && \
941 Feld[x][y] == EL_ACID) || \
942 (DONT_COLLIDE_WITH(e) && \
944 !PLAYER_ENEMY_PROTECTED(x, y))))
946 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
947 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
949 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
952 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
955 #define ANDROID_CAN_CLONE_FIELD(x, y) \
956 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
957 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
959 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
960 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
962 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
963 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
965 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
966 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
968 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
971 #define PIG_CAN_ENTER_FIELD(e, x, y) \
972 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
974 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
975 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
976 Feld[x][y] == EL_EM_EXIT_OPEN || \
977 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
978 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
979 IS_FOOD_PENGUIN(Feld[x][y])))
980 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
981 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
984 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
986 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
987 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
989 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
990 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
991 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
993 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
995 #define CE_ENTER_FIELD_COND(e, x, y) \
996 (!IS_PLAYER(x, y) && \
997 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
999 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1000 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1002 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1003 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1005 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1006 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1007 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1008 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1010 /* game button identifiers */
1011 #define GAME_CTRL_ID_STOP 0
1012 #define GAME_CTRL_ID_PAUSE 1
1013 #define GAME_CTRL_ID_PLAY 2
1014 #define SOUND_CTRL_ID_MUSIC 3
1015 #define SOUND_CTRL_ID_LOOPS 4
1016 #define SOUND_CTRL_ID_SIMPLE 5
1017 #define GAME_CTRL_ID_SAVE 6
1018 #define GAME_CTRL_ID_LOAD 7
1020 #define NUM_GAME_BUTTONS 8
1023 /* forward declaration for internal use */
1025 static void CreateField(int, int, int);
1027 static void ResetGfxAnimation(int, int);
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1053 static void TestIfElementSmashesCustomElement(int, int, int);
1056 static void HandleElementChange(int, int, int);
1057 static void ExecuteCustomElementAction(int, int, int, int);
1058 static boolean ChangeElement(int, int, int, int);
1060 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1061 #define CheckTriggeredElementChange(x, y, e, ev) \
1062 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1063 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1064 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1065 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1066 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1067 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1068 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1070 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1071 #define CheckElementChange(x, y, e, te, ev) \
1072 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1073 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1074 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1075 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1076 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1078 static void PlayLevelSound(int, int, int);
1079 static void PlayLevelSoundNearest(int, int, int);
1080 static void PlayLevelSoundAction(int, int, int);
1081 static void PlayLevelSoundElementAction(int, int, int, int);
1082 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1083 static void PlayLevelSoundActionIfLoop(int, int, int);
1084 static void StopLevelSoundActionIfLoop(int, int, int);
1085 static void PlayLevelMusic();
1087 static void HandleGameButtons(struct GadgetInfo *);
1089 int AmoebeNachbarNr(int, int);
1090 void AmoebeUmwandeln(int, int);
1091 void ContinueMoving(int, int);
1092 void Bang(int, int);
1093 void InitMovDir(int, int);
1094 void InitAmoebaNr(int, int);
1095 int NewHiScore(void);
1097 void TestIfGoodThingHitsBadThing(int, int, int);
1098 void TestIfBadThingHitsGoodThing(int, int, int);
1099 void TestIfPlayerTouchesBadThing(int, int);
1100 void TestIfPlayerRunsIntoBadThing(int, int, int);
1101 void TestIfBadThingTouchesPlayer(int, int);
1102 void TestIfBadThingRunsIntoPlayer(int, int, int);
1103 void TestIfFriendTouchesBadThing(int, int);
1104 void TestIfBadThingTouchesFriend(int, int);
1105 void TestIfBadThingTouchesOtherBadThing(int, int);
1106 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1108 void KillPlayer(struct PlayerInfo *);
1109 void BuryPlayer(struct PlayerInfo *);
1110 void RemovePlayer(struct PlayerInfo *);
1112 static int getInvisibleActiveFromInvisibleElement(int);
1113 static int getInvisibleFromInvisibleActiveElement(int);
1115 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1117 /* for detection of endless loops, caused by custom element programming */
1118 /* (using maximal playfield width x 10 is just a rough approximation) */
1119 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1121 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1123 if (recursion_loop_detected) \
1126 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1128 recursion_loop_detected = TRUE; \
1129 recursion_loop_element = (e); \
1132 recursion_loop_depth++; \
1135 #define RECURSION_LOOP_DETECTION_END() \
1137 recursion_loop_depth--; \
1140 static int recursion_loop_depth;
1141 static boolean recursion_loop_detected;
1142 static boolean recursion_loop_element;
1144 static int map_player_action[MAX_PLAYERS];
1147 /* ------------------------------------------------------------------------- */
1148 /* definition of elements that automatically change to other elements after */
1149 /* a specified time, eventually calling a function when changing */
1150 /* ------------------------------------------------------------------------- */
1152 /* forward declaration for changer functions */
1153 static void InitBuggyBase(int, int);
1154 static void WarnBuggyBase(int, int);
1156 static void InitTrap(int, int);
1157 static void ActivateTrap(int, int);
1158 static void ChangeActiveTrap(int, int);
1160 static void InitRobotWheel(int, int);
1161 static void RunRobotWheel(int, int);
1162 static void StopRobotWheel(int, int);
1164 static void InitTimegateWheel(int, int);
1165 static void RunTimegateWheel(int, int);
1167 static void InitMagicBallDelay(int, int);
1168 static void ActivateMagicBall(int, int);
1170 struct ChangingElementInfo
1175 void (*pre_change_function)(int x, int y);
1176 void (*change_function)(int x, int y);
1177 void (*post_change_function)(int x, int y);
1180 static struct ChangingElementInfo change_delay_list[] =
1215 EL_STEEL_EXIT_OPENING,
1223 EL_STEEL_EXIT_CLOSING,
1224 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1263 EL_EM_STEEL_EXIT_CLOSED,
1287 EL_SWITCHGATE_OPENING,
1295 EL_SWITCHGATE_CLOSING,
1296 EL_SWITCHGATE_CLOSED,
1303 EL_TIMEGATE_OPENING,
1311 EL_TIMEGATE_CLOSING,
1320 EL_ACID_SPLASH_LEFT,
1328 EL_ACID_SPLASH_RIGHT,
1337 EL_SP_BUGGY_BASE_ACTIVATING,
1344 EL_SP_BUGGY_BASE_ACTIVATING,
1345 EL_SP_BUGGY_BASE_ACTIVE,
1352 EL_SP_BUGGY_BASE_ACTIVE,
1376 EL_ROBOT_WHEEL_ACTIVE,
1384 EL_TIMEGATE_SWITCH_ACTIVE,
1392 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1393 EL_DC_TIMEGATE_SWITCH,
1400 EL_EMC_MAGIC_BALL_ACTIVE,
1401 EL_EMC_MAGIC_BALL_ACTIVE,
1408 EL_EMC_SPRING_BUMPER_ACTIVE,
1409 EL_EMC_SPRING_BUMPER,
1416 EL_DIAGONAL_SHRINKING,
1424 EL_DIAGONAL_GROWING,
1445 int push_delay_fixed, push_delay_random;
1449 { EL_SPRING, 0, 0 },
1450 { EL_BALLOON, 0, 0 },
1452 { EL_SOKOBAN_OBJECT, 2, 0 },
1453 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1454 { EL_SATELLITE, 2, 0 },
1455 { EL_SP_DISK_YELLOW, 2, 0 },
1457 { EL_UNDEFINED, 0, 0 },
1465 move_stepsize_list[] =
1467 { EL_AMOEBA_DROP, 2 },
1468 { EL_AMOEBA_DROPPING, 2 },
1469 { EL_QUICKSAND_FILLING, 1 },
1470 { EL_QUICKSAND_EMPTYING, 1 },
1471 { EL_QUICKSAND_FAST_FILLING, 2 },
1472 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1473 { EL_MAGIC_WALL_FILLING, 2 },
1474 { EL_MAGIC_WALL_EMPTYING, 2 },
1475 { EL_BD_MAGIC_WALL_FILLING, 2 },
1476 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1477 { EL_DC_MAGIC_WALL_FILLING, 2 },
1478 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1480 { EL_UNDEFINED, 0 },
1488 collect_count_list[] =
1491 { EL_BD_DIAMOND, 1 },
1492 { EL_EMERALD_YELLOW, 1 },
1493 { EL_EMERALD_RED, 1 },
1494 { EL_EMERALD_PURPLE, 1 },
1496 { EL_SP_INFOTRON, 1 },
1500 { EL_UNDEFINED, 0 },
1508 access_direction_list[] =
1510 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1512 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1513 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1514 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1515 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1516 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1517 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1518 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1519 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1520 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1522 { EL_SP_PORT_LEFT, MV_RIGHT },
1523 { EL_SP_PORT_RIGHT, MV_LEFT },
1524 { EL_SP_PORT_UP, MV_DOWN },
1525 { EL_SP_PORT_DOWN, MV_UP },
1526 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1527 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1528 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1529 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1537 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1538 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1539 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1540 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1542 { EL_UNDEFINED, MV_NONE }
1545 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1547 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1548 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1549 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1550 IS_JUST_CHANGING(x, y))
1552 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1554 /* static variables for playfield scan mode (scanning forward or backward) */
1555 static int playfield_scan_start_x = 0;
1556 static int playfield_scan_start_y = 0;
1557 static int playfield_scan_delta_x = 1;
1558 static int playfield_scan_delta_y = 1;
1560 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1561 (y) >= 0 && (y) <= lev_fieldy - 1; \
1562 (y) += playfield_scan_delta_y) \
1563 for ((x) = playfield_scan_start_x; \
1564 (x) >= 0 && (x) <= lev_fieldx - 1; \
1565 (x) += playfield_scan_delta_x)
1568 void DEBUG_SetMaximumDynamite()
1572 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1573 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1574 local_player->inventory_element[local_player->inventory_size++] =
1579 static void InitPlayfieldScanModeVars()
1581 if (game.use_reverse_scan_direction)
1583 playfield_scan_start_x = lev_fieldx - 1;
1584 playfield_scan_start_y = lev_fieldy - 1;
1586 playfield_scan_delta_x = -1;
1587 playfield_scan_delta_y = -1;
1591 playfield_scan_start_x = 0;
1592 playfield_scan_start_y = 0;
1594 playfield_scan_delta_x = 1;
1595 playfield_scan_delta_y = 1;
1599 static void InitPlayfieldScanMode(int mode)
1601 game.use_reverse_scan_direction =
1602 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1604 InitPlayfieldScanModeVars();
1607 static int get_move_delay_from_stepsize(int move_stepsize)
1610 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1612 /* make sure that stepsize value is always a power of 2 */
1613 move_stepsize = (1 << log_2(move_stepsize));
1615 return TILEX / move_stepsize;
1618 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1621 int player_nr = player->index_nr;
1622 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1623 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1625 /* do no immediately change move delay -- the player might just be moving */
1626 player->move_delay_value_next = move_delay;
1628 /* information if player can move must be set separately */
1629 player->cannot_move = cannot_move;
1633 player->move_delay = game.initial_move_delay[player_nr];
1634 player->move_delay_value = game.initial_move_delay_value[player_nr];
1636 player->move_delay_value_next = -1;
1638 player->move_delay_reset_counter = 0;
1642 void GetPlayerConfig()
1644 GameFrameDelay = setup.game_frame_delay;
1646 if (!audio.sound_available)
1647 setup.sound_simple = FALSE;
1649 if (!audio.loops_available)
1650 setup.sound_loops = FALSE;
1652 if (!audio.music_available)
1653 setup.sound_music = FALSE;
1655 if (!video.fullscreen_available)
1656 setup.fullscreen = FALSE;
1658 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1660 SetAudioMode(setup.sound);
1664 int GetElementFromGroupElement(int element)
1666 if (IS_GROUP_ELEMENT(element))
1668 struct ElementGroupInfo *group = element_info[element].group;
1669 int last_anim_random_frame = gfx.anim_random_frame;
1672 if (group->choice_mode == ANIM_RANDOM)
1673 gfx.anim_random_frame = RND(group->num_elements_resolved);
1675 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676 group->choice_mode, 0,
1679 if (group->choice_mode == ANIM_RANDOM)
1680 gfx.anim_random_frame = last_anim_random_frame;
1682 group->choice_pos++;
1684 element = group->element_resolved[element_pos];
1690 static void InitPlayerField(int x, int y, int element, boolean init_game)
1692 if (element == EL_SP_MURPHY)
1696 if (stored_player[0].present)
1698 Feld[x][y] = EL_SP_MURPHY_CLONE;
1704 stored_player[0].initial_element = element;
1705 stored_player[0].use_murphy = TRUE;
1707 if (!level.use_artwork_element[0])
1708 stored_player[0].artwork_element = EL_SP_MURPHY;
1711 Feld[x][y] = EL_PLAYER_1;
1717 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1718 int jx = player->jx, jy = player->jy;
1720 player->present = TRUE;
1722 player->block_last_field = (element == EL_SP_MURPHY ?
1723 level.sp_block_last_field :
1724 level.block_last_field);
1726 /* ---------- initialize player's last field block delay --------------- */
1728 /* always start with reliable default value (no adjustment needed) */
1729 player->block_delay_adjustment = 0;
1731 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1732 if (player->block_last_field && element == EL_SP_MURPHY)
1733 player->block_delay_adjustment = 1;
1735 /* special case 2: in game engines before 3.1.1, blocking was different */
1736 if (game.use_block_last_field_bug)
1737 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1739 if (!options.network || player->connected)
1741 player->active = TRUE;
1743 /* remove potentially duplicate players */
1744 if (StorePlayer[jx][jy] == Feld[x][y])
1745 StorePlayer[jx][jy] = 0;
1747 StorePlayer[x][y] = Feld[x][y];
1749 #if DEBUG_INIT_PLAYER
1752 printf("- player element %d activated", player->element_nr);
1753 printf(" (local player is %d and currently %s)\n",
1754 local_player->element_nr,
1755 local_player->active ? "active" : "not active");
1760 Feld[x][y] = EL_EMPTY;
1762 player->jx = player->last_jx = x;
1763 player->jy = player->last_jy = y;
1766 #if USE_PLAYER_REANIMATION
1769 int player_nr = GET_PLAYER_NR(element);
1770 struct PlayerInfo *player = &stored_player[player_nr];
1772 if (player->active && player->killed)
1773 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1778 static void InitField(int x, int y, boolean init_game)
1780 int element = Feld[x][y];
1789 InitPlayerField(x, y, element, init_game);
1792 case EL_SOKOBAN_FIELD_PLAYER:
1793 element = Feld[x][y] = EL_PLAYER_1;
1794 InitField(x, y, init_game);
1796 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1797 InitField(x, y, init_game);
1800 case EL_SOKOBAN_FIELD_EMPTY:
1801 local_player->sokobanfields_still_needed++;
1805 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1806 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1807 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1808 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1809 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1810 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1811 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1812 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1813 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1814 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1823 case EL_SPACESHIP_RIGHT:
1824 case EL_SPACESHIP_UP:
1825 case EL_SPACESHIP_LEFT:
1826 case EL_SPACESHIP_DOWN:
1827 case EL_BD_BUTTERFLY:
1828 case EL_BD_BUTTERFLY_RIGHT:
1829 case EL_BD_BUTTERFLY_UP:
1830 case EL_BD_BUTTERFLY_LEFT:
1831 case EL_BD_BUTTERFLY_DOWN:
1833 case EL_BD_FIREFLY_RIGHT:
1834 case EL_BD_FIREFLY_UP:
1835 case EL_BD_FIREFLY_LEFT:
1836 case EL_BD_FIREFLY_DOWN:
1837 case EL_PACMAN_RIGHT:
1839 case EL_PACMAN_LEFT:
1840 case EL_PACMAN_DOWN:
1842 case EL_YAMYAM_LEFT:
1843 case EL_YAMYAM_RIGHT:
1845 case EL_YAMYAM_DOWN:
1846 case EL_DARK_YAMYAM:
1849 case EL_SP_SNIKSNAK:
1850 case EL_SP_ELECTRON:
1859 case EL_AMOEBA_FULL:
1864 case EL_AMOEBA_DROP:
1865 if (y == lev_fieldy - 1)
1867 Feld[x][y] = EL_AMOEBA_GROWING;
1868 Store[x][y] = EL_AMOEBA_WET;
1872 case EL_DYNAMITE_ACTIVE:
1873 case EL_SP_DISK_RED_ACTIVE:
1874 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1875 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1876 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1877 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1878 MovDelay[x][y] = 96;
1881 case EL_EM_DYNAMITE_ACTIVE:
1882 MovDelay[x][y] = 32;
1886 local_player->lights_still_needed++;
1890 local_player->friends_still_needed++;
1895 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1898 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1899 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1900 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1901 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1902 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1903 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1904 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1905 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1906 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1907 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1908 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1909 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1912 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1913 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1914 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1916 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1918 game.belt_dir[belt_nr] = belt_dir;
1919 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1921 else /* more than one switch -- set it like the first switch */
1923 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1928 #if !USE_BOTH_SWITCHGATE_SWITCHES
1929 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1931 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1934 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1936 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1940 case EL_LIGHT_SWITCH_ACTIVE:
1942 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1945 case EL_INVISIBLE_STEELWALL:
1946 case EL_INVISIBLE_WALL:
1947 case EL_INVISIBLE_SAND:
1948 if (game.light_time_left > 0 ||
1949 game.lenses_time_left > 0)
1950 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1953 case EL_EMC_MAGIC_BALL:
1954 if (game.ball_state)
1955 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1958 case EL_EMC_MAGIC_BALL_SWITCH:
1959 if (game.ball_state)
1960 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1963 case EL_TRIGGER_PLAYER:
1964 case EL_TRIGGER_ELEMENT:
1965 case EL_TRIGGER_CE_VALUE:
1966 case EL_TRIGGER_CE_SCORE:
1968 case EL_ANY_ELEMENT:
1969 case EL_CURRENT_CE_VALUE:
1970 case EL_CURRENT_CE_SCORE:
1987 /* reference elements should not be used on the playfield */
1988 Feld[x][y] = EL_EMPTY;
1992 if (IS_CUSTOM_ELEMENT(element))
1994 if (CAN_MOVE(element))
1997 #if USE_NEW_CUSTOM_VALUE
1998 if (!element_info[element].use_last_ce_value || init_game)
1999 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2002 else if (IS_GROUP_ELEMENT(element))
2004 Feld[x][y] = GetElementFromGroupElement(element);
2006 InitField(x, y, init_game);
2013 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2016 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2018 InitField(x, y, init_game);
2020 /* not needed to call InitMovDir() -- already done by InitField()! */
2021 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2022 CAN_MOVE(Feld[x][y]))
2026 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2028 int old_element = Feld[x][y];
2030 InitField(x, y, init_game);
2032 /* not needed to call InitMovDir() -- already done by InitField()! */
2033 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034 CAN_MOVE(old_element) &&
2035 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2038 /* this case is in fact a combination of not less than three bugs:
2039 first, it calls InitMovDir() for elements that can move, although this is
2040 already done by InitField(); then, it checks the element that was at this
2041 field _before_ the call to InitField() (which can change it); lastly, it
2042 was not called for "mole with direction" elements, which were treated as
2043 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2047 static int get_key_element_from_nr(int key_nr)
2049 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2050 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2051 EL_EM_KEY_1 : EL_KEY_1);
2053 return key_base_element + key_nr;
2056 static int get_next_dropped_element(struct PlayerInfo *player)
2058 return (player->inventory_size > 0 ?
2059 player->inventory_element[player->inventory_size - 1] :
2060 player->inventory_infinite_element != EL_UNDEFINED ?
2061 player->inventory_infinite_element :
2062 player->dynabombs_left > 0 ?
2063 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2067 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2069 /* pos >= 0: get element from bottom of the stack;
2070 pos < 0: get element from top of the stack */
2074 int min_inventory_size = -pos;
2075 int inventory_pos = player->inventory_size - min_inventory_size;
2076 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2078 return (player->inventory_size >= min_inventory_size ?
2079 player->inventory_element[inventory_pos] :
2080 player->inventory_infinite_element != EL_UNDEFINED ?
2081 player->inventory_infinite_element :
2082 player->dynabombs_left >= min_dynabombs_left ?
2083 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088 int min_dynabombs_left = pos + 1;
2089 int min_inventory_size = pos + 1 - player->dynabombs_left;
2090 int inventory_pos = pos - player->dynabombs_left;
2092 return (player->inventory_infinite_element != EL_UNDEFINED ?
2093 player->inventory_infinite_element :
2094 player->dynabombs_left >= min_dynabombs_left ?
2095 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096 player->inventory_size >= min_inventory_size ?
2097 player->inventory_element[inventory_pos] :
2102 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2104 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2105 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2108 if (gpo1->sort_priority != gpo2->sort_priority)
2109 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2111 compare_result = gpo1->nr - gpo2->nr;
2113 return compare_result;
2116 void InitGameControlValues()
2120 for (i = 0; game_panel_controls[i].nr != -1; i++)
2122 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2123 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2124 struct TextPosInfo *pos = gpc->pos;
2126 int type = gpc->type;
2130 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2131 Error(ERR_EXIT, "this should not happen -- please debug");
2134 /* force update of game controls after initialization */
2135 gpc->value = gpc->last_value = -1;
2136 gpc->frame = gpc->last_frame = -1;
2137 gpc->gfx_frame = -1;
2139 /* determine panel value width for later calculation of alignment */
2140 if (type == TYPE_INTEGER || type == TYPE_STRING)
2142 pos->width = pos->size * getFontWidth(pos->font);
2143 pos->height = getFontHeight(pos->font);
2145 else if (type == TYPE_ELEMENT)
2147 pos->width = pos->size;
2148 pos->height = pos->size;
2151 /* fill structure for game panel draw order */
2153 gpo->sort_priority = pos->sort_priority;
2156 /* sort game panel controls according to sort_priority and control number */
2157 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2158 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2161 void UpdatePlayfieldElementCount()
2163 boolean use_element_count = FALSE;
2166 /* first check if it is needed at all to calculate playfield element count */
2167 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2168 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2169 use_element_count = TRUE;
2171 if (!use_element_count)
2174 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2175 element_info[i].element_count = 0;
2177 SCAN_PLAYFIELD(x, y)
2179 element_info[Feld[x][y]].element_count++;
2182 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2183 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2184 if (IS_IN_GROUP(j, i))
2185 element_info[EL_GROUP_START + i].element_count +=
2186 element_info[j].element_count;
2189 void UpdateGameControlValues()
2192 int time = (local_player->LevelSolved ?
2193 local_player->LevelSolved_CountingTime :
2194 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2195 level.native_em_level->lev->time :
2196 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2197 level.native_sp_level->game_sp->time_played :
2198 game.no_time_limit ? TimePlayed : TimeLeft);
2199 int score = (local_player->LevelSolved ?
2200 local_player->LevelSolved_CountingScore :
2201 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202 level.native_em_level->lev->score :
2203 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2204 level.native_sp_level->game_sp->score :
2205 local_player->score);
2206 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed :
2210 local_player->gems_still_needed);
2211 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212 level.native_em_level->lev->required > 0 :
2213 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2214 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2215 local_player->gems_still_needed > 0 ||
2216 local_player->sokobanfields_still_needed > 0 ||
2217 local_player->lights_still_needed > 0);
2219 UpdatePlayfieldElementCount();
2221 /* update game panel control values */
2223 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2224 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2226 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2227 for (i = 0; i < MAX_NUM_KEYS; i++)
2228 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2229 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2230 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2232 if (game.centered_player_nr == -1)
2234 for (i = 0; i < MAX_PLAYERS; i++)
2236 /* only one player in Supaplex game engine */
2237 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2240 for (k = 0; k < MAX_NUM_KEYS; k++)
2242 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2244 if (level.native_em_level->ply[i]->keys & (1 << k))
2245 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2246 get_key_element_from_nr(k);
2248 else if (stored_player[i].key[k])
2249 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250 get_key_element_from_nr(k);
2253 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255 level.native_em_level->ply[i]->dynamite;
2256 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2257 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258 level.native_sp_level->game_sp->red_disk_count;
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2261 stored_player[i].inventory_size;
2263 if (stored_player[i].num_white_keys > 0)
2264 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2267 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2268 stored_player[i].num_white_keys;
2273 int player_nr = game.centered_player_nr;
2275 for (k = 0; k < MAX_NUM_KEYS; k++)
2277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2283 else if (stored_player[player_nr].key[k])
2284 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2285 get_key_element_from_nr(k);
2288 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290 level.native_em_level->ply[player_nr]->dynamite;
2291 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2292 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2293 level.native_sp_level->game_sp->red_disk_count;
2295 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2296 stored_player[player_nr].inventory_size;
2298 if (stored_player[player_nr].num_white_keys > 0)
2299 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[player_nr].num_white_keys;
2305 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2307 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2308 get_inventory_element_from_pos(local_player, i);
2309 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2310 get_inventory_element_from_pos(local_player, -i - 1);
2313 game_panel_controls[GAME_PANEL_SCORE].value = score;
2314 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2316 game_panel_controls[GAME_PANEL_TIME].value = time;
2318 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2319 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2320 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2322 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2325 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2327 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2328 local_player->shield_normal_time_left;
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2330 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2332 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2333 local_player->shield_deadly_time_left;
2335 game_panel_controls[GAME_PANEL_EXIT].value =
2336 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2338 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2339 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2340 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2341 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2342 EL_EMC_MAGIC_BALL_SWITCH);
2344 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2345 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2346 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2347 game.light_time_left;
2349 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2350 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2351 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2352 game.timegate_time_left;
2354 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2355 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2357 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2358 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2359 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2360 game.lenses_time_left;
2362 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2363 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2364 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2365 game.magnify_time_left;
2367 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2368 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2369 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2370 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2371 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2372 EL_BALLOON_SWITCH_NONE);
2374 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2375 local_player->dynabomb_count;
2376 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2377 local_player->dynabomb_size;
2378 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2379 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2381 game_panel_controls[GAME_PANEL_PENGUINS].value =
2382 local_player->friends_still_needed;
2384 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2385 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2387 local_player->sokobanfields_still_needed;
2389 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2390 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2392 for (i = 0; i < NUM_BELTS; i++)
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2395 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2396 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2397 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2398 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2401 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2402 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2403 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2404 game.magic_wall_time_left;
2406 #if USE_PLAYER_GRAVITY
2407 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2408 local_player->gravity;
2410 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2413 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2414 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2416 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2418 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2419 game.panel.element[i].id : EL_UNDEFINED);
2421 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2422 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2423 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2424 element_info[game.panel.element_count[i].id].element_count : 0);
2426 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2428 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2429 element_info[game.panel.ce_score[i].id].collect_score : 0);
2431 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2432 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2433 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2434 element_info[game.panel.ce_score_element[i].id].collect_score :
2437 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2438 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2439 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2441 /* update game panel control frames */
2443 for (i = 0; game_panel_controls[i].nr != -1; i++)
2445 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2447 if (gpc->type == TYPE_ELEMENT)
2449 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2451 int last_anim_random_frame = gfx.anim_random_frame;
2452 int element = gpc->value;
2453 int graphic = el2panelimg(element);
2455 if (gpc->value != gpc->last_value)
2458 gpc->gfx_random = INIT_GFX_RANDOM();
2464 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2465 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2466 gpc->gfx_random = INIT_GFX_RANDOM();
2469 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2470 gfx.anim_random_frame = gpc->gfx_random;
2472 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2473 gpc->gfx_frame = element_info[element].collect_score;
2475 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2478 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2479 gfx.anim_random_frame = last_anim_random_frame;
2485 void DisplayGameControlValues()
2487 boolean redraw_panel = FALSE;
2490 for (i = 0; game_panel_controls[i].nr != -1; i++)
2492 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2494 if (PANEL_DEACTIVATED(gpc->pos))
2497 if (gpc->value == gpc->last_value &&
2498 gpc->frame == gpc->last_frame)
2501 redraw_panel = TRUE;
2507 /* copy default game door content to main double buffer */
2509 /* !!! CHECK AGAIN !!! */
2510 SetPanelBackground();
2511 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2512 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2514 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518 /* redraw game control buttons */
2520 RedrawGameButtons();
2526 game_status = GAME_MODE_PSEUDO_PANEL;
2529 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 for (i = 0; game_panel_controls[i].nr != -1; i++)
2535 int nr = game_panel_order[i].nr;
2536 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541 struct TextPosInfo *pos = gpc->pos;
2542 int type = gpc->type;
2543 int value = gpc->value;
2544 int frame = gpc->frame;
2546 int last_value = gpc->last_value;
2547 int last_frame = gpc->last_frame;
2549 int size = pos->size;
2550 int font = pos->font;
2551 boolean draw_masked = pos->draw_masked;
2552 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554 if (PANEL_DEACTIVATED(pos))
2558 if (value == last_value && frame == last_frame)
2562 gpc->last_value = value;
2563 gpc->last_frame = frame;
2566 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2569 if (type == TYPE_INTEGER)
2571 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2572 nr == GAME_PANEL_TIME)
2574 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576 if (use_dynamic_size) /* use dynamic number of digits */
2578 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2579 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2580 int size2 = size1 + 1;
2581 int font1 = pos->font;
2582 int font2 = pos->font_alt;
2584 size = (value < value_change ? size1 : size2);
2585 font = (value < value_change ? font1 : font2);
2588 /* clear background if value just changed its size (dynamic digits) */
2589 if ((last_value < value_change) != (value < value_change))
2591 int width1 = size1 * getFontWidth(font1);
2592 int width2 = size2 * getFontWidth(font2);
2593 int max_width = MAX(width1, width2);
2594 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596 pos->width = max_width;
2598 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2599 max_width, max_height);
2606 /* correct text size if "digits" is zero or less */
2608 size = strlen(int2str(value, size));
2610 /* dynamically correct text alignment */
2611 pos->width = size * getFontWidth(font);
2614 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2615 int2str(value, size), font, mask_mode);
2617 else if (type == TYPE_ELEMENT)
2619 int element, graphic;
2623 int dst_x = PANEL_XPOS(pos);
2624 int dst_y = PANEL_YPOS(pos);
2627 if (value != EL_UNDEFINED && value != EL_EMPTY)
2630 graphic = el2panelimg(value);
2632 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2635 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642 width = graphic_info[graphic].width * size / TILESIZE;
2643 height = graphic_info[graphic].height * size / TILESIZE;
2647 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2648 dst_x - src_x, dst_y - src_y);
2649 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2654 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2659 if (value == EL_UNDEFINED || value == EL_EMPTY)
2661 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2662 graphic = el2panelimg(element);
2664 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2665 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2666 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2671 graphic = el2panelimg(value);
2673 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2676 width = graphic_info[graphic].width * size / TILESIZE;
2677 height = graphic_info[graphic].height * size / TILESIZE;
2679 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2682 else if (type == TYPE_STRING)
2684 boolean active = (value != 0);
2685 char *state_normal = "off";
2686 char *state_active = "on";
2687 char *state = (active ? state_active : state_normal);
2688 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2689 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2690 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2691 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2693 if (nr == GAME_PANEL_GRAVITY_STATE)
2695 int font1 = pos->font; /* (used for normal state) */
2696 int font2 = pos->font_alt; /* (used for active state) */
2698 int size1 = strlen(state_normal);
2699 int size2 = strlen(state_active);
2700 int width1 = size1 * getFontWidth(font1);
2701 int width2 = size2 * getFontWidth(font2);
2702 int max_width = MAX(width1, width2);
2703 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705 pos->width = max_width;
2707 /* clear background for values that may have changed its size */
2708 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709 max_width, max_height);
2712 font = (active ? font2 : font1);
2722 /* don't truncate output if "chars" is zero or less */
2725 /* dynamically correct text alignment */
2726 pos->width = size * getFontWidth(font);
2730 s_cut = getStringCopyN(s, size);
2732 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2733 s_cut, font, mask_mode);
2739 redraw_mask |= REDRAW_DOOR_1;
2742 game_status = GAME_MODE_PLAYING;
2745 void UpdateAndDisplayGameControlValues()
2747 if (tape.warp_forward)
2750 UpdateGameControlValues();
2751 DisplayGameControlValues();
2754 void DrawGameValue_Emeralds(int value)
2756 struct TextPosInfo *pos = &game.panel.gems;
2757 int font_nr = pos->font;
2758 int font_width = getFontWidth(font_nr);
2759 int chars = pos->size;
2762 return; /* !!! USE NEW STUFF !!! */
2765 if (PANEL_DEACTIVATED(pos))
2768 pos->width = chars * font_width;
2770 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2773 void DrawGameValue_Dynamite(int value)
2775 struct TextPosInfo *pos = &game.panel.inventory_count;
2776 int font_nr = pos->font;
2777 int font_width = getFontWidth(font_nr);
2778 int chars = pos->size;
2781 return; /* !!! USE NEW STUFF !!! */
2784 if (PANEL_DEACTIVATED(pos))
2787 pos->width = chars * font_width;
2789 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2792 void DrawGameValue_Score(int value)
2794 struct TextPosInfo *pos = &game.panel.score;
2795 int font_nr = pos->font;
2796 int font_width = getFontWidth(font_nr);
2797 int chars = pos->size;
2800 return; /* !!! USE NEW STUFF !!! */
2803 if (PANEL_DEACTIVATED(pos))
2806 pos->width = chars * font_width;
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Time(int value)
2813 struct TextPosInfo *pos = &game.panel.time;
2814 static int last_value = -1;
2817 int chars = pos->size;
2818 int font1_nr = pos->font;
2819 int font2_nr = pos->font_alt;
2820 int font_nr = font1_nr;
2821 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2824 return; /* !!! USE NEW STUFF !!! */
2827 if (PANEL_DEACTIVATED(pos))
2830 if (use_dynamic_chars) /* use dynamic number of chars */
2832 chars = (value < 1000 ? chars1 : chars2);
2833 font_nr = (value < 1000 ? font1_nr : font2_nr);
2836 /* clear background if value just changed its size (dynamic chars only) */
2837 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2839 int width1 = chars1 * getFontWidth(font1_nr);
2840 int width2 = chars2 * getFontWidth(font2_nr);
2841 int max_width = MAX(width1, width2);
2842 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2844 pos->width = max_width;
2846 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847 max_width, max_height);
2850 pos->width = chars * getFontWidth(font_nr);
2852 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2857 void DrawGameValue_Level(int value)
2859 struct TextPosInfo *pos = &game.panel.level_number;
2862 int chars = pos->size;
2863 int font1_nr = pos->font;
2864 int font2_nr = pos->font_alt;
2865 int font_nr = font1_nr;
2866 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2869 return; /* !!! USE NEW STUFF !!! */
2872 if (PANEL_DEACTIVATED(pos))
2875 if (use_dynamic_chars) /* use dynamic number of chars */
2877 chars = (level_nr < 100 ? chars1 : chars2);
2878 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2881 pos->width = chars * getFontWidth(font_nr);
2883 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2886 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2891 return; /* !!! USE NEW STUFF !!! */
2894 for (i = 0; i < MAX_NUM_KEYS; i++)
2896 struct TextPosInfo *pos = &game.panel.key[i];
2897 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2898 int src_y = DOOR_GFX_PAGEY1 + 123;
2899 int dst_x = PANEL_XPOS(pos);
2900 int dst_y = PANEL_YPOS(pos);
2902 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2903 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2905 int graphic = el2edimg(element);
2907 if (PANEL_DEACTIVATED(pos))
2911 /* masked blit with tiles from half-size scaled bitmap does not work yet
2912 (no mask bitmap created for these sizes after loading and scaling) --
2913 solution: load without creating mask, scale, then create final mask */
2915 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2916 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2923 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2925 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2926 dst_x - src_x, dst_y - src_y);
2927 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2932 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2934 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2935 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2940 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2943 int key[MAX_NUM_KEYS];
2946 /* prevent EM engine from updating time/score values parallel to GameWon() */
2947 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2948 local_player->LevelSolved)
2951 for (i = 0; i < MAX_NUM_KEYS; i++)
2952 key[i] = key_bits & (1 << i);
2954 DrawGameValue_Level(level_nr);
2956 DrawGameValue_Emeralds(emeralds);
2957 DrawGameValue_Dynamite(dynamite);
2958 DrawGameValue_Score(score);
2959 DrawGameValue_Time(time);
2961 DrawGameValue_Keys(key);
2964 void UpdateGameDoorValues()
2966 UpdateGameControlValues();
2969 void DrawGameDoorValues()
2971 DisplayGameControlValues();
2974 void DrawGameDoorValues_OLD()
2976 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2977 int dynamite_value = 0;
2978 int score_value = (local_player->LevelSolved ? local_player->score_final :
2979 local_player->score);
2980 int gems_value = local_player->gems_still_needed;
2984 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2986 DrawGameDoorValues_EM();
2991 if (game.centered_player_nr == -1)
2993 for (i = 0; i < MAX_PLAYERS; i++)
2995 for (j = 0; j < MAX_NUM_KEYS; j++)
2996 if (stored_player[i].key[j])
2997 key_bits |= (1 << j);
2999 dynamite_value += stored_player[i].inventory_size;
3004 int player_nr = game.centered_player_nr;
3006 for (i = 0; i < MAX_NUM_KEYS; i++)
3007 if (stored_player[player_nr].key[i])
3008 key_bits |= (1 << i);
3010 dynamite_value = stored_player[player_nr].inventory_size;
3013 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3019 =============================================================================
3021 -----------------------------------------------------------------------------
3022 initialize game engine due to level / tape version number
3023 =============================================================================
3026 static void InitGameEngine()
3028 int i, j, k, l, x, y;
3030 /* set game engine from tape file when re-playing, else from level file */
3031 game.engine_version = (tape.playing ? tape.engine_version :
3032 level.game_version);
3034 /* set single or multi-player game mode (needed for re-playing tapes) */
3035 game.team_mode = setup.team_mode;
3039 int num_players = 0;
3041 for (i = 0; i < MAX_PLAYERS; i++)
3042 if (tape.player_participates[i])
3045 /* multi-player tapes contain input data for more than one player */
3046 game.team_mode = (num_players > 1);
3049 /* ---------------------------------------------------------------------- */
3050 /* set flags for bugs and changes according to active game engine version */
3051 /* ---------------------------------------------------------------------- */
3054 Summary of bugfix/change:
3055 Fixed handling for custom elements that change when pushed by the player.
3057 Fixed/changed in version:
3061 Before 3.1.0, custom elements that "change when pushing" changed directly
3062 after the player started pushing them (until then handled in "DigField()").
3063 Since 3.1.0, these custom elements are not changed until the "pushing"
3064 move of the element is finished (now handled in "ContinueMoving()").
3066 Affected levels/tapes:
3067 The first condition is generally needed for all levels/tapes before version
3068 3.1.0, which might use the old behaviour before it was changed; known tapes
3069 that are affected are some tapes from the level set "Walpurgis Gardens" by
3071 The second condition is an exception from the above case and is needed for
3072 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3073 above (including some development versions of 3.1.0), but before it was
3074 known that this change would break tapes like the above and was fixed in
3075 3.1.1, so that the changed behaviour was active although the engine version
3076 while recording maybe was before 3.1.0. There is at least one tape that is
3077 affected by this exception, which is the tape for the one-level set "Bug
3078 Machine" by Juergen Bonhagen.
3081 game.use_change_when_pushing_bug =
3082 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3084 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3085 tape.game_version < VERSION_IDENT(3,1,1,0)));
3088 Summary of bugfix/change:
3089 Fixed handling for blocking the field the player leaves when moving.
3091 Fixed/changed in version:
3095 Before 3.1.1, when "block last field when moving" was enabled, the field
3096 the player is leaving when moving was blocked for the time of the move,
3097 and was directly unblocked afterwards. This resulted in the last field
3098 being blocked for exactly one less than the number of frames of one player
3099 move. Additionally, even when blocking was disabled, the last field was
3100 blocked for exactly one frame.
3101 Since 3.1.1, due to changes in player movement handling, the last field
3102 is not blocked at all when blocking is disabled. When blocking is enabled,
3103 the last field is blocked for exactly the number of frames of one player
3104 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3105 last field is blocked for exactly one more than the number of frames of
3108 Affected levels/tapes:
3109 (!!! yet to be determined -- probably many !!!)
3112 game.use_block_last_field_bug =
3113 (game.engine_version < VERSION_IDENT(3,1,1,0));
3116 Summary of bugfix/change:
3117 Changed behaviour of CE changes with multiple changes per single frame.
3119 Fixed/changed in version:
3123 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3124 This resulted in race conditions where CEs seem to behave strange in some
3125 situations (where triggered CE changes were just skipped because there was
3126 already a CE change on that tile in the playfield in that engine frame).
3127 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3128 (The number of changes per frame must be limited in any case, because else
3129 it is easily possible to define CE changes that would result in an infinite
3130 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3131 should be set large enough so that it would only be reached in cases where
3132 the corresponding CE change conditions run into a loop. Therefore, it seems
3133 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3134 maximal number of change pages for custom elements.)
3136 Affected levels/tapes:
3140 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3141 game.max_num_changes_per_frame = 1;
3143 game.max_num_changes_per_frame =
3144 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3147 /* ---------------------------------------------------------------------- */
3149 /* default scan direction: scan playfield from top/left to bottom/right */
3150 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3152 /* dynamically adjust element properties according to game engine version */
3153 InitElementPropertiesEngine(game.engine_version);
3156 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3157 printf(" tape version == %06d [%s] [file: %06d]\n",
3158 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3160 printf(" => game.engine_version == %06d\n", game.engine_version);
3163 /* ---------- initialize player's initial move delay --------------------- */
3165 /* dynamically adjust player properties according to level information */
3166 for (i = 0; i < MAX_PLAYERS; i++)
3167 game.initial_move_delay_value[i] =
3168 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3170 /* dynamically adjust player properties according to game engine version */
3171 for (i = 0; i < MAX_PLAYERS; i++)
3172 game.initial_move_delay[i] =
3173 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3174 game.initial_move_delay_value[i] : 0);
3176 /* ---------- initialize player's initial push delay --------------------- */
3178 /* dynamically adjust player properties according to game engine version */
3179 game.initial_push_delay_value =
3180 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3182 /* ---------- initialize changing elements ------------------------------- */
3184 /* initialize changing elements information */
3185 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3187 struct ElementInfo *ei = &element_info[i];
3189 /* this pointer might have been changed in the level editor */
3190 ei->change = &ei->change_page[0];
3192 if (!IS_CUSTOM_ELEMENT(i))
3194 ei->change->target_element = EL_EMPTY_SPACE;
3195 ei->change->delay_fixed = 0;
3196 ei->change->delay_random = 0;
3197 ei->change->delay_frames = 1;
3200 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3202 ei->has_change_event[j] = FALSE;
3204 ei->event_page_nr[j] = 0;
3205 ei->event_page[j] = &ei->change_page[0];
3209 /* add changing elements from pre-defined list */
3210 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3212 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3213 struct ElementInfo *ei = &element_info[ch_delay->element];
3215 ei->change->target_element = ch_delay->target_element;
3216 ei->change->delay_fixed = ch_delay->change_delay;
3218 ei->change->pre_change_function = ch_delay->pre_change_function;
3219 ei->change->change_function = ch_delay->change_function;
3220 ei->change->post_change_function = ch_delay->post_change_function;
3222 ei->change->can_change = TRUE;
3223 ei->change->can_change_or_has_action = TRUE;
3225 ei->has_change_event[CE_DELAY] = TRUE;
3227 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3228 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3231 /* ---------- initialize internal run-time variables --------------------- */
3233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3235 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3237 for (j = 0; j < ei->num_change_pages; j++)
3239 ei->change_page[j].can_change_or_has_action =
3240 (ei->change_page[j].can_change |
3241 ei->change_page[j].has_action);
3245 /* add change events from custom element configuration */
3246 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3248 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3250 for (j = 0; j < ei->num_change_pages; j++)
3252 if (!ei->change_page[j].can_change_or_has_action)
3255 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3257 /* only add event page for the first page found with this event */
3258 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3260 ei->has_change_event[k] = TRUE;
3262 ei->event_page_nr[k] = j;
3263 ei->event_page[k] = &ei->change_page[j];
3270 /* ---------- initialize reference elements in change conditions --------- */
3272 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3274 int element = EL_CUSTOM_START + i;
3275 struct ElementInfo *ei = &element_info[element];
3277 for (j = 0; j < ei->num_change_pages; j++)
3279 int trigger_element = ei->change_page[j].initial_trigger_element;
3281 if (trigger_element >= EL_PREV_CE_8 &&
3282 trigger_element <= EL_NEXT_CE_8)
3283 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3285 ei->change_page[j].trigger_element = trigger_element;
3290 /* ---------- initialize run-time trigger player and element ------------- */
3292 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3294 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3296 for (j = 0; j < ei->num_change_pages; j++)
3298 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3299 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3300 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3301 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3302 ei->change_page[j].actual_trigger_ce_value = 0;
3303 ei->change_page[j].actual_trigger_ce_score = 0;
3307 /* ---------- initialize trigger events ---------------------------------- */
3309 /* initialize trigger events information */
3310 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3312 trigger_events[i][j] = FALSE;
3314 /* add trigger events from element change event properties */
3315 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317 struct ElementInfo *ei = &element_info[i];
3319 for (j = 0; j < ei->num_change_pages; j++)
3321 if (!ei->change_page[j].can_change_or_has_action)
3324 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3326 int trigger_element = ei->change_page[j].trigger_element;
3328 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3330 if (ei->change_page[j].has_event[k])
3332 if (IS_GROUP_ELEMENT(trigger_element))
3334 struct ElementGroupInfo *group =
3335 element_info[trigger_element].group;
3337 for (l = 0; l < group->num_elements_resolved; l++)
3338 trigger_events[group->element_resolved[l]][k] = TRUE;
3340 else if (trigger_element == EL_ANY_ELEMENT)
3341 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3342 trigger_events[l][k] = TRUE;
3344 trigger_events[trigger_element][k] = TRUE;
3351 /* ---------- initialize push delay -------------------------------------- */
3353 /* initialize push delay values to default */
3354 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356 if (!IS_CUSTOM_ELEMENT(i))
3358 /* set default push delay values (corrected since version 3.0.7-1) */
3359 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3361 element_info[i].push_delay_fixed = 2;
3362 element_info[i].push_delay_random = 8;
3366 element_info[i].push_delay_fixed = 8;
3367 element_info[i].push_delay_random = 8;
3372 /* set push delay value for certain elements from pre-defined list */
3373 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3375 int e = push_delay_list[i].element;
3377 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3378 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3381 /* set push delay value for Supaplex elements for newer engine versions */
3382 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3384 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386 if (IS_SP_ELEMENT(i))
3388 /* set SP push delay to just enough to push under a falling zonk */
3389 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3391 element_info[i].push_delay_fixed = delay;
3392 element_info[i].push_delay_random = 0;
3397 /* ---------- initialize move stepsize ----------------------------------- */
3399 /* initialize move stepsize values to default */
3400 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401 if (!IS_CUSTOM_ELEMENT(i))
3402 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3404 /* set move stepsize value for certain elements from pre-defined list */
3405 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3407 int e = move_stepsize_list[i].element;
3409 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3412 /* ---------- initialize collect score ----------------------------------- */
3414 /* initialize collect score values for custom elements from initial value */
3415 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416 if (IS_CUSTOM_ELEMENT(i))
3417 element_info[i].collect_score = element_info[i].collect_score_initial;
3419 /* ---------- initialize collect count ----------------------------------- */
3421 /* initialize collect count values for non-custom elements */
3422 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423 if (!IS_CUSTOM_ELEMENT(i))
3424 element_info[i].collect_count_initial = 0;
3426 /* add collect count values for all elements from pre-defined list */
3427 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3428 element_info[collect_count_list[i].element].collect_count_initial =
3429 collect_count_list[i].count;
3431 /* ---------- initialize access direction -------------------------------- */
3433 /* initialize access direction values to default (access from every side) */
3434 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435 if (!IS_CUSTOM_ELEMENT(i))
3436 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3438 /* set access direction value for certain elements from pre-defined list */
3439 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3440 element_info[access_direction_list[i].element].access_direction =
3441 access_direction_list[i].direction;
3443 /* ---------- initialize explosion content ------------------------------- */
3444 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3446 if (IS_CUSTOM_ELEMENT(i))
3449 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3451 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3453 element_info[i].content.e[x][y] =
3454 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3455 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3456 i == EL_PLAYER_3 ? EL_EMERALD :
3457 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3458 i == EL_MOLE ? EL_EMERALD_RED :
3459 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3460 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3461 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3462 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3463 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3464 i == EL_WALL_EMERALD ? EL_EMERALD :
3465 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3466 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3467 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3468 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3469 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3470 i == EL_WALL_PEARL ? EL_PEARL :
3471 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3476 /* ---------- initialize recursion detection ------------------------------ */
3477 recursion_loop_depth = 0;
3478 recursion_loop_detected = FALSE;
3479 recursion_loop_element = EL_UNDEFINED;
3481 /* ---------- initialize graphics engine ---------------------------------- */
3482 game.scroll_delay_value =
3483 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3484 setup.scroll_delay ? setup.scroll_delay_value : 0);
3485 game.scroll_delay_value =
3486 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3489 int get_num_special_action(int element, int action_first, int action_last)
3491 int num_special_action = 0;
3494 for (i = action_first; i <= action_last; i++)
3496 boolean found = FALSE;
3498 for (j = 0; j < NUM_DIRECTIONS; j++)
3499 if (el_act_dir2img(element, i, j) !=
3500 el_act_dir2img(element, ACTION_DEFAULT, j))
3504 num_special_action++;
3509 return num_special_action;
3514 =============================================================================
3516 -----------------------------------------------------------------------------
3517 initialize and start new game
3518 =============================================================================
3523 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3524 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3525 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3527 boolean do_fading = (game_status == GAME_MODE_MAIN);
3530 int initial_move_dir = MV_DOWN;
3532 int initial_move_dir = MV_NONE;
3540 if (!game.restart_level)
3541 CloseDoor(DOOR_CLOSE_1);
3544 if (level_editor_test_game)
3545 FadeSkipNextFadeIn();
3547 FadeSetEnterScreen();
3549 if (level_editor_test_game)
3550 fading = fading_none;
3552 fading = menu.destination;
3556 FadeOut(REDRAW_FIELD);
3559 FadeOut(REDRAW_FIELD);
3564 game_status = GAME_MODE_PLAYING;
3567 /* needed if different viewport properties defined for playing */
3568 ChangeViewportPropertiesIfNeeded();
3572 DrawCompleteVideoDisplay();
3576 InitGameControlValues();
3578 /* don't play tapes over network */
3579 network_playing = (options.network && !tape.playing);
3581 for (i = 0; i < MAX_PLAYERS; i++)
3583 struct PlayerInfo *player = &stored_player[i];
3585 player->index_nr = i;
3586 player->index_bit = (1 << i);
3587 player->element_nr = EL_PLAYER_1 + i;
3589 player->present = FALSE;
3590 player->active = FALSE;
3591 player->mapped = FALSE;
3593 player->killed = FALSE;
3594 player->reanimated = FALSE;
3597 player->effective_action = 0;
3598 player->programmed_action = 0;
3601 player->score_final = 0;
3603 player->gems_still_needed = level.gems_needed;
3604 player->sokobanfields_still_needed = 0;
3605 player->lights_still_needed = 0;
3606 player->friends_still_needed = 0;
3608 for (j = 0; j < MAX_NUM_KEYS; j++)
3609 player->key[j] = FALSE;
3611 player->num_white_keys = 0;
3613 player->dynabomb_count = 0;
3614 player->dynabomb_size = 1;
3615 player->dynabombs_left = 0;
3616 player->dynabomb_xl = FALSE;
3618 player->MovDir = initial_move_dir;
3621 player->GfxDir = initial_move_dir;
3622 player->GfxAction = ACTION_DEFAULT;
3624 player->StepFrame = 0;
3626 player->initial_element = player->element_nr;
3627 player->artwork_element =
3628 (level.use_artwork_element[i] ? level.artwork_element[i] :
3629 player->element_nr);
3630 player->use_murphy = FALSE;
3632 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3633 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3635 player->gravity = level.initial_player_gravity[i];
3637 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3639 player->actual_frame_counter = 0;
3641 player->step_counter = 0;
3643 player->last_move_dir = initial_move_dir;
3645 player->is_active = FALSE;
3647 player->is_waiting = FALSE;
3648 player->is_moving = FALSE;
3649 player->is_auto_moving = FALSE;
3650 player->is_digging = FALSE;
3651 player->is_snapping = FALSE;
3652 player->is_collecting = FALSE;
3653 player->is_pushing = FALSE;
3654 player->is_switching = FALSE;
3655 player->is_dropping = FALSE;
3656 player->is_dropping_pressed = FALSE;
3658 player->is_bored = FALSE;
3659 player->is_sleeping = FALSE;
3661 player->frame_counter_bored = -1;
3662 player->frame_counter_sleeping = -1;
3664 player->anim_delay_counter = 0;
3665 player->post_delay_counter = 0;
3667 player->dir_waiting = initial_move_dir;
3668 player->action_waiting = ACTION_DEFAULT;
3669 player->last_action_waiting = ACTION_DEFAULT;
3670 player->special_action_bored = ACTION_DEFAULT;
3671 player->special_action_sleeping = ACTION_DEFAULT;
3673 player->switch_x = -1;
3674 player->switch_y = -1;
3676 player->drop_x = -1;
3677 player->drop_y = -1;
3679 player->show_envelope = 0;
3681 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3683 player->push_delay = -1; /* initialized when pushing starts */
3684 player->push_delay_value = game.initial_push_delay_value;
3686 player->drop_delay = 0;
3687 player->drop_pressed_delay = 0;
3689 player->last_jx = -1;
3690 player->last_jy = -1;
3694 player->shield_normal_time_left = 0;
3695 player->shield_deadly_time_left = 0;
3697 player->inventory_infinite_element = EL_UNDEFINED;
3698 player->inventory_size = 0;
3700 if (level.use_initial_inventory[i])
3702 for (j = 0; j < level.initial_inventory_size[i]; j++)
3704 int element = level.initial_inventory_content[i][j];
3705 int collect_count = element_info[element].collect_count_initial;
3708 if (!IS_CUSTOM_ELEMENT(element))
3711 if (collect_count == 0)
3712 player->inventory_infinite_element = element;
3714 for (k = 0; k < collect_count; k++)
3715 if (player->inventory_size < MAX_INVENTORY_SIZE)
3716 player->inventory_element[player->inventory_size++] = element;
3720 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3721 SnapField(player, 0, 0);
3723 player->LevelSolved = FALSE;
3724 player->GameOver = FALSE;
3726 player->LevelSolved_GameWon = FALSE;
3727 player->LevelSolved_GameEnd = FALSE;
3728 player->LevelSolved_PanelOff = FALSE;
3729 player->LevelSolved_SaveTape = FALSE;
3730 player->LevelSolved_SaveScore = FALSE;
3731 player->LevelSolved_CountingTime = 0;
3732 player->LevelSolved_CountingScore = 0;
3734 map_player_action[i] = i;
3737 network_player_action_received = FALSE;
3739 #if defined(NETWORK_AVALIABLE)
3740 /* initial null action */
3741 if (network_playing)
3742 SendToServer_MovePlayer(MV_NONE);
3751 TimeLeft = level.time;
3754 ScreenMovDir = MV_NONE;
3758 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3760 AllPlayersGone = FALSE;
3762 game.no_time_limit = (level.time == 0);
3764 game.yamyam_content_nr = 0;
3765 game.robot_wheel_active = FALSE;
3766 game.magic_wall_active = FALSE;
3767 game.magic_wall_time_left = 0;
3768 game.light_time_left = 0;
3769 game.timegate_time_left = 0;
3770 game.switchgate_pos = 0;
3771 game.wind_direction = level.wind_direction_initial;
3773 #if !USE_PLAYER_GRAVITY
3774 game.gravity = FALSE;
3775 game.explosions_delayed = TRUE;
3778 game.lenses_time_left = 0;
3779 game.magnify_time_left = 0;
3781 game.ball_state = level.ball_state_initial;
3782 game.ball_content_nr = 0;
3784 game.envelope_active = FALSE;
3786 /* set focus to local player for network games, else to all players */
3787 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3788 game.centered_player_nr_next = game.centered_player_nr;
3789 game.set_centered_player = FALSE;
3791 if (network_playing && tape.recording)
3793 /* store client dependent player focus when recording network games */
3794 tape.centered_player_nr_next = game.centered_player_nr_next;
3795 tape.set_centered_player = TRUE;
3798 for (i = 0; i < NUM_BELTS; i++)
3800 game.belt_dir[i] = MV_NONE;
3801 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3804 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3805 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3807 #if DEBUG_INIT_PLAYER
3810 printf("Player status at level initialization:\n");
3814 SCAN_PLAYFIELD(x, y)
3816 Feld[x][y] = level.field[x][y];
3817 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3818 ChangeDelay[x][y] = 0;
3819 ChangePage[x][y] = -1;
3820 #if USE_NEW_CUSTOM_VALUE
3821 CustomValue[x][y] = 0; /* initialized in InitField() */
3823 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3825 WasJustMoving[x][y] = 0;
3826 WasJustFalling[x][y] = 0;
3827 CheckCollision[x][y] = 0;
3828 CheckImpact[x][y] = 0;
3830 Pushed[x][y] = FALSE;
3832 ChangeCount[x][y] = 0;
3833 ChangeEvent[x][y] = -1;
3835 ExplodePhase[x][y] = 0;
3836 ExplodeDelay[x][y] = 0;
3837 ExplodeField[x][y] = EX_TYPE_NONE;
3839 RunnerVisit[x][y] = 0;
3840 PlayerVisit[x][y] = 0;
3843 GfxRandom[x][y] = INIT_GFX_RANDOM();
3844 GfxElement[x][y] = EL_UNDEFINED;
3845 GfxAction[x][y] = ACTION_DEFAULT;
3846 GfxDir[x][y] = MV_NONE;
3847 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3850 SCAN_PLAYFIELD(x, y)
3852 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3854 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3856 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3859 InitField(x, y, TRUE);
3861 ResetGfxAnimation(x, y);
3866 for (i = 0; i < MAX_PLAYERS; i++)
3868 struct PlayerInfo *player = &stored_player[i];
3870 /* set number of special actions for bored and sleeping animation */
3871 player->num_special_action_bored =
3872 get_num_special_action(player->artwork_element,
3873 ACTION_BORING_1, ACTION_BORING_LAST);
3874 player->num_special_action_sleeping =
3875 get_num_special_action(player->artwork_element,
3876 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3879 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3880 emulate_sb ? EMU_SOKOBAN :
3881 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3883 #if USE_NEW_ALL_SLIPPERY
3884 /* initialize type of slippery elements */
3885 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3887 if (!IS_CUSTOM_ELEMENT(i))
3889 /* default: elements slip down either to the left or right randomly */
3890 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3892 /* SP style elements prefer to slip down on the left side */
3893 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3894 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3896 /* BD style elements prefer to slip down on the left side */
3897 if (game.emulation == EMU_BOULDERDASH)
3898 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3903 /* initialize explosion and ignition delay */
3904 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3906 if (!IS_CUSTOM_ELEMENT(i))
3909 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3910 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3911 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3912 int last_phase = (num_phase + 1) * delay;
3913 int half_phase = (num_phase / 2) * delay;
3915 element_info[i].explosion_delay = last_phase - 1;
3916 element_info[i].ignition_delay = half_phase;
3918 if (i == EL_BLACK_ORB)
3919 element_info[i].ignition_delay = 1;
3923 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3924 element_info[i].explosion_delay = 1;
3926 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3927 element_info[i].ignition_delay = 1;
3931 /* correct non-moving belts to start moving left */
3932 for (i = 0; i < NUM_BELTS; i++)
3933 if (game.belt_dir[i] == MV_NONE)
3934 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3936 #if USE_NEW_PLAYER_ASSIGNMENTS
3937 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3938 /* choose default local player */
3939 local_player = &stored_player[0];
3941 for (i = 0; i < MAX_PLAYERS; i++)
3942 stored_player[i].connected = FALSE;
3944 local_player->connected = TRUE;
3945 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3948 printf("::: TEAM MODE: %d\n", game.team_mode);
3954 for (i = 0; i < MAX_PLAYERS; i++)
3955 stored_player[i].connected = tape.player_participates[i];
3957 /* try to guess locally connected team mode players (needed for correct
3958 assignment of player figures from level to locally playing players) */
3960 for (i = 0; i < MAX_PLAYERS; i++)
3961 if (tape.player_participates[i])
3962 stored_player[i].connected = TRUE;
3965 else if (game.team_mode && !options.network)
3967 /* try to guess locally connected team mode players (needed for correct
3968 assignment of player figures from level to locally playing players) */
3970 for (i = 0; i < MAX_PLAYERS; i++)
3971 if (setup.input[i].use_joystick ||
3972 setup.input[i].key.left != KSYM_UNDEFINED)
3973 stored_player[i].connected = TRUE;
3976 #if DEBUG_INIT_PLAYER
3979 printf("Player status after level initialization:\n");
3981 for (i = 0; i < MAX_PLAYERS; i++)
3983 struct PlayerInfo *player = &stored_player[i];
3985 printf("- player %d: present == %d, connected == %d, active == %d",
3991 if (local_player == player)
3992 printf(" (local player)");
3999 #if DEBUG_INIT_PLAYER
4001 printf("Reassigning players ...\n");
4004 /* check if any connected player was not found in playfield */
4005 for (i = 0; i < MAX_PLAYERS; i++)
4007 struct PlayerInfo *player = &stored_player[i];
4009 if (player->connected && !player->present)
4011 struct PlayerInfo *field_player = NULL;
4013 #if DEBUG_INIT_PLAYER
4015 printf("- looking for field player for player %d ...\n", i + 1);
4018 /* assign first free player found that is present in the playfield */
4021 /* first try: look for unmapped playfield player that is not connected */
4022 for (j = 0; j < MAX_PLAYERS; j++)
4023 if (field_player == NULL &&
4024 stored_player[j].present &&
4025 !stored_player[j].mapped &&
4026 !stored_player[j].connected)
4027 field_player = &stored_player[j];
4029 /* second try: look for *any* unmapped playfield player */
4030 for (j = 0; j < MAX_PLAYERS; j++)
4031 if (field_player == NULL &&
4032 stored_player[j].present &&
4033 !stored_player[j].mapped)
4034 field_player = &stored_player[j];
4036 /* first try: look for unmapped playfield player that is not connected */
4037 if (field_player == NULL)
4038 for (j = 0; j < MAX_PLAYERS; j++)
4039 if (stored_player[j].present &&
4040 !stored_player[j].mapped &&
4041 !stored_player[j].connected)
4042 field_player = &stored_player[j];
4044 /* second try: look for *any* unmapped playfield player */
4045 if (field_player == NULL)
4046 for (j = 0; j < MAX_PLAYERS; j++)
4047 if (stored_player[j].present &&
4048 !stored_player[j].mapped)
4049 field_player = &stored_player[j];
4052 if (field_player != NULL)
4054 int jx = field_player->jx, jy = field_player->jy;
4056 #if DEBUG_INIT_PLAYER
4058 printf("- found player %d\n", field_player->index_nr + 1);
4061 player->present = FALSE;
4062 player->active = FALSE;
4064 field_player->present = TRUE;
4065 field_player->active = TRUE;
4068 player->initial_element = field_player->initial_element;
4069 player->artwork_element = field_player->artwork_element;
4071 player->block_last_field = field_player->block_last_field;
4072 player->block_delay_adjustment = field_player->block_delay_adjustment;
4075 StorePlayer[jx][jy] = field_player->element_nr;
4077 field_player->jx = field_player->last_jx = jx;
4078 field_player->jy = field_player->last_jy = jy;
4080 if (local_player == player)
4081 local_player = field_player;
4083 map_player_action[field_player->index_nr] = i;
4085 field_player->mapped = TRUE;
4087 #if DEBUG_INIT_PLAYER
4089 printf("- map_player_action[%d] == %d\n",
4090 field_player->index_nr + 1, i + 1);
4095 if (player->connected && player->present)
4096 player->mapped = TRUE;
4099 #if DEBUG_INIT_PLAYER
4102 printf("Player status after player assignment (first stage):\n");
4104 for (i = 0; i < MAX_PLAYERS; i++)
4106 struct PlayerInfo *player = &stored_player[i];
4108 printf("- player %d: present == %d, connected == %d, active == %d",
4114 if (local_player == player)
4115 printf(" (local player)");
4124 /* check if any connected player was not found in playfield */
4125 for (i = 0; i < MAX_PLAYERS; i++)
4127 struct PlayerInfo *player = &stored_player[i];
4129 if (player->connected && !player->present)
4131 for (j = 0; j < MAX_PLAYERS; j++)
4133 struct PlayerInfo *field_player = &stored_player[j];
4134 int jx = field_player->jx, jy = field_player->jy;
4136 /* assign first free player found that is present in the playfield */
4137 if (field_player->present && !field_player->connected)
4139 player->present = TRUE;
4140 player->active = TRUE;
4142 field_player->present = FALSE;
4143 field_player->active = FALSE;
4145 player->initial_element = field_player->initial_element;
4146 player->artwork_element = field_player->artwork_element;
4148 player->block_last_field = field_player->block_last_field;
4149 player->block_delay_adjustment = field_player->block_delay_adjustment;
4151 StorePlayer[jx][jy] = player->element_nr;
4153 player->jx = player->last_jx = jx;
4154 player->jy = player->last_jy = jy;
4164 printf("::: local_player->present == %d\n", local_player->present);
4169 /* when playing a tape, eliminate all players who do not participate */
4171 #if USE_NEW_PLAYER_ASSIGNMENTS
4174 if (!game.team_mode)
4177 for (i = 0; i < MAX_PLAYERS; i++)
4179 if (stored_player[i].active &&
4180 !tape.player_participates[map_player_action[i]])
4182 struct PlayerInfo *player = &stored_player[i];
4183 int jx = player->jx, jy = player->jy;
4185 #if DEBUG_INIT_PLAYER
4187 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4190 player->active = FALSE;
4191 StorePlayer[jx][jy] = 0;
4192 Feld[jx][jy] = EL_EMPTY;
4198 for (i = 0; i < MAX_PLAYERS; i++)
4200 if (stored_player[i].active &&
4201 !tape.player_participates[i])
4203 struct PlayerInfo *player = &stored_player[i];
4204 int jx = player->jx, jy = player->jy;
4206 player->active = FALSE;
4207 StorePlayer[jx][jy] = 0;
4208 Feld[jx][jy] = EL_EMPTY;
4213 else if (!options.network && !game.team_mode) /* && !tape.playing */
4215 /* when in single player mode, eliminate all but the first active player */
4217 for (i = 0; i < MAX_PLAYERS; i++)
4219 if (stored_player[i].active)
4221 for (j = i + 1; j < MAX_PLAYERS; j++)
4223 if (stored_player[j].active)
4225 struct PlayerInfo *player = &stored_player[j];
4226 int jx = player->jx, jy = player->jy;
4228 player->active = FALSE;
4229 player->present = FALSE;
4231 StorePlayer[jx][jy] = 0;
4232 Feld[jx][jy] = EL_EMPTY;
4239 /* when recording the game, store which players take part in the game */
4242 #if USE_NEW_PLAYER_ASSIGNMENTS
4243 for (i = 0; i < MAX_PLAYERS; i++)
4244 if (stored_player[i].connected)
4245 tape.player_participates[i] = TRUE;
4247 for (i = 0; i < MAX_PLAYERS; i++)
4248 if (stored_player[i].active)
4249 tape.player_participates[i] = TRUE;
4253 #if DEBUG_INIT_PLAYER
4256 printf("Player status after player assignment (final stage):\n");
4258 for (i = 0; i < MAX_PLAYERS; i++)
4260 struct PlayerInfo *player = &stored_player[i];
4262 printf("- player %d: present == %d, connected == %d, active == %d",
4268 if (local_player == player)
4269 printf(" (local player)");
4276 if (BorderElement == EL_EMPTY)
4279 SBX_Right = lev_fieldx - SCR_FIELDX;
4281 SBY_Lower = lev_fieldy - SCR_FIELDY;
4286 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4288 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4293 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4294 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4296 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4297 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4299 if (EVEN(SCR_FIELDX))
4301 if (EVEN(SCR_FIELDY))
4306 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4307 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4309 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4310 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4313 /* if local player not found, look for custom element that might create
4314 the player (make some assumptions about the right custom element) */
4315 if (!local_player->present)
4317 int start_x = 0, start_y = 0;
4318 int found_rating = 0;
4319 int found_element = EL_UNDEFINED;
4320 int player_nr = local_player->index_nr;
4322 SCAN_PLAYFIELD(x, y)
4324 int element = Feld[x][y];
4329 if (level.use_start_element[player_nr] &&
4330 level.start_element[player_nr] == element &&
4337 found_element = element;
4340 if (!IS_CUSTOM_ELEMENT(element))
4343 if (CAN_CHANGE(element))
4345 for (i = 0; i < element_info[element].num_change_pages; i++)
4347 /* check for player created from custom element as single target */
4348 content = element_info[element].change_page[i].target_element;
4349 is_player = ELEM_IS_PLAYER(content);
4351 if (is_player && (found_rating < 3 ||
4352 (found_rating == 3 && element < found_element)))
4358 found_element = element;
4363 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4365 /* check for player created from custom element as explosion content */
4366 content = element_info[element].content.e[xx][yy];
4367 is_player = ELEM_IS_PLAYER(content);
4369 if (is_player && (found_rating < 2 ||
4370 (found_rating == 2 && element < found_element)))
4372 start_x = x + xx - 1;
4373 start_y = y + yy - 1;
4376 found_element = element;
4379 if (!CAN_CHANGE(element))
4382 for (i = 0; i < element_info[element].num_change_pages; i++)
4384 /* check for player created from custom element as extended target */
4386 element_info[element].change_page[i].target_content.e[xx][yy];
4388 is_player = ELEM_IS_PLAYER(content);
4390 if (is_player && (found_rating < 1 ||
4391 (found_rating == 1 && element < found_element)))
4393 start_x = x + xx - 1;
4394 start_y = y + yy - 1;
4397 found_element = element;
4403 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4404 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4407 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4408 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4413 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4414 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4415 local_player->jx - MIDPOSX);
4417 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4418 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4419 local_player->jy - MIDPOSY);
4423 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4427 /* do not use PLAYING mask for fading out from main screen */
4428 game_status = GAME_MODE_MAIN;
4435 if (!game.restart_level)
4436 CloseDoor(DOOR_CLOSE_1);
4439 if (level_editor_test_game)
4440 FadeSkipNextFadeIn();
4442 FadeSetEnterScreen();
4444 if (level_editor_test_game)
4445 fading = fading_none;
4447 fading = menu.destination;
4451 FadeOut(REDRAW_FIELD);
4454 FadeOut(REDRAW_FIELD);
4460 game_status = GAME_MODE_PLAYING;
4463 /* !!! FIX THIS (START) !!! */
4464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4466 InitGameEngine_EM();
4468 /* blit playfield from scroll buffer to normal back buffer for fading in */
4469 BlitScreenToBitmap_EM(backbuffer);
4471 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4473 InitGameEngine_SP();
4475 /* blit playfield from scroll buffer to normal back buffer for fading in */
4476 BlitScreenToBitmap_SP(backbuffer);
4483 /* after drawing the level, correct some elements */
4484 if (game.timegate_time_left == 0)
4485 CloseAllOpenTimegates();
4488 BlitScreenToBitmap(backbuffer);
4490 /* blit playfield from scroll buffer to normal back buffer for fading in */
4491 if (setup.soft_scrolling)
4492 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4495 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4497 /* !!! FIX THIS (END) !!! */
4500 FadeIn(REDRAW_FIELD);
4503 FadeIn(REDRAW_FIELD);
4508 if (!game.restart_level)
4510 /* copy default game door content to main double buffer */
4513 /* !!! CHECK AGAIN !!! */
4514 SetPanelBackground();
4515 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4516 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4518 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4520 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4521 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4522 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4523 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4526 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4527 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4531 SetPanelBackground();
4532 SetDrawBackgroundMask(REDRAW_DOOR_1);
4535 UpdateAndDisplayGameControlValues();
4537 UpdateGameDoorValues();
4538 DrawGameDoorValues();
4541 if (!game.restart_level)
4545 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4546 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4547 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4551 /* copy actual game door content to door double buffer for OpenDoor() */
4553 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4555 BlitBitmap(drawto, bitmap_db_door,
4556 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4559 OpenDoor(DOOR_OPEN_ALL);
4561 PlaySound(SND_GAME_STARTING);
4563 if (setup.sound_music)
4566 KeyboardAutoRepeatOffUnlessAutoplay();
4568 #if DEBUG_INIT_PLAYER
4571 printf("Player status (final):\n");
4573 for (i = 0; i < MAX_PLAYERS; i++)
4575 struct PlayerInfo *player = &stored_player[i];
4577 printf("- player %d: present == %d, connected == %d, active == %d",
4583 if (local_player == player)
4584 printf(" (local player)");
4599 if (!game.restart_level && !tape.playing)
4601 LevelStats_incPlayed(level_nr);
4603 SaveLevelSetup_SeriesInfo();
4606 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4610 game.restart_level = FALSE;
4613 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4615 /* this is used for non-R'n'D game engines to update certain engine values */
4617 /* needed to determine if sounds are played within the visible screen area */
4618 scroll_x = actual_scroll_x;
4619 scroll_y = actual_scroll_y;
4622 void InitMovDir(int x, int y)
4624 int i, element = Feld[x][y];
4625 static int xy[4][2] =
4632 static int direction[3][4] =
4634 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4635 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4636 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4645 Feld[x][y] = EL_BUG;
4646 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4649 case EL_SPACESHIP_RIGHT:
4650 case EL_SPACESHIP_UP:
4651 case EL_SPACESHIP_LEFT:
4652 case EL_SPACESHIP_DOWN:
4653 Feld[x][y] = EL_SPACESHIP;
4654 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4657 case EL_BD_BUTTERFLY_RIGHT:
4658 case EL_BD_BUTTERFLY_UP:
4659 case EL_BD_BUTTERFLY_LEFT:
4660 case EL_BD_BUTTERFLY_DOWN:
4661 Feld[x][y] = EL_BD_BUTTERFLY;
4662 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4665 case EL_BD_FIREFLY_RIGHT:
4666 case EL_BD_FIREFLY_UP:
4667 case EL_BD_FIREFLY_LEFT:
4668 case EL_BD_FIREFLY_DOWN:
4669 Feld[x][y] = EL_BD_FIREFLY;
4670 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4673 case EL_PACMAN_RIGHT:
4675 case EL_PACMAN_LEFT:
4676 case EL_PACMAN_DOWN:
4677 Feld[x][y] = EL_PACMAN;
4678 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4681 case EL_YAMYAM_LEFT:
4682 case EL_YAMYAM_RIGHT:
4684 case EL_YAMYAM_DOWN:
4685 Feld[x][y] = EL_YAMYAM;
4686 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4689 case EL_SP_SNIKSNAK:
4690 MovDir[x][y] = MV_UP;
4693 case EL_SP_ELECTRON:
4694 MovDir[x][y] = MV_LEFT;
4701 Feld[x][y] = EL_MOLE;
4702 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4706 if (IS_CUSTOM_ELEMENT(element))
4708 struct ElementInfo *ei = &element_info[element];
4709 int move_direction_initial = ei->move_direction_initial;
4710 int move_pattern = ei->move_pattern;
4712 if (move_direction_initial == MV_START_PREVIOUS)
4714 if (MovDir[x][y] != MV_NONE)
4717 move_direction_initial = MV_START_AUTOMATIC;
4720 if (move_direction_initial == MV_START_RANDOM)
4721 MovDir[x][y] = 1 << RND(4);
4722 else if (move_direction_initial & MV_ANY_DIRECTION)
4723 MovDir[x][y] = move_direction_initial;
4724 else if (move_pattern == MV_ALL_DIRECTIONS ||
4725 move_pattern == MV_TURNING_LEFT ||
4726 move_pattern == MV_TURNING_RIGHT ||
4727 move_pattern == MV_TURNING_LEFT_RIGHT ||
4728 move_pattern == MV_TURNING_RIGHT_LEFT ||
4729 move_pattern == MV_TURNING_RANDOM)
4730 MovDir[x][y] = 1 << RND(4);
4731 else if (move_pattern == MV_HORIZONTAL)
4732 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4733 else if (move_pattern == MV_VERTICAL)
4734 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4735 else if (move_pattern & MV_ANY_DIRECTION)
4736 MovDir[x][y] = element_info[element].move_pattern;
4737 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4738 move_pattern == MV_ALONG_RIGHT_SIDE)
4740 /* use random direction as default start direction */
4741 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4742 MovDir[x][y] = 1 << RND(4);
4744 for (i = 0; i < NUM_DIRECTIONS; i++)
4746 int x1 = x + xy[i][0];
4747 int y1 = y + xy[i][1];
4749 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4751 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4752 MovDir[x][y] = direction[0][i];
4754 MovDir[x][y] = direction[1][i];
4763 MovDir[x][y] = 1 << RND(4);
4765 if (element != EL_BUG &&
4766 element != EL_SPACESHIP &&
4767 element != EL_BD_BUTTERFLY &&
4768 element != EL_BD_FIREFLY)
4771 for (i = 0; i < NUM_DIRECTIONS; i++)
4773 int x1 = x + xy[i][0];
4774 int y1 = y + xy[i][1];
4776 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4778 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4780 MovDir[x][y] = direction[0][i];
4783 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4784 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4786 MovDir[x][y] = direction[1][i];
4795 GfxDir[x][y] = MovDir[x][y];
4798 void InitAmoebaNr(int x, int y)
4801 int group_nr = AmoebeNachbarNr(x, y);
4805 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4807 if (AmoebaCnt[i] == 0)
4815 AmoebaNr[x][y] = group_nr;
4816 AmoebaCnt[group_nr]++;
4817 AmoebaCnt2[group_nr]++;
4820 static void PlayerWins(struct PlayerInfo *player)
4822 player->LevelSolved = TRUE;
4823 player->GameOver = TRUE;
4825 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4826 level.native_em_level->lev->score : player->score);
4828 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4830 player->LevelSolved_CountingScore = player->score_final;
4835 static int time, time_final;
4836 static int score, score_final;
4837 static int game_over_delay_1 = 0;
4838 static int game_over_delay_2 = 0;
4839 int game_over_delay_value_1 = 50;
4840 int game_over_delay_value_2 = 50;
4842 if (!local_player->LevelSolved_GameWon)
4846 /* do not start end game actions before the player stops moving (to exit) */
4847 if (local_player->MovPos)
4850 local_player->LevelSolved_GameWon = TRUE;
4851 local_player->LevelSolved_SaveTape = tape.recording;
4852 local_player->LevelSolved_SaveScore = !tape.playing;
4856 LevelStats_incSolved(level_nr);
4858 SaveLevelSetup_SeriesInfo();
4861 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4865 if (tape.auto_play) /* tape might already be stopped here */
4866 tape.auto_play_level_solved = TRUE;
4872 game_over_delay_1 = game_over_delay_value_1;
4873 game_over_delay_2 = game_over_delay_value_2;
4875 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4876 score = score_final = local_player->score_final;
4881 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4883 else if (game.no_time_limit && TimePlayed < 999)
4886 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4889 local_player->score_final = score_final;
4891 if (level_editor_test_game)
4894 score = score_final;
4897 local_player->LevelSolved_CountingTime = time;
4898 local_player->LevelSolved_CountingScore = score;
4900 game_panel_controls[GAME_PANEL_TIME].value = time;
4901 game_panel_controls[GAME_PANEL_SCORE].value = score;
4903 DisplayGameControlValues();
4905 DrawGameValue_Time(time);
4906 DrawGameValue_Score(score);
4910 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4912 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4914 /* close exit door after last player */
4915 if ((AllPlayersGone &&
4916 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4917 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4918 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4919 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4920 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4922 int element = Feld[ExitX][ExitY];
4925 if (element == EL_EM_EXIT_OPEN ||
4926 element == EL_EM_STEEL_EXIT_OPEN)
4933 Feld[ExitX][ExitY] =
4934 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4935 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4936 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4937 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4938 EL_EM_STEEL_EXIT_CLOSING);
4940 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4944 /* player disappears */
4945 DrawLevelField(ExitX, ExitY);
4948 for (i = 0; i < MAX_PLAYERS; i++)
4950 struct PlayerInfo *player = &stored_player[i];
4952 if (player->present)
4954 RemovePlayer(player);
4956 /* player disappears */
4957 DrawLevelField(player->jx, player->jy);
4962 PlaySound(SND_GAME_WINNING);
4965 if (game_over_delay_1 > 0)
4967 game_over_delay_1--;
4972 if (time != time_final)
4974 int time_to_go = ABS(time_final - time);
4975 int time_count_dir = (time < time_final ? +1 : -1);
4976 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4978 time += time_count_steps * time_count_dir;
4979 score += time_count_steps * level.score[SC_TIME_BONUS];
4982 local_player->LevelSolved_CountingTime = time;
4983 local_player->LevelSolved_CountingScore = score;
4985 game_panel_controls[GAME_PANEL_TIME].value = time;
4986 game_panel_controls[GAME_PANEL_SCORE].value = score;
4988 DisplayGameControlValues();
4990 DrawGameValue_Time(time);
4991 DrawGameValue_Score(score);
4994 if (time == time_final)
4995 StopSound(SND_GAME_LEVELTIME_BONUS);
4996 else if (setup.sound_loops)
4997 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4999 PlaySound(SND_GAME_LEVELTIME_BONUS);
5004 local_player->LevelSolved_PanelOff = TRUE;
5006 if (game_over_delay_2 > 0)
5008 game_over_delay_2--;
5021 boolean raise_level = FALSE;
5023 local_player->LevelSolved_GameEnd = TRUE;
5025 CloseDoor(DOOR_CLOSE_1);
5027 if (local_player->LevelSolved_SaveTape)
5034 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5036 SaveTape(tape.level_nr); /* ask to save tape */
5040 if (level_editor_test_game)
5042 game_status = GAME_MODE_MAIN;
5045 DrawAndFadeInMainMenu(REDRAW_FIELD);
5053 if (!local_player->LevelSolved_SaveScore)
5056 FadeOut(REDRAW_FIELD);
5059 game_status = GAME_MODE_MAIN;
5061 DrawAndFadeInMainMenu(REDRAW_FIELD);
5066 if (level_nr == leveldir_current->handicap_level)
5068 leveldir_current->handicap_level++;
5070 SaveLevelSetup_SeriesInfo();
5073 if (level_nr < leveldir_current->last_level)
5074 raise_level = TRUE; /* advance to next level */
5076 if ((hi_pos = NewHiScore()) >= 0)
5078 game_status = GAME_MODE_SCORES;
5080 DrawHallOfFame(hi_pos);
5091 FadeOut(REDRAW_FIELD);
5094 game_status = GAME_MODE_MAIN;
5102 DrawAndFadeInMainMenu(REDRAW_FIELD);
5111 LoadScore(level_nr);
5113 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5114 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5117 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5119 if (local_player->score_final > highscore[k].Score)
5121 /* player has made it to the hall of fame */
5123 if (k < MAX_SCORE_ENTRIES - 1)
5125 int m = MAX_SCORE_ENTRIES - 1;
5128 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5129 if (strEqual(setup.player_name, highscore[l].Name))
5131 if (m == k) /* player's new highscore overwrites his old one */
5135 for (l = m; l > k; l--)
5137 strcpy(highscore[l].Name, highscore[l - 1].Name);
5138 highscore[l].Score = highscore[l - 1].Score;
5145 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5146 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5147 highscore[k].Score = local_player->score_final;
5153 else if (!strncmp(setup.player_name, highscore[k].Name,
5154 MAX_PLAYER_NAME_LEN))
5155 break; /* player already there with a higher score */
5161 SaveScore(level_nr);
5166 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5168 int element = Feld[x][y];
5169 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5170 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5171 int horiz_move = (dx != 0);
5172 int sign = (horiz_move ? dx : dy);
5173 int step = sign * element_info[element].move_stepsize;
5175 /* special values for move stepsize for spring and things on conveyor belt */
5178 if (CAN_FALL(element) &&
5179 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5180 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5181 else if (element == EL_SPRING)
5182 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5188 inline static int getElementMoveStepsize(int x, int y)
5190 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5193 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5195 if (player->GfxAction != action || player->GfxDir != dir)
5198 printf("Player frame reset! (%d => %d, %d => %d)\n",
5199 player->GfxAction, action, player->GfxDir, dir);
5202 player->GfxAction = action;
5203 player->GfxDir = dir;
5205 player->StepFrame = 0;
5209 #if USE_GFX_RESET_GFX_ANIMATION
5210 static void ResetGfxFrame(int x, int y, boolean redraw)
5212 int element = Feld[x][y];
5213 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5214 int last_gfx_frame = GfxFrame[x][y];
5216 if (graphic_info[graphic].anim_global_sync)
5217 GfxFrame[x][y] = FrameCounter;
5218 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5219 GfxFrame[x][y] = CustomValue[x][y];
5220 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5221 GfxFrame[x][y] = element_info[element].collect_score;
5222 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5223 GfxFrame[x][y] = ChangeDelay[x][y];
5225 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5226 DrawLevelGraphicAnimation(x, y, graphic);
5230 static void ResetGfxAnimation(int x, int y)
5232 GfxAction[x][y] = ACTION_DEFAULT;
5233 GfxDir[x][y] = MovDir[x][y];
5236 #if USE_GFX_RESET_GFX_ANIMATION
5237 ResetGfxFrame(x, y, FALSE);
5241 static void ResetRandomAnimationValue(int x, int y)
5243 GfxRandom[x][y] = INIT_GFX_RANDOM();
5246 void InitMovingField(int x, int y, int direction)
5248 int element = Feld[x][y];
5249 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5250 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5253 boolean is_moving_before, is_moving_after;
5255 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5258 /* check if element was/is moving or being moved before/after mode change */
5261 is_moving_before = (WasJustMoving[x][y] != 0);
5263 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5264 is_moving_before = WasJustMoving[x][y];
5267 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5269 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5271 /* reset animation only for moving elements which change direction of moving
5272 or which just started or stopped moving
5273 (else CEs with property "can move" / "not moving" are reset each frame) */
5274 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5276 if (is_moving_before != is_moving_after ||
5277 direction != MovDir[x][y])
5278 ResetGfxAnimation(x, y);
5280 if ((is_moving_before || is_moving_after) && !continues_moving)
5281 ResetGfxAnimation(x, y);
5284 if (!continues_moving)
5285 ResetGfxAnimation(x, y);
5288 MovDir[x][y] = direction;
5289 GfxDir[x][y] = direction;
5291 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5292 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5293 direction == MV_DOWN && CAN_FALL(element) ?
5294 ACTION_FALLING : ACTION_MOVING);
5296 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5297 ACTION_FALLING : ACTION_MOVING);
5300 /* this is needed for CEs with property "can move" / "not moving" */
5302 if (is_moving_after)
5304 if (Feld[newx][newy] == EL_EMPTY)
5305 Feld[newx][newy] = EL_BLOCKED;
5307 MovDir[newx][newy] = MovDir[x][y];
5309 #if USE_NEW_CUSTOM_VALUE
5310 CustomValue[newx][newy] = CustomValue[x][y];
5313 GfxFrame[newx][newy] = GfxFrame[x][y];
5314 GfxRandom[newx][newy] = GfxRandom[x][y];
5315 GfxAction[newx][newy] = GfxAction[x][y];
5316 GfxDir[newx][newy] = GfxDir[x][y];
5320 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5322 int direction = MovDir[x][y];
5323 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5324 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5330 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5332 int oldx = x, oldy = y;
5333 int direction = MovDir[x][y];
5335 if (direction == MV_LEFT)
5337 else if (direction == MV_RIGHT)
5339 else if (direction == MV_UP)
5341 else if (direction == MV_DOWN)
5344 *comes_from_x = oldx;
5345 *comes_from_y = oldy;
5348 int MovingOrBlocked2Element(int x, int y)
5350 int element = Feld[x][y];
5352 if (element == EL_BLOCKED)
5356 Blocked2Moving(x, y, &oldx, &oldy);
5357 return Feld[oldx][oldy];
5363 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5365 /* like MovingOrBlocked2Element(), but if element is moving
5366 and (x,y) is the field the moving element is just leaving,
5367 return EL_BLOCKED instead of the element value */
5368 int element = Feld[x][y];
5370 if (IS_MOVING(x, y))
5372 if (element == EL_BLOCKED)
5376 Blocked2Moving(x, y, &oldx, &oldy);
5377 return Feld[oldx][oldy];
5386 static void RemoveField(int x, int y)
5388 Feld[x][y] = EL_EMPTY;
5394 #if USE_NEW_CUSTOM_VALUE
5395 CustomValue[x][y] = 0;
5399 ChangeDelay[x][y] = 0;
5400 ChangePage[x][y] = -1;
5401 Pushed[x][y] = FALSE;
5404 ExplodeField[x][y] = EX_TYPE_NONE;
5407 GfxElement[x][y] = EL_UNDEFINED;
5408 GfxAction[x][y] = ACTION_DEFAULT;
5409 GfxDir[x][y] = MV_NONE;
5411 /* !!! this would prevent the removed tile from being redrawn !!! */
5412 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5416 void RemoveMovingField(int x, int y)
5418 int oldx = x, oldy = y, newx = x, newy = y;
5419 int element = Feld[x][y];
5420 int next_element = EL_UNDEFINED;
5422 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5425 if (IS_MOVING(x, y))
5427 Moving2Blocked(x, y, &newx, &newy);
5429 if (Feld[newx][newy] != EL_BLOCKED)
5431 /* element is moving, but target field is not free (blocked), but
5432 already occupied by something different (example: acid pool);
5433 in this case, only remove the moving field, but not the target */
5435 RemoveField(oldx, oldy);
5437 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5439 TEST_DrawLevelField(oldx, oldy);
5444 else if (element == EL_BLOCKED)
5446 Blocked2Moving(x, y, &oldx, &oldy);
5447 if (!IS_MOVING(oldx, oldy))
5451 if (element == EL_BLOCKED &&
5452 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5453 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5454 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5455 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5456 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5457 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5458 next_element = get_next_element(Feld[oldx][oldy]);
5460 RemoveField(oldx, oldy);
5461 RemoveField(newx, newy);
5463 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5465 if (next_element != EL_UNDEFINED)
5466 Feld[oldx][oldy] = next_element;
5468 TEST_DrawLevelField(oldx, oldy);
5469 TEST_DrawLevelField(newx, newy);
5472 void DrawDynamite(int x, int y)
5474 int sx = SCREENX(x), sy = SCREENY(y);
5475 int graphic = el2img(Feld[x][y]);
5478 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5481 if (IS_WALKABLE_INSIDE(Back[x][y]))
5485 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5486 else if (Store[x][y])
5487 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5489 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5491 if (Back[x][y] || Store[x][y])
5492 DrawGraphicThruMask(sx, sy, graphic, frame);
5494 DrawGraphic(sx, sy, graphic, frame);
5497 void CheckDynamite(int x, int y)
5499 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5503 if (MovDelay[x][y] != 0)
5506 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5512 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5517 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5519 boolean num_checked_players = 0;
5522 for (i = 0; i < MAX_PLAYERS; i++)
5524 if (stored_player[i].active)
5526 int sx = stored_player[i].jx;
5527 int sy = stored_player[i].jy;
5529 if (num_checked_players == 0)
5536 *sx1 = MIN(*sx1, sx);
5537 *sy1 = MIN(*sy1, sy);
5538 *sx2 = MAX(*sx2, sx);
5539 *sy2 = MAX(*sy2, sy);
5542 num_checked_players++;
5547 static boolean checkIfAllPlayersFitToScreen_RND()
5549 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5551 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5553 return (sx2 - sx1 < SCR_FIELDX &&
5554 sy2 - sy1 < SCR_FIELDY);
5557 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5559 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5561 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5563 *sx = (sx1 + sx2) / 2;
5564 *sy = (sy1 + sy2) / 2;
5567 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5568 boolean center_screen, boolean quick_relocation)
5570 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5571 boolean no_delay = (tape.warp_forward);
5572 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5573 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5575 if (quick_relocation)
5577 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5579 if (!level.shifted_relocation || center_screen)
5581 /* quick relocation (without scrolling), with centering of screen */
5583 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5584 x > SBX_Right + MIDPOSX ? SBX_Right :
5587 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5588 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5593 /* quick relocation (without scrolling), but do not center screen */
5595 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5596 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5599 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5600 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5603 int offset_x = x + (scroll_x - center_scroll_x);
5604 int offset_y = y + (scroll_y - center_scroll_y);
5606 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5607 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5608 offset_x - MIDPOSX);
5610 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5611 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5612 offset_y - MIDPOSY);
5618 if (!level.shifted_relocation || center_screen)
5620 /* quick relocation (without scrolling), with centering of screen */
5622 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5623 x > SBX_Right + MIDPOSX ? SBX_Right :
5626 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5627 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5632 /* quick relocation (without scrolling), but do not center screen */
5634 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5635 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5638 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5639 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5642 int offset_x = x + (scroll_x - center_scroll_x);
5643 int offset_y = y + (scroll_y - center_scroll_y);
5645 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5646 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5647 offset_x - MIDPOSX);
5649 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5650 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5651 offset_y - MIDPOSY);
5654 /* quick relocation (without scrolling), inside visible screen area */
5656 int offset = game.scroll_delay_value;
5658 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5659 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5660 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5662 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5663 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5664 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5666 /* don't scroll over playfield boundaries */
5667 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5668 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5670 /* don't scroll over playfield boundaries */
5671 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5672 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5676 RedrawPlayfield(TRUE, 0,0,0,0);
5681 int scroll_xx, scroll_yy;
5683 if (!level.shifted_relocation || center_screen)
5685 /* visible relocation (with scrolling), with centering of screen */
5687 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5688 x > SBX_Right + MIDPOSX ? SBX_Right :
5691 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5692 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5697 /* visible relocation (with scrolling), but do not center screen */
5699 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5700 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5703 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5707 int offset_x = x + (scroll_x - center_scroll_x);
5708 int offset_y = y + (scroll_y - center_scroll_y);
5710 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5711 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5712 offset_x - MIDPOSX);
5714 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5715 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5716 offset_y - MIDPOSY);
5721 /* visible relocation (with scrolling), with centering of screen */
5723 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5724 x > SBX_Right + MIDPOSX ? SBX_Right :
5727 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5728 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5732 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5734 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5737 int fx = FX, fy = FY;
5739 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5740 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5742 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5748 fx += dx * TILEX / 2;
5749 fy += dy * TILEY / 2;
5751 ScrollLevel(dx, dy);
5754 /* scroll in two steps of half tile size to make things smoother */
5755 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5757 Delay(wait_delay_value);
5759 /* scroll second step to align at full tile size */
5761 Delay(wait_delay_value);
5766 Delay(wait_delay_value);
5770 void RelocatePlayer(int jx, int jy, int el_player_raw)
5772 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5773 int player_nr = GET_PLAYER_NR(el_player);
5774 struct PlayerInfo *player = &stored_player[player_nr];
5775 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5776 boolean no_delay = (tape.warp_forward);
5777 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5778 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5779 int old_jx = player->jx;
5780 int old_jy = player->jy;
5781 int old_element = Feld[old_jx][old_jy];
5782 int element = Feld[jx][jy];
5783 boolean player_relocated = (old_jx != jx || old_jy != jy);
5785 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5786 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5787 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5788 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5789 int leave_side_horiz = move_dir_horiz;
5790 int leave_side_vert = move_dir_vert;
5791 int enter_side = enter_side_horiz | enter_side_vert;
5792 int leave_side = leave_side_horiz | leave_side_vert;
5794 if (player->GameOver) /* do not reanimate dead player */
5797 if (!player_relocated) /* no need to relocate the player */
5800 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5802 RemoveField(jx, jy); /* temporarily remove newly placed player */
5803 DrawLevelField(jx, jy);
5806 if (player->present)
5808 while (player->MovPos)
5810 ScrollPlayer(player, SCROLL_GO_ON);
5811 ScrollScreen(NULL, SCROLL_GO_ON);
5813 AdvanceFrameAndPlayerCounters(player->index_nr);
5818 Delay(wait_delay_value);
5821 DrawPlayer(player); /* needed here only to cleanup last field */
5822 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5824 player->is_moving = FALSE;
5827 if (IS_CUSTOM_ELEMENT(old_element))
5828 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5830 player->index_bit, leave_side);
5832 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5834 player->index_bit, leave_side);
5836 Feld[jx][jy] = el_player;
5837 InitPlayerField(jx, jy, el_player, TRUE);
5839 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5840 possible that the relocation target field did not contain a player element,
5841 but a walkable element, to which the new player was relocated -- in this
5842 case, restore that (already initialized!) element on the player field */
5843 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5845 Feld[jx][jy] = element; /* restore previously existing element */
5847 /* !!! do not initialize already initialized element a second time !!! */
5848 /* (this causes at least problems with "element creation" CE trigger for
5849 already existing elements, and existing Sokoban fields counted twice) */
5850 InitField(jx, jy, FALSE);
5854 /* only visually relocate centered player */
5855 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5856 FALSE, level.instant_relocation);
5858 TestIfPlayerTouchesBadThing(jx, jy);
5859 TestIfPlayerTouchesCustomElement(jx, jy);
5861 if (IS_CUSTOM_ELEMENT(element))
5862 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5863 player->index_bit, enter_side);
5865 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5866 player->index_bit, enter_side);
5869 if (player->is_switching)
5871 /* ensure that relocation while still switching an element does not cause
5872 a new element to be treated as also switched directly after relocation
5873 (this is important for teleporter switches that teleport the player to
5874 a place where another teleporter switch is in the same direction, which
5875 would then incorrectly be treated as immediately switched before the
5876 direction key that caused the switch was released) */
5878 player->switch_x += jx - old_jx;
5879 player->switch_y += jy - old_jy;
5884 void Explode(int ex, int ey, int phase, int mode)
5890 /* !!! eliminate this variable !!! */
5891 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5893 if (game.explosions_delayed)
5895 ExplodeField[ex][ey] = mode;
5899 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5901 int center_element = Feld[ex][ey];
5902 int artwork_element, explosion_element; /* set these values later */
5905 /* --- This is only really needed (and now handled) in "Impact()". --- */
5906 /* do not explode moving elements that left the explode field in time */
5907 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5908 center_element == EL_EMPTY &&
5909 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5914 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5915 if (mode == EX_TYPE_NORMAL ||
5916 mode == EX_TYPE_CENTER ||
5917 mode == EX_TYPE_CROSS)
5918 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5921 /* remove things displayed in background while burning dynamite */
5922 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5925 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5927 /* put moving element to center field (and let it explode there) */
5928 center_element = MovingOrBlocked2Element(ex, ey);
5929 RemoveMovingField(ex, ey);
5930 Feld[ex][ey] = center_element;
5933 /* now "center_element" is finally determined -- set related values now */
5934 artwork_element = center_element; /* for custom player artwork */
5935 explosion_element = center_element; /* for custom player artwork */
5937 if (IS_PLAYER(ex, ey))
5939 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5941 artwork_element = stored_player[player_nr].artwork_element;
5943 if (level.use_explosion_element[player_nr])
5945 explosion_element = level.explosion_element[player_nr];
5946 artwork_element = explosion_element;
5951 if (mode == EX_TYPE_NORMAL ||
5952 mode == EX_TYPE_CENTER ||
5953 mode == EX_TYPE_CROSS)
5954 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5957 last_phase = element_info[explosion_element].explosion_delay + 1;
5959 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5961 int xx = x - ex + 1;
5962 int yy = y - ey + 1;
5965 if (!IN_LEV_FIELD(x, y) ||
5966 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5967 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5970 element = Feld[x][y];
5972 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5974 element = MovingOrBlocked2Element(x, y);
5976 if (!IS_EXPLOSION_PROOF(element))
5977 RemoveMovingField(x, y);
5980 /* indestructible elements can only explode in center (but not flames) */
5981 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5982 mode == EX_TYPE_BORDER)) ||
5983 element == EL_FLAMES)
5986 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5987 behaviour, for example when touching a yamyam that explodes to rocks
5988 with active deadly shield, a rock is created under the player !!! */
5989 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5991 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5992 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5993 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5995 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5998 if (IS_ACTIVE_BOMB(element))
6000 /* re-activate things under the bomb like gate or penguin */
6001 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6008 /* save walkable background elements while explosion on same tile */
6009 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6010 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6011 Back[x][y] = element;
6013 /* ignite explodable elements reached by other explosion */
6014 if (element == EL_EXPLOSION)
6015 element = Store2[x][y];
6017 if (AmoebaNr[x][y] &&
6018 (element == EL_AMOEBA_FULL ||
6019 element == EL_BD_AMOEBA ||
6020 element == EL_AMOEBA_GROWING))
6022 AmoebaCnt[AmoebaNr[x][y]]--;
6023 AmoebaCnt2[AmoebaNr[x][y]]--;
6028 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6030 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6032 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6034 if (PLAYERINFO(ex, ey)->use_murphy)
6035 Store[x][y] = EL_EMPTY;
6038 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6039 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6040 else if (ELEM_IS_PLAYER(center_element))
6041 Store[x][y] = EL_EMPTY;
6042 else if (center_element == EL_YAMYAM)
6043 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6044 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6045 Store[x][y] = element_info[center_element].content.e[xx][yy];
6047 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6048 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6049 otherwise) -- FIX THIS !!! */
6050 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6051 Store[x][y] = element_info[element].content.e[1][1];
6053 else if (!CAN_EXPLODE(element))
6054 Store[x][y] = element_info[element].content.e[1][1];
6057 Store[x][y] = EL_EMPTY;
6059 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6060 center_element == EL_AMOEBA_TO_DIAMOND)
6061 Store2[x][y] = element;
6063 Feld[x][y] = EL_EXPLOSION;
6064 GfxElement[x][y] = artwork_element;
6066 ExplodePhase[x][y] = 1;
6067 ExplodeDelay[x][y] = last_phase;
6072 if (center_element == EL_YAMYAM)
6073 game.yamyam_content_nr =
6074 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6086 GfxFrame[x][y] = 0; /* restart explosion animation */
6088 last_phase = ExplodeDelay[x][y];
6090 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6094 /* activate this even in non-DEBUG version until cause for crash in
6095 getGraphicAnimationFrame() (see below) is found and eliminated */
6101 /* this can happen if the player leaves an explosion just in time */
6102 if (GfxElement[x][y] == EL_UNDEFINED)
6103 GfxElement[x][y] = EL_EMPTY;
6105 if (GfxElement[x][y] == EL_UNDEFINED)
6108 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6109 printf("Explode(): This should never happen!\n");
6112 GfxElement[x][y] = EL_EMPTY;
6118 border_element = Store2[x][y];
6119 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6120 border_element = StorePlayer[x][y];
6122 if (phase == element_info[border_element].ignition_delay ||
6123 phase == last_phase)
6125 boolean border_explosion = FALSE;
6127 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6128 !PLAYER_EXPLOSION_PROTECTED(x, y))
6130 KillPlayerUnlessExplosionProtected(x, y);
6131 border_explosion = TRUE;
6133 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6135 Feld[x][y] = Store2[x][y];
6138 border_explosion = TRUE;
6140 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6142 AmoebeUmwandeln(x, y);
6144 border_explosion = TRUE;
6147 /* if an element just explodes due to another explosion (chain-reaction),
6148 do not immediately end the new explosion when it was the last frame of
6149 the explosion (as it would be done in the following "if"-statement!) */
6150 if (border_explosion && phase == last_phase)
6154 if (phase == last_phase)
6158 element = Feld[x][y] = Store[x][y];
6159 Store[x][y] = Store2[x][y] = 0;
6160 GfxElement[x][y] = EL_UNDEFINED;
6162 /* player can escape from explosions and might therefore be still alive */
6163 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6164 element <= EL_PLAYER_IS_EXPLODING_4)
6166 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6167 int explosion_element = EL_PLAYER_1 + player_nr;
6168 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6169 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6171 if (level.use_explosion_element[player_nr])
6172 explosion_element = level.explosion_element[player_nr];
6174 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6175 element_info[explosion_element].content.e[xx][yy]);
6178 /* restore probably existing indestructible background element */
6179 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6180 element = Feld[x][y] = Back[x][y];
6183 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6184 GfxDir[x][y] = MV_NONE;
6185 ChangeDelay[x][y] = 0;
6186 ChangePage[x][y] = -1;
6188 #if USE_NEW_CUSTOM_VALUE
6189 CustomValue[x][y] = 0;
6192 InitField_WithBug2(x, y, FALSE);
6194 TEST_DrawLevelField(x, y);
6196 TestIfElementTouchesCustomElement(x, y);
6198 if (GFX_CRUMBLED(element))
6199 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6201 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6202 StorePlayer[x][y] = 0;
6204 if (ELEM_IS_PLAYER(element))
6205 RelocatePlayer(x, y, element);
6207 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6209 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6210 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6213 TEST_DrawLevelFieldCrumbled(x, y);
6215 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6217 DrawLevelElement(x, y, Back[x][y]);
6218 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6220 else if (IS_WALKABLE_UNDER(Back[x][y]))
6222 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6223 DrawLevelElementThruMask(x, y, Back[x][y]);
6225 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6226 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6230 void DynaExplode(int ex, int ey)
6233 int dynabomb_element = Feld[ex][ey];
6234 int dynabomb_size = 1;
6235 boolean dynabomb_xl = FALSE;
6236 struct PlayerInfo *player;
6237 static int xy[4][2] =
6245 if (IS_ACTIVE_BOMB(dynabomb_element))
6247 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6248 dynabomb_size = player->dynabomb_size;
6249 dynabomb_xl = player->dynabomb_xl;
6250 player->dynabombs_left++;
6253 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6255 for (i = 0; i < NUM_DIRECTIONS; i++)
6257 for (j = 1; j <= dynabomb_size; j++)
6259 int x = ex + j * xy[i][0];
6260 int y = ey + j * xy[i][1];
6263 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6266 element = Feld[x][y];
6268 /* do not restart explosions of fields with active bombs */
6269 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6272 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6274 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6275 !IS_DIGGABLE(element) && !dynabomb_xl)
6281 void Bang(int x, int y)
6283 int element = MovingOrBlocked2Element(x, y);
6284 int explosion_type = EX_TYPE_NORMAL;
6286 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6288 struct PlayerInfo *player = PLAYERINFO(x, y);
6290 #if USE_FIX_CE_ACTION_WITH_PLAYER
6291 element = Feld[x][y] = player->initial_element;
6293 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6294 player->element_nr);
6297 if (level.use_explosion_element[player->index_nr])
6299 int explosion_element = level.explosion_element[player->index_nr];
6301 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6302 explosion_type = EX_TYPE_CROSS;
6303 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6304 explosion_type = EX_TYPE_CENTER;
6312 case EL_BD_BUTTERFLY:
6315 case EL_DARK_YAMYAM:
6319 RaiseScoreElement(element);
6322 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6323 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6324 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6325 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6326 case EL_DYNABOMB_INCREASE_NUMBER:
6327 case EL_DYNABOMB_INCREASE_SIZE:
6328 case EL_DYNABOMB_INCREASE_POWER:
6329 explosion_type = EX_TYPE_DYNA;
6332 case EL_DC_LANDMINE:
6334 case EL_EM_EXIT_OPEN:
6335 case EL_EM_STEEL_EXIT_OPEN:
6337 explosion_type = EX_TYPE_CENTER;
6342 case EL_LAMP_ACTIVE:
6343 case EL_AMOEBA_TO_DIAMOND:
6344 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6345 explosion_type = EX_TYPE_CENTER;
6349 if (element_info[element].explosion_type == EXPLODES_CROSS)
6350 explosion_type = EX_TYPE_CROSS;
6351 else if (element_info[element].explosion_type == EXPLODES_1X1)
6352 explosion_type = EX_TYPE_CENTER;
6356 if (explosion_type == EX_TYPE_DYNA)
6359 Explode(x, y, EX_PHASE_START, explosion_type);
6361 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6364 void SplashAcid(int x, int y)
6366 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6367 (!IN_LEV_FIELD(x - 1, y - 2) ||
6368 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6369 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6371 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6372 (!IN_LEV_FIELD(x + 1, y - 2) ||
6373 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6374 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6376 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6379 static void InitBeltMovement()
6381 static int belt_base_element[4] =
6383 EL_CONVEYOR_BELT_1_LEFT,
6384 EL_CONVEYOR_BELT_2_LEFT,
6385 EL_CONVEYOR_BELT_3_LEFT,
6386 EL_CONVEYOR_BELT_4_LEFT
6388 static int belt_base_active_element[4] =
6390 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6391 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6392 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6393 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6398 /* set frame order for belt animation graphic according to belt direction */
6399 for (i = 0; i < NUM_BELTS; i++)
6403 for (j = 0; j < NUM_BELT_PARTS; j++)
6405 int element = belt_base_active_element[belt_nr] + j;
6406 int graphic_1 = el2img(element);
6407 int graphic_2 = el2panelimg(element);
6409 if (game.belt_dir[i] == MV_LEFT)
6411 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6412 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6416 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6417 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6422 SCAN_PLAYFIELD(x, y)
6424 int element = Feld[x][y];
6426 for (i = 0; i < NUM_BELTS; i++)
6428 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6430 int e_belt_nr = getBeltNrFromBeltElement(element);
6433 if (e_belt_nr == belt_nr)
6435 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6437 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6444 static void ToggleBeltSwitch(int x, int y)
6446 static int belt_base_element[4] =
6448 EL_CONVEYOR_BELT_1_LEFT,
6449 EL_CONVEYOR_BELT_2_LEFT,
6450 EL_CONVEYOR_BELT_3_LEFT,
6451 EL_CONVEYOR_BELT_4_LEFT
6453 static int belt_base_active_element[4] =
6455 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6456 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6457 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6458 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6460 static int belt_base_switch_element[4] =
6462 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6463 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6464 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6465 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6467 static int belt_move_dir[4] =
6475 int element = Feld[x][y];
6476 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6477 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6478 int belt_dir = belt_move_dir[belt_dir_nr];
6481 if (!IS_BELT_SWITCH(element))
6484 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6485 game.belt_dir[belt_nr] = belt_dir;
6487 if (belt_dir_nr == 3)
6490 /* set frame order for belt animation graphic according to belt direction */
6491 for (i = 0; i < NUM_BELT_PARTS; i++)
6493 int element = belt_base_active_element[belt_nr] + i;
6494 int graphic_1 = el2img(element);
6495 int graphic_2 = el2panelimg(element);
6497 if (belt_dir == MV_LEFT)
6499 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6500 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6504 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6505 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6509 SCAN_PLAYFIELD(xx, yy)
6511 int element = Feld[xx][yy];
6513 if (IS_BELT_SWITCH(element))
6515 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6517 if (e_belt_nr == belt_nr)
6519 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6520 TEST_DrawLevelField(xx, yy);
6523 else if (IS_BELT(element) && belt_dir != MV_NONE)
6525 int e_belt_nr = getBeltNrFromBeltElement(element);
6527 if (e_belt_nr == belt_nr)
6529 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6531 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6532 TEST_DrawLevelField(xx, yy);
6535 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6537 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6539 if (e_belt_nr == belt_nr)
6541 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6543 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6544 TEST_DrawLevelField(xx, yy);
6550 static void ToggleSwitchgateSwitch(int x, int y)
6554 game.switchgate_pos = !game.switchgate_pos;
6556 SCAN_PLAYFIELD(xx, yy)
6558 int element = Feld[xx][yy];
6560 #if !USE_BOTH_SWITCHGATE_SWITCHES
6561 if (element == EL_SWITCHGATE_SWITCH_UP ||
6562 element == EL_SWITCHGATE_SWITCH_DOWN)
6564 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6565 TEST_DrawLevelField(xx, yy);
6567 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6568 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6570 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6571 TEST_DrawLevelField(xx, yy);
6574 if (element == EL_SWITCHGATE_SWITCH_UP)
6576 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6577 TEST_DrawLevelField(xx, yy);
6579 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6581 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6582 TEST_DrawLevelField(xx, yy);
6584 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6586 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6587 TEST_DrawLevelField(xx, yy);
6589 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6591 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6592 TEST_DrawLevelField(xx, yy);
6595 else if (element == EL_SWITCHGATE_OPEN ||
6596 element == EL_SWITCHGATE_OPENING)
6598 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6600 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6602 else if (element == EL_SWITCHGATE_CLOSED ||
6603 element == EL_SWITCHGATE_CLOSING)
6605 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6607 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6612 static int getInvisibleActiveFromInvisibleElement(int element)
6614 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6615 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6616 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6620 static int getInvisibleFromInvisibleActiveElement(int element)
6622 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6623 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6624 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6628 static void RedrawAllLightSwitchesAndInvisibleElements()
6632 SCAN_PLAYFIELD(x, y)
6634 int element = Feld[x][y];
6636 if (element == EL_LIGHT_SWITCH &&
6637 game.light_time_left > 0)
6639 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6640 TEST_DrawLevelField(x, y);
6642 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6643 game.light_time_left == 0)
6645 Feld[x][y] = EL_LIGHT_SWITCH;
6646 TEST_DrawLevelField(x, y);
6648 else if (element == EL_EMC_DRIPPER &&
6649 game.light_time_left > 0)
6651 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6652 TEST_DrawLevelField(x, y);
6654 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6655 game.light_time_left == 0)
6657 Feld[x][y] = EL_EMC_DRIPPER;
6658 TEST_DrawLevelField(x, y);
6660 else if (element == EL_INVISIBLE_STEELWALL ||
6661 element == EL_INVISIBLE_WALL ||
6662 element == EL_INVISIBLE_SAND)
6664 if (game.light_time_left > 0)
6665 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6667 TEST_DrawLevelField(x, y);
6669 /* uncrumble neighbour fields, if needed */
6670 if (element == EL_INVISIBLE_SAND)
6671 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6673 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6674 element == EL_INVISIBLE_WALL_ACTIVE ||
6675 element == EL_INVISIBLE_SAND_ACTIVE)
6677 if (game.light_time_left == 0)
6678 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6680 TEST_DrawLevelField(x, y);
6682 /* re-crumble neighbour fields, if needed */
6683 if (element == EL_INVISIBLE_SAND)
6684 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6689 static void RedrawAllInvisibleElementsForLenses()
6693 SCAN_PLAYFIELD(x, y)
6695 int element = Feld[x][y];
6697 if (element == EL_EMC_DRIPPER &&
6698 game.lenses_time_left > 0)
6700 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6701 TEST_DrawLevelField(x, y);
6703 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6704 game.lenses_time_left == 0)
6706 Feld[x][y] = EL_EMC_DRIPPER;
6707 TEST_DrawLevelField(x, y);
6709 else if (element == EL_INVISIBLE_STEELWALL ||
6710 element == EL_INVISIBLE_WALL ||
6711 element == EL_INVISIBLE_SAND)
6713 if (game.lenses_time_left > 0)
6714 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6716 TEST_DrawLevelField(x, y);
6718 /* uncrumble neighbour fields, if needed */
6719 if (element == EL_INVISIBLE_SAND)
6720 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6722 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6723 element == EL_INVISIBLE_WALL_ACTIVE ||
6724 element == EL_INVISIBLE_SAND_ACTIVE)
6726 if (game.lenses_time_left == 0)
6727 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6729 TEST_DrawLevelField(x, y);
6731 /* re-crumble neighbour fields, if needed */
6732 if (element == EL_INVISIBLE_SAND)
6733 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6738 static void RedrawAllInvisibleElementsForMagnifier()
6742 SCAN_PLAYFIELD(x, y)
6744 int element = Feld[x][y];
6746 if (element == EL_EMC_FAKE_GRASS &&
6747 game.magnify_time_left > 0)
6749 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6750 TEST_DrawLevelField(x, y);
6752 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6753 game.magnify_time_left == 0)
6755 Feld[x][y] = EL_EMC_FAKE_GRASS;
6756 TEST_DrawLevelField(x, y);
6758 else if (IS_GATE_GRAY(element) &&
6759 game.magnify_time_left > 0)
6761 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6762 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6763 IS_EM_GATE_GRAY(element) ?
6764 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6765 IS_EMC_GATE_GRAY(element) ?
6766 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6767 IS_DC_GATE_GRAY(element) ?
6768 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6770 TEST_DrawLevelField(x, y);
6772 else if (IS_GATE_GRAY_ACTIVE(element) &&
6773 game.magnify_time_left == 0)
6775 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6776 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6777 IS_EM_GATE_GRAY_ACTIVE(element) ?
6778 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6779 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6780 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6781 IS_DC_GATE_GRAY_ACTIVE(element) ?
6782 EL_DC_GATE_WHITE_GRAY :
6784 TEST_DrawLevelField(x, y);
6789 static void ToggleLightSwitch(int x, int y)
6791 int element = Feld[x][y];
6793 game.light_time_left =
6794 (element == EL_LIGHT_SWITCH ?
6795 level.time_light * FRAMES_PER_SECOND : 0);
6797 RedrawAllLightSwitchesAndInvisibleElements();
6800 static void ActivateTimegateSwitch(int x, int y)
6804 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6806 SCAN_PLAYFIELD(xx, yy)
6808 int element = Feld[xx][yy];
6810 if (element == EL_TIMEGATE_CLOSED ||
6811 element == EL_TIMEGATE_CLOSING)
6813 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6814 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6818 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6820 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6821 TEST_DrawLevelField(xx, yy);
6828 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6829 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6831 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6835 void Impact(int x, int y)
6837 boolean last_line = (y == lev_fieldy - 1);
6838 boolean object_hit = FALSE;
6839 boolean impact = (last_line || object_hit);
6840 int element = Feld[x][y];
6841 int smashed = EL_STEELWALL;
6843 if (!last_line) /* check if element below was hit */
6845 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6848 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6849 MovDir[x][y + 1] != MV_DOWN ||
6850 MovPos[x][y + 1] <= TILEY / 2));
6852 /* do not smash moving elements that left the smashed field in time */
6853 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6854 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6857 #if USE_QUICKSAND_IMPACT_BUGFIX
6858 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6860 RemoveMovingField(x, y + 1);
6861 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6862 Feld[x][y + 2] = EL_ROCK;
6863 TEST_DrawLevelField(x, y + 2);
6868 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6870 RemoveMovingField(x, y + 1);
6871 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6872 Feld[x][y + 2] = EL_ROCK;
6873 TEST_DrawLevelField(x, y + 2);
6880 smashed = MovingOrBlocked2Element(x, y + 1);
6882 impact = (last_line || object_hit);
6885 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6887 SplashAcid(x, y + 1);
6891 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6892 /* only reset graphic animation if graphic really changes after impact */
6894 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6896 ResetGfxAnimation(x, y);
6897 TEST_DrawLevelField(x, y);
6900 if (impact && CAN_EXPLODE_IMPACT(element))
6905 else if (impact && element == EL_PEARL &&
6906 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6908 ResetGfxAnimation(x, y);
6910 Feld[x][y] = EL_PEARL_BREAKING;
6911 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6914 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6916 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6921 if (impact && element == EL_AMOEBA_DROP)
6923 if (object_hit && IS_PLAYER(x, y + 1))
6924 KillPlayerUnlessEnemyProtected(x, y + 1);
6925 else if (object_hit && smashed == EL_PENGUIN)
6929 Feld[x][y] = EL_AMOEBA_GROWING;
6930 Store[x][y] = EL_AMOEBA_WET;
6932 ResetRandomAnimationValue(x, y);
6937 if (object_hit) /* check which object was hit */
6939 if ((CAN_PASS_MAGIC_WALL(element) &&
6940 (smashed == EL_MAGIC_WALL ||
6941 smashed == EL_BD_MAGIC_WALL)) ||
6942 (CAN_PASS_DC_MAGIC_WALL(element) &&
6943 smashed == EL_DC_MAGIC_WALL))
6946 int activated_magic_wall =
6947 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6948 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6949 EL_DC_MAGIC_WALL_ACTIVE);
6951 /* activate magic wall / mill */
6952 SCAN_PLAYFIELD(xx, yy)
6954 if (Feld[xx][yy] == smashed)
6955 Feld[xx][yy] = activated_magic_wall;
6958 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6959 game.magic_wall_active = TRUE;
6961 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6962 SND_MAGIC_WALL_ACTIVATING :
6963 smashed == EL_BD_MAGIC_WALL ?
6964 SND_BD_MAGIC_WALL_ACTIVATING :
6965 SND_DC_MAGIC_WALL_ACTIVATING));
6968 if (IS_PLAYER(x, y + 1))
6970 if (CAN_SMASH_PLAYER(element))
6972 KillPlayerUnlessEnemyProtected(x, y + 1);
6976 else if (smashed == EL_PENGUIN)
6978 if (CAN_SMASH_PLAYER(element))
6984 else if (element == EL_BD_DIAMOND)
6986 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6992 else if (((element == EL_SP_INFOTRON ||
6993 element == EL_SP_ZONK) &&
6994 (smashed == EL_SP_SNIKSNAK ||
6995 smashed == EL_SP_ELECTRON ||
6996 smashed == EL_SP_DISK_ORANGE)) ||
6997 (element == EL_SP_INFOTRON &&
6998 smashed == EL_SP_DISK_YELLOW))
7003 else if (CAN_SMASH_EVERYTHING(element))
7005 if (IS_CLASSIC_ENEMY(smashed) ||
7006 CAN_EXPLODE_SMASHED(smashed))
7011 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7013 if (smashed == EL_LAMP ||
7014 smashed == EL_LAMP_ACTIVE)
7019 else if (smashed == EL_NUT)
7021 Feld[x][y + 1] = EL_NUT_BREAKING;
7022 PlayLevelSound(x, y, SND_NUT_BREAKING);
7023 RaiseScoreElement(EL_NUT);
7026 else if (smashed == EL_PEARL)
7028 ResetGfxAnimation(x, y);
7030 Feld[x][y + 1] = EL_PEARL_BREAKING;
7031 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7034 else if (smashed == EL_DIAMOND)
7036 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7037 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7040 else if (IS_BELT_SWITCH(smashed))
7042 ToggleBeltSwitch(x, y + 1);
7044 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7045 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7046 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7047 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7049 ToggleSwitchgateSwitch(x, y + 1);
7051 else if (smashed == EL_LIGHT_SWITCH ||
7052 smashed == EL_LIGHT_SWITCH_ACTIVE)
7054 ToggleLightSwitch(x, y + 1);
7059 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7062 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7064 CheckElementChangeBySide(x, y + 1, smashed, element,
7065 CE_SWITCHED, CH_SIDE_TOP);
7066 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7072 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7077 /* play sound of magic wall / mill */
7079 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7080 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7081 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7083 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7084 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7085 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7086 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7087 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7088 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7093 /* play sound of object that hits the ground */
7094 if (last_line || object_hit)
7095 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7098 inline static void TurnRoundExt(int x, int y)
7110 { 0, 0 }, { 0, 0 }, { 0, 0 },
7115 int left, right, back;
7119 { MV_DOWN, MV_UP, MV_RIGHT },
7120 { MV_UP, MV_DOWN, MV_LEFT },
7122 { MV_LEFT, MV_RIGHT, MV_DOWN },
7126 { MV_RIGHT, MV_LEFT, MV_UP }
7129 int element = Feld[x][y];
7130 int move_pattern = element_info[element].move_pattern;
7132 int old_move_dir = MovDir[x][y];
7133 int left_dir = turn[old_move_dir].left;
7134 int right_dir = turn[old_move_dir].right;
7135 int back_dir = turn[old_move_dir].back;
7137 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7138 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7139 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7140 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7142 int left_x = x + left_dx, left_y = y + left_dy;
7143 int right_x = x + right_dx, right_y = y + right_dy;
7144 int move_x = x + move_dx, move_y = y + move_dy;
7148 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7150 TestIfBadThingTouchesOtherBadThing(x, y);
7152 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7153 MovDir[x][y] = right_dir;
7154 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7155 MovDir[x][y] = left_dir;
7157 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7159 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7162 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7164 TestIfBadThingTouchesOtherBadThing(x, y);
7166 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7167 MovDir[x][y] = left_dir;
7168 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7169 MovDir[x][y] = right_dir;
7171 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7173 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7176 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7178 TestIfBadThingTouchesOtherBadThing(x, y);
7180 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7181 MovDir[x][y] = left_dir;
7182 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7183 MovDir[x][y] = right_dir;
7185 if (MovDir[x][y] != old_move_dir)
7188 else if (element == EL_YAMYAM)
7190 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7191 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7193 if (can_turn_left && can_turn_right)
7194 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7195 else if (can_turn_left)
7196 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7197 else if (can_turn_right)
7198 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7200 MovDir[x][y] = back_dir;
7202 MovDelay[x][y] = 16 + 16 * RND(3);
7204 else if (element == EL_DARK_YAMYAM)
7206 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7208 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7211 if (can_turn_left && can_turn_right)
7212 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7213 else if (can_turn_left)
7214 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7215 else if (can_turn_right)
7216 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7218 MovDir[x][y] = back_dir;
7220 MovDelay[x][y] = 16 + 16 * RND(3);
7222 else if (element == EL_PACMAN)
7224 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7225 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7227 if (can_turn_left && can_turn_right)
7228 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7229 else if (can_turn_left)
7230 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7231 else if (can_turn_right)
7232 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7234 MovDir[x][y] = back_dir;
7236 MovDelay[x][y] = 6 + RND(40);
7238 else if (element == EL_PIG)
7240 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7241 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7242 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7243 boolean should_turn_left, should_turn_right, should_move_on;
7245 int rnd = RND(rnd_value);
7247 should_turn_left = (can_turn_left &&
7249 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7250 y + back_dy + left_dy)));
7251 should_turn_right = (can_turn_right &&
7253 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7254 y + back_dy + right_dy)));
7255 should_move_on = (can_move_on &&
7258 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7259 y + move_dy + left_dy) ||
7260 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7261 y + move_dy + right_dy)));
7263 if (should_turn_left || should_turn_right || should_move_on)
7265 if (should_turn_left && should_turn_right && should_move_on)
7266 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7267 rnd < 2 * rnd_value / 3 ? right_dir :
7269 else if (should_turn_left && should_turn_right)
7270 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7271 else if (should_turn_left && should_move_on)
7272 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7273 else if (should_turn_right && should_move_on)
7274 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7275 else if (should_turn_left)
7276 MovDir[x][y] = left_dir;
7277 else if (should_turn_right)
7278 MovDir[x][y] = right_dir;
7279 else if (should_move_on)
7280 MovDir[x][y] = old_move_dir;
7282 else if (can_move_on && rnd > rnd_value / 8)
7283 MovDir[x][y] = old_move_dir;
7284 else if (can_turn_left && can_turn_right)
7285 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7286 else if (can_turn_left && rnd > rnd_value / 8)
7287 MovDir[x][y] = left_dir;
7288 else if (can_turn_right && rnd > rnd_value/8)
7289 MovDir[x][y] = right_dir;
7291 MovDir[x][y] = back_dir;
7293 xx = x + move_xy[MovDir[x][y]].dx;
7294 yy = y + move_xy[MovDir[x][y]].dy;
7296 if (!IN_LEV_FIELD(xx, yy) ||
7297 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7298 MovDir[x][y] = old_move_dir;
7302 else if (element == EL_DRAGON)
7304 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7305 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7306 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7308 int rnd = RND(rnd_value);
7310 if (can_move_on && rnd > rnd_value / 8)
7311 MovDir[x][y] = old_move_dir;
7312 else if (can_turn_left && can_turn_right)
7313 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7314 else if (can_turn_left && rnd > rnd_value / 8)
7315 MovDir[x][y] = left_dir;
7316 else if (can_turn_right && rnd > rnd_value / 8)
7317 MovDir[x][y] = right_dir;
7319 MovDir[x][y] = back_dir;
7321 xx = x + move_xy[MovDir[x][y]].dx;
7322 yy = y + move_xy[MovDir[x][y]].dy;
7324 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7325 MovDir[x][y] = old_move_dir;
7329 else if (element == EL_MOLE)
7331 boolean can_move_on =
7332 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7333 IS_AMOEBOID(Feld[move_x][move_y]) ||
7334 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7337 boolean can_turn_left =
7338 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7339 IS_AMOEBOID(Feld[left_x][left_y])));
7341 boolean can_turn_right =
7342 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7343 IS_AMOEBOID(Feld[right_x][right_y])));
7345 if (can_turn_left && can_turn_right)
7346 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7347 else if (can_turn_left)
7348 MovDir[x][y] = left_dir;
7350 MovDir[x][y] = right_dir;
7353 if (MovDir[x][y] != old_move_dir)
7356 else if (element == EL_BALLOON)
7358 MovDir[x][y] = game.wind_direction;
7361 else if (element == EL_SPRING)
7363 #if USE_NEW_SPRING_BUMPER
7364 if (MovDir[x][y] & MV_HORIZONTAL)
7366 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7367 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7369 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7370 ResetGfxAnimation(move_x, move_y);
7371 TEST_DrawLevelField(move_x, move_y);
7373 MovDir[x][y] = back_dir;
7375 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7376 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7377 MovDir[x][y] = MV_NONE;
7380 if (MovDir[x][y] & MV_HORIZONTAL &&
7381 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7382 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7383 MovDir[x][y] = MV_NONE;
7388 else if (element == EL_ROBOT ||
7389 element == EL_SATELLITE ||
7390 element == EL_PENGUIN ||
7391 element == EL_EMC_ANDROID)
7393 int attr_x = -1, attr_y = -1;
7404 for (i = 0; i < MAX_PLAYERS; i++)
7406 struct PlayerInfo *player = &stored_player[i];
7407 int jx = player->jx, jy = player->jy;
7409 if (!player->active)
7413 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7421 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7422 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7423 game.engine_version < VERSION_IDENT(3,1,0,0)))
7429 if (element == EL_PENGUIN)
7432 static int xy[4][2] =
7440 for (i = 0; i < NUM_DIRECTIONS; i++)
7442 int ex = x + xy[i][0];
7443 int ey = y + xy[i][1];
7445 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7446 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7447 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7448 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7457 MovDir[x][y] = MV_NONE;
7459 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7460 else if (attr_x > x)
7461 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7463 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7464 else if (attr_y > y)
7465 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7467 if (element == EL_ROBOT)
7471 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7472 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7473 Moving2Blocked(x, y, &newx, &newy);
7475 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7476 MovDelay[x][y] = 8 + 8 * !RND(3);
7478 MovDelay[x][y] = 16;
7480 else if (element == EL_PENGUIN)
7486 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7488 boolean first_horiz = RND(2);
7489 int new_move_dir = MovDir[x][y];
7492 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7493 Moving2Blocked(x, y, &newx, &newy);
7495 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7499 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7500 Moving2Blocked(x, y, &newx, &newy);
7502 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7505 MovDir[x][y] = old_move_dir;
7509 else if (element == EL_SATELLITE)
7515 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7517 boolean first_horiz = RND(2);
7518 int new_move_dir = MovDir[x][y];
7521 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7522 Moving2Blocked(x, y, &newx, &newy);
7524 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7528 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7529 Moving2Blocked(x, y, &newx, &newy);
7531 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7534 MovDir[x][y] = old_move_dir;
7538 else if (element == EL_EMC_ANDROID)
7540 static int check_pos[16] =
7542 -1, /* 0 => (invalid) */
7543 7, /* 1 => MV_LEFT */
7544 3, /* 2 => MV_RIGHT */
7545 -1, /* 3 => (invalid) */
7547 0, /* 5 => MV_LEFT | MV_UP */
7548 2, /* 6 => MV_RIGHT | MV_UP */
7549 -1, /* 7 => (invalid) */
7550 5, /* 8 => MV_DOWN */
7551 6, /* 9 => MV_LEFT | MV_DOWN */
7552 4, /* 10 => MV_RIGHT | MV_DOWN */
7553 -1, /* 11 => (invalid) */
7554 -1, /* 12 => (invalid) */
7555 -1, /* 13 => (invalid) */
7556 -1, /* 14 => (invalid) */
7557 -1, /* 15 => (invalid) */
7565 { -1, -1, MV_LEFT | MV_UP },
7567 { +1, -1, MV_RIGHT | MV_UP },
7568 { +1, 0, MV_RIGHT },
7569 { +1, +1, MV_RIGHT | MV_DOWN },
7571 { -1, +1, MV_LEFT | MV_DOWN },
7574 int start_pos, check_order;
7575 boolean can_clone = FALSE;
7578 /* check if there is any free field around current position */
7579 for (i = 0; i < 8; i++)
7581 int newx = x + check_xy[i].dx;
7582 int newy = y + check_xy[i].dy;
7584 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7592 if (can_clone) /* randomly find an element to clone */
7596 start_pos = check_pos[RND(8)];
7597 check_order = (RND(2) ? -1 : +1);
7599 for (i = 0; i < 8; i++)
7601 int pos_raw = start_pos + i * check_order;
7602 int pos = (pos_raw + 8) % 8;
7603 int newx = x + check_xy[pos].dx;
7604 int newy = y + check_xy[pos].dy;
7606 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7608 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7609 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7611 Store[x][y] = Feld[newx][newy];
7620 if (can_clone) /* randomly find a direction to move */
7624 start_pos = check_pos[RND(8)];
7625 check_order = (RND(2) ? -1 : +1);
7627 for (i = 0; i < 8; i++)
7629 int pos_raw = start_pos + i * check_order;
7630 int pos = (pos_raw + 8) % 8;
7631 int newx = x + check_xy[pos].dx;
7632 int newy = y + check_xy[pos].dy;
7633 int new_move_dir = check_xy[pos].dir;
7635 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7637 MovDir[x][y] = new_move_dir;
7638 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7647 if (can_clone) /* cloning and moving successful */
7650 /* cannot clone -- try to move towards player */
7652 start_pos = check_pos[MovDir[x][y] & 0x0f];
7653 check_order = (RND(2) ? -1 : +1);
7655 for (i = 0; i < 3; i++)
7657 /* first check start_pos, then previous/next or (next/previous) pos */
7658 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7659 int pos = (pos_raw + 8) % 8;
7660 int newx = x + check_xy[pos].dx;
7661 int newy = y + check_xy[pos].dy;
7662 int new_move_dir = check_xy[pos].dir;
7664 if (IS_PLAYER(newx, newy))
7667 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7669 MovDir[x][y] = new_move_dir;
7670 MovDelay[x][y] = level.android_move_time * 8 + 1;
7677 else if (move_pattern == MV_TURNING_LEFT ||
7678 move_pattern == MV_TURNING_RIGHT ||
7679 move_pattern == MV_TURNING_LEFT_RIGHT ||
7680 move_pattern == MV_TURNING_RIGHT_LEFT ||
7681 move_pattern == MV_TURNING_RANDOM ||
7682 move_pattern == MV_ALL_DIRECTIONS)
7684 boolean can_turn_left =
7685 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7686 boolean can_turn_right =
7687 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7689 if (element_info[element].move_stepsize == 0) /* "not moving" */
7692 if (move_pattern == MV_TURNING_LEFT)
7693 MovDir[x][y] = left_dir;
7694 else if (move_pattern == MV_TURNING_RIGHT)
7695 MovDir[x][y] = right_dir;
7696 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7697 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7698 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7699 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7700 else if (move_pattern == MV_TURNING_RANDOM)
7701 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7702 can_turn_right && !can_turn_left ? right_dir :
7703 RND(2) ? left_dir : right_dir);
7704 else if (can_turn_left && can_turn_right)
7705 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7706 else if (can_turn_left)
7707 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7708 else if (can_turn_right)
7709 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7711 MovDir[x][y] = back_dir;
7713 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7715 else if (move_pattern == MV_HORIZONTAL ||
7716 move_pattern == MV_VERTICAL)
7718 if (move_pattern & old_move_dir)
7719 MovDir[x][y] = back_dir;
7720 else if (move_pattern == MV_HORIZONTAL)
7721 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7722 else if (move_pattern == MV_VERTICAL)
7723 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7725 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7727 else if (move_pattern & MV_ANY_DIRECTION)
7729 MovDir[x][y] = move_pattern;
7730 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7732 else if (move_pattern & MV_WIND_DIRECTION)
7734 MovDir[x][y] = game.wind_direction;
7735 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7737 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7739 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7740 MovDir[x][y] = left_dir;
7741 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7742 MovDir[x][y] = right_dir;
7744 if (MovDir[x][y] != old_move_dir)
7745 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7747 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7749 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7750 MovDir[x][y] = right_dir;
7751 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7752 MovDir[x][y] = left_dir;
7754 if (MovDir[x][y] != old_move_dir)
7755 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7757 else if (move_pattern == MV_TOWARDS_PLAYER ||
7758 move_pattern == MV_AWAY_FROM_PLAYER)
7760 int attr_x = -1, attr_y = -1;
7762 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7773 for (i = 0; i < MAX_PLAYERS; i++)
7775 struct PlayerInfo *player = &stored_player[i];
7776 int jx = player->jx, jy = player->jy;
7778 if (!player->active)
7782 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7790 MovDir[x][y] = MV_NONE;
7792 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7793 else if (attr_x > x)
7794 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7796 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7797 else if (attr_y > y)
7798 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7800 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7802 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7804 boolean first_horiz = RND(2);
7805 int new_move_dir = MovDir[x][y];
7807 if (element_info[element].move_stepsize == 0) /* "not moving" */
7809 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7810 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7816 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7817 Moving2Blocked(x, y, &newx, &newy);
7819 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7823 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7824 Moving2Blocked(x, y, &newx, &newy);
7826 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7829 MovDir[x][y] = old_move_dir;
7832 else if (move_pattern == MV_WHEN_PUSHED ||
7833 move_pattern == MV_WHEN_DROPPED)
7835 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7836 MovDir[x][y] = MV_NONE;
7840 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7842 static int test_xy[7][2] =
7852 static int test_dir[7] =
7862 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7863 int move_preference = -1000000; /* start with very low preference */
7864 int new_move_dir = MV_NONE;
7865 int start_test = RND(4);
7868 for (i = 0; i < NUM_DIRECTIONS; i++)
7870 int move_dir = test_dir[start_test + i];
7871 int move_dir_preference;
7873 xx = x + test_xy[start_test + i][0];
7874 yy = y + test_xy[start_test + i][1];
7876 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7877 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7879 new_move_dir = move_dir;
7884 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7887 move_dir_preference = -1 * RunnerVisit[xx][yy];
7888 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7889 move_dir_preference = PlayerVisit[xx][yy];
7891 if (move_dir_preference > move_preference)
7893 /* prefer field that has not been visited for the longest time */
7894 move_preference = move_dir_preference;
7895 new_move_dir = move_dir;
7897 else if (move_dir_preference == move_preference &&
7898 move_dir == old_move_dir)
7900 /* prefer last direction when all directions are preferred equally */
7901 move_preference = move_dir_preference;
7902 new_move_dir = move_dir;
7906 MovDir[x][y] = new_move_dir;
7907 if (old_move_dir != new_move_dir)
7908 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7912 static void TurnRound(int x, int y)
7914 int direction = MovDir[x][y];
7918 GfxDir[x][y] = MovDir[x][y];
7920 if (direction != MovDir[x][y])
7924 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7926 ResetGfxFrame(x, y, FALSE);
7929 static boolean JustBeingPushed(int x, int y)
7933 for (i = 0; i < MAX_PLAYERS; i++)
7935 struct PlayerInfo *player = &stored_player[i];
7937 if (player->active && player->is_pushing && player->MovPos)
7939 int next_jx = player->jx + (player->jx - player->last_jx);
7940 int next_jy = player->jy + (player->jy - player->last_jy);
7942 if (x == next_jx && y == next_jy)
7950 void StartMoving(int x, int y)
7952 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7953 int element = Feld[x][y];
7958 if (MovDelay[x][y] == 0)
7959 GfxAction[x][y] = ACTION_DEFAULT;
7961 if (CAN_FALL(element) && y < lev_fieldy - 1)
7963 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7964 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7965 if (JustBeingPushed(x, y))
7968 if (element == EL_QUICKSAND_FULL)
7970 if (IS_FREE(x, y + 1))
7972 InitMovingField(x, y, MV_DOWN);
7973 started_moving = TRUE;
7975 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7976 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7977 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7978 Store[x][y] = EL_ROCK;
7980 Store[x][y] = EL_ROCK;
7983 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7985 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7987 if (!MovDelay[x][y])
7989 MovDelay[x][y] = TILEY + 1;
7991 ResetGfxAnimation(x, y);
7992 ResetGfxAnimation(x, y + 1);
7997 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7998 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8005 Feld[x][y] = EL_QUICKSAND_EMPTY;
8006 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8007 Store[x][y + 1] = Store[x][y];
8010 PlayLevelSoundAction(x, y, ACTION_FILLING);
8012 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8014 if (!MovDelay[x][y])
8016 MovDelay[x][y] = TILEY + 1;
8018 ResetGfxAnimation(x, y);
8019 ResetGfxAnimation(x, y + 1);
8024 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8025 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8032 Feld[x][y] = EL_QUICKSAND_EMPTY;
8033 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8034 Store[x][y + 1] = Store[x][y];
8037 PlayLevelSoundAction(x, y, ACTION_FILLING);
8040 else if (element == EL_QUICKSAND_FAST_FULL)
8042 if (IS_FREE(x, y + 1))
8044 InitMovingField(x, y, MV_DOWN);
8045 started_moving = TRUE;
8047 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8048 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8049 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8050 Store[x][y] = EL_ROCK;
8052 Store[x][y] = EL_ROCK;
8055 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8057 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8059 if (!MovDelay[x][y])
8061 MovDelay[x][y] = TILEY + 1;
8063 ResetGfxAnimation(x, y);
8064 ResetGfxAnimation(x, y + 1);
8069 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8070 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8077 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8078 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8079 Store[x][y + 1] = Store[x][y];
8082 PlayLevelSoundAction(x, y, ACTION_FILLING);
8084 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8086 if (!MovDelay[x][y])
8088 MovDelay[x][y] = TILEY + 1;
8090 ResetGfxAnimation(x, y);
8091 ResetGfxAnimation(x, y + 1);
8096 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8097 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8104 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8105 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8106 Store[x][y + 1] = Store[x][y];
8109 PlayLevelSoundAction(x, y, ACTION_FILLING);
8112 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8113 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8115 InitMovingField(x, y, MV_DOWN);
8116 started_moving = TRUE;
8118 Feld[x][y] = EL_QUICKSAND_FILLING;
8119 Store[x][y] = element;
8121 PlayLevelSoundAction(x, y, ACTION_FILLING);
8123 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8124 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8126 InitMovingField(x, y, MV_DOWN);
8127 started_moving = TRUE;
8129 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8130 Store[x][y] = element;
8132 PlayLevelSoundAction(x, y, ACTION_FILLING);
8134 else if (element == EL_MAGIC_WALL_FULL)
8136 if (IS_FREE(x, y + 1))
8138 InitMovingField(x, y, MV_DOWN);
8139 started_moving = TRUE;
8141 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8142 Store[x][y] = EL_CHANGED(Store[x][y]);
8144 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8146 if (!MovDelay[x][y])
8147 MovDelay[x][y] = TILEY / 4 + 1;
8156 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8157 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8158 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8162 else if (element == EL_BD_MAGIC_WALL_FULL)
8164 if (IS_FREE(x, y + 1))
8166 InitMovingField(x, y, MV_DOWN);
8167 started_moving = TRUE;
8169 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8170 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8172 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8174 if (!MovDelay[x][y])
8175 MovDelay[x][y] = TILEY / 4 + 1;
8184 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8185 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8186 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8190 else if (element == EL_DC_MAGIC_WALL_FULL)
8192 if (IS_FREE(x, y + 1))
8194 InitMovingField(x, y, MV_DOWN);
8195 started_moving = TRUE;
8197 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8198 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8200 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8202 if (!MovDelay[x][y])
8203 MovDelay[x][y] = TILEY / 4 + 1;
8212 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8213 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8214 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8218 else if ((CAN_PASS_MAGIC_WALL(element) &&
8219 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8220 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8221 (CAN_PASS_DC_MAGIC_WALL(element) &&
8222 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8225 InitMovingField(x, y, MV_DOWN);
8226 started_moving = TRUE;
8229 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8230 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8231 EL_DC_MAGIC_WALL_FILLING);
8232 Store[x][y] = element;
8234 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8236 SplashAcid(x, y + 1);
8238 InitMovingField(x, y, MV_DOWN);
8239 started_moving = TRUE;
8241 Store[x][y] = EL_ACID;
8244 #if USE_FIX_IMPACT_COLLISION
8245 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8246 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8248 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8249 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8251 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8252 CAN_FALL(element) && WasJustFalling[x][y] &&
8253 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8255 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8256 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8257 (Feld[x][y + 1] == EL_BLOCKED)))
8259 /* this is needed for a special case not covered by calling "Impact()"
8260 from "ContinueMoving()": if an element moves to a tile directly below
8261 another element which was just falling on that tile (which was empty
8262 in the previous frame), the falling element above would just stop
8263 instead of smashing the element below (in previous version, the above
8264 element was just checked for "moving" instead of "falling", resulting
8265 in incorrect smashes caused by horizontal movement of the above
8266 element; also, the case of the player being the element to smash was
8267 simply not covered here... :-/ ) */
8269 CheckCollision[x][y] = 0;
8270 CheckImpact[x][y] = 0;
8274 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8276 if (MovDir[x][y] == MV_NONE)
8278 InitMovingField(x, y, MV_DOWN);
8279 started_moving = TRUE;
8282 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8284 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8285 MovDir[x][y] = MV_DOWN;
8287 InitMovingField(x, y, MV_DOWN);
8288 started_moving = TRUE;
8290 else if (element == EL_AMOEBA_DROP)
8292 Feld[x][y] = EL_AMOEBA_GROWING;
8293 Store[x][y] = EL_AMOEBA_WET;
8295 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8296 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8297 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8298 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8300 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8301 (IS_FREE(x - 1, y + 1) ||
8302 Feld[x - 1][y + 1] == EL_ACID));
8303 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8304 (IS_FREE(x + 1, y + 1) ||
8305 Feld[x + 1][y + 1] == EL_ACID));
8306 boolean can_fall_any = (can_fall_left || can_fall_right);
8307 boolean can_fall_both = (can_fall_left && can_fall_right);
8308 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8310 #if USE_NEW_ALL_SLIPPERY
8311 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8313 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8314 can_fall_right = FALSE;
8315 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8316 can_fall_left = FALSE;
8317 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8318 can_fall_right = FALSE;
8319 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8320 can_fall_left = FALSE;
8322 can_fall_any = (can_fall_left || can_fall_right);
8323 can_fall_both = FALSE;
8326 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8328 if (slippery_type == SLIPPERY_ONLY_LEFT)
8329 can_fall_right = FALSE;
8330 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8331 can_fall_left = FALSE;
8332 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8333 can_fall_right = FALSE;
8334 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8335 can_fall_left = FALSE;
8337 can_fall_any = (can_fall_left || can_fall_right);
8338 can_fall_both = (can_fall_left && can_fall_right);
8342 #if USE_NEW_ALL_SLIPPERY
8344 #if USE_NEW_SP_SLIPPERY
8345 /* !!! better use the same properties as for custom elements here !!! */
8346 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8347 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8349 can_fall_right = FALSE; /* slip down on left side */
8350 can_fall_both = FALSE;
8355 #if USE_NEW_ALL_SLIPPERY
8358 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8359 can_fall_right = FALSE; /* slip down on left side */
8361 can_fall_left = !(can_fall_right = RND(2));
8363 can_fall_both = FALSE;
8368 if (game.emulation == EMU_BOULDERDASH ||
8369 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8370 can_fall_right = FALSE; /* slip down on left side */
8372 can_fall_left = !(can_fall_right = RND(2));
8374 can_fall_both = FALSE;
8380 /* if not determined otherwise, prefer left side for slipping down */
8381 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8382 started_moving = TRUE;
8386 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8388 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8391 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8392 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8393 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8394 int belt_dir = game.belt_dir[belt_nr];
8396 if ((belt_dir == MV_LEFT && left_is_free) ||
8397 (belt_dir == MV_RIGHT && right_is_free))
8399 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8401 InitMovingField(x, y, belt_dir);
8402 started_moving = TRUE;
8404 Pushed[x][y] = TRUE;
8405 Pushed[nextx][y] = TRUE;
8407 GfxAction[x][y] = ACTION_DEFAULT;
8411 MovDir[x][y] = 0; /* if element was moving, stop it */
8416 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8418 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8420 if (CAN_MOVE(element) && !started_moving)
8423 int move_pattern = element_info[element].move_pattern;
8428 if (MovDir[x][y] == MV_NONE)
8430 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8431 x, y, element, element_info[element].token_name);
8432 printf("StartMoving(): This should never happen!\n");
8437 Moving2Blocked(x, y, &newx, &newy);
8439 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8442 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8443 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8445 WasJustMoving[x][y] = 0;
8446 CheckCollision[x][y] = 0;
8448 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8450 if (Feld[x][y] != element) /* element has changed */
8454 if (!MovDelay[x][y]) /* start new movement phase */
8456 /* all objects that can change their move direction after each step
8457 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8459 if (element != EL_YAMYAM &&
8460 element != EL_DARK_YAMYAM &&
8461 element != EL_PACMAN &&
8462 !(move_pattern & MV_ANY_DIRECTION) &&
8463 move_pattern != MV_TURNING_LEFT &&
8464 move_pattern != MV_TURNING_RIGHT &&
8465 move_pattern != MV_TURNING_LEFT_RIGHT &&
8466 move_pattern != MV_TURNING_RIGHT_LEFT &&
8467 move_pattern != MV_TURNING_RANDOM)
8471 if (MovDelay[x][y] && (element == EL_BUG ||
8472 element == EL_SPACESHIP ||
8473 element == EL_SP_SNIKSNAK ||
8474 element == EL_SP_ELECTRON ||
8475 element == EL_MOLE))
8476 TEST_DrawLevelField(x, y);
8480 if (MovDelay[x][y]) /* wait some time before next movement */
8484 if (element == EL_ROBOT ||
8485 element == EL_YAMYAM ||
8486 element == EL_DARK_YAMYAM)
8488 DrawLevelElementAnimationIfNeeded(x, y, element);
8489 PlayLevelSoundAction(x, y, ACTION_WAITING);
8491 else if (element == EL_SP_ELECTRON)
8492 DrawLevelElementAnimationIfNeeded(x, y, element);
8493 else if (element == EL_DRAGON)
8496 int dir = MovDir[x][y];
8497 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8498 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8499 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8500 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8501 dir == MV_UP ? IMG_FLAMES_1_UP :
8502 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8503 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8505 GfxAction[x][y] = ACTION_ATTACKING;
8507 if (IS_PLAYER(x, y))
8508 DrawPlayerField(x, y);
8510 TEST_DrawLevelField(x, y);
8512 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8514 for (i = 1; i <= 3; i++)
8516 int xx = x + i * dx;
8517 int yy = y + i * dy;
8518 int sx = SCREENX(xx);
8519 int sy = SCREENY(yy);
8520 int flame_graphic = graphic + (i - 1);
8522 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8527 int flamed = MovingOrBlocked2Element(xx, yy);
8531 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8533 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8534 RemoveMovingField(xx, yy);
8536 RemoveField(xx, yy);
8538 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8541 RemoveMovingField(xx, yy);
8544 ChangeDelay[xx][yy] = 0;
8546 Feld[xx][yy] = EL_FLAMES;
8548 if (IN_SCR_FIELD(sx, sy))
8550 TEST_DrawLevelFieldCrumbled(xx, yy);
8551 DrawGraphic(sx, sy, flame_graphic, frame);
8556 if (Feld[xx][yy] == EL_FLAMES)
8557 Feld[xx][yy] = EL_EMPTY;
8558 TEST_DrawLevelField(xx, yy);
8563 if (MovDelay[x][y]) /* element still has to wait some time */
8565 PlayLevelSoundAction(x, y, ACTION_WAITING);
8571 /* now make next step */
8573 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8575 if (DONT_COLLIDE_WITH(element) &&
8576 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8577 !PLAYER_ENEMY_PROTECTED(newx, newy))
8579 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8584 else if (CAN_MOVE_INTO_ACID(element) &&
8585 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8586 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8587 (MovDir[x][y] == MV_DOWN ||
8588 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8590 SplashAcid(newx, newy);
8591 Store[x][y] = EL_ACID;
8593 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8595 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8596 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8597 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8598 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8601 TEST_DrawLevelField(x, y);
8603 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8604 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8605 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8607 local_player->friends_still_needed--;
8608 if (!local_player->friends_still_needed &&
8609 !local_player->GameOver && AllPlayersGone)
8610 PlayerWins(local_player);
8614 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8616 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8617 TEST_DrawLevelField(newx, newy);
8619 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8621 else if (!IS_FREE(newx, newy))
8623 GfxAction[x][y] = ACTION_WAITING;
8625 if (IS_PLAYER(x, y))
8626 DrawPlayerField(x, y);
8628 TEST_DrawLevelField(x, y);
8633 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8635 if (IS_FOOD_PIG(Feld[newx][newy]))
8637 if (IS_MOVING(newx, newy))
8638 RemoveMovingField(newx, newy);
8641 Feld[newx][newy] = EL_EMPTY;
8642 TEST_DrawLevelField(newx, newy);
8645 PlayLevelSound(x, y, SND_PIG_DIGGING);
8647 else if (!IS_FREE(newx, newy))
8649 if (IS_PLAYER(x, y))
8650 DrawPlayerField(x, y);
8652 TEST_DrawLevelField(x, y);
8657 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8659 if (Store[x][y] != EL_EMPTY)
8661 boolean can_clone = FALSE;
8664 /* check if element to clone is still there */
8665 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8667 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8675 /* cannot clone or target field not free anymore -- do not clone */
8676 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8677 Store[x][y] = EL_EMPTY;
8680 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8682 if (IS_MV_DIAGONAL(MovDir[x][y]))
8684 int diagonal_move_dir = MovDir[x][y];
8685 int stored = Store[x][y];
8686 int change_delay = 8;
8689 /* android is moving diagonally */
8691 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8693 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8694 GfxElement[x][y] = EL_EMC_ANDROID;
8695 GfxAction[x][y] = ACTION_SHRINKING;
8696 GfxDir[x][y] = diagonal_move_dir;
8697 ChangeDelay[x][y] = change_delay;
8699 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8702 DrawLevelGraphicAnimation(x, y, graphic);
8703 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8705 if (Feld[newx][newy] == EL_ACID)
8707 SplashAcid(newx, newy);
8712 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8714 Store[newx][newy] = EL_EMC_ANDROID;
8715 GfxElement[newx][newy] = EL_EMC_ANDROID;
8716 GfxAction[newx][newy] = ACTION_GROWING;
8717 GfxDir[newx][newy] = diagonal_move_dir;
8718 ChangeDelay[newx][newy] = change_delay;
8720 graphic = el_act_dir2img(GfxElement[newx][newy],
8721 GfxAction[newx][newy], GfxDir[newx][newy]);
8723 DrawLevelGraphicAnimation(newx, newy, graphic);
8724 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8730 Feld[newx][newy] = EL_EMPTY;
8731 TEST_DrawLevelField(newx, newy);
8733 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8736 else if (!IS_FREE(newx, newy))
8739 if (IS_PLAYER(x, y))
8740 DrawPlayerField(x, y);
8742 TEST_DrawLevelField(x, y);
8748 else if (IS_CUSTOM_ELEMENT(element) &&
8749 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8752 if (!DigFieldByCE(newx, newy, element))
8755 int new_element = Feld[newx][newy];
8757 if (!IS_FREE(newx, newy))
8759 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8760 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8763 /* no element can dig solid indestructible elements */
8764 if (IS_INDESTRUCTIBLE(new_element) &&
8765 !IS_DIGGABLE(new_element) &&
8766 !IS_COLLECTIBLE(new_element))
8769 if (AmoebaNr[newx][newy] &&
8770 (new_element == EL_AMOEBA_FULL ||
8771 new_element == EL_BD_AMOEBA ||
8772 new_element == EL_AMOEBA_GROWING))
8774 AmoebaCnt[AmoebaNr[newx][newy]]--;
8775 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8778 if (IS_MOVING(newx, newy))
8779 RemoveMovingField(newx, newy);
8782 RemoveField(newx, newy);
8783 TEST_DrawLevelField(newx, newy);
8786 /* if digged element was about to explode, prevent the explosion */
8787 ExplodeField[newx][newy] = EX_TYPE_NONE;
8789 PlayLevelSoundAction(x, y, action);
8792 Store[newx][newy] = EL_EMPTY;
8795 /* this makes it possible to leave the removed element again */
8796 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8797 Store[newx][newy] = new_element;
8799 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8801 int move_leave_element = element_info[element].move_leave_element;
8803 /* this makes it possible to leave the removed element again */
8804 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8805 new_element : move_leave_element);
8811 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8813 RunnerVisit[x][y] = FrameCounter;
8814 PlayerVisit[x][y] /= 8; /* expire player visit path */
8817 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8819 if (!IS_FREE(newx, newy))
8821 if (IS_PLAYER(x, y))
8822 DrawPlayerField(x, y);
8824 TEST_DrawLevelField(x, y);
8830 boolean wanna_flame = !RND(10);
8831 int dx = newx - x, dy = newy - y;
8832 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8833 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8834 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8835 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8836 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8837 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8840 IS_CLASSIC_ENEMY(element1) ||
8841 IS_CLASSIC_ENEMY(element2)) &&
8842 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8843 element1 != EL_FLAMES && element2 != EL_FLAMES)
8845 ResetGfxAnimation(x, y);
8846 GfxAction[x][y] = ACTION_ATTACKING;
8848 if (IS_PLAYER(x, y))
8849 DrawPlayerField(x, y);
8851 TEST_DrawLevelField(x, y);
8853 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8855 MovDelay[x][y] = 50;
8859 RemoveField(newx, newy);
8861 Feld[newx][newy] = EL_FLAMES;
8862 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8865 RemoveField(newx1, newy1);
8867 Feld[newx1][newy1] = EL_FLAMES;
8869 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8872 RemoveField(newx2, newy2);
8874 Feld[newx2][newy2] = EL_FLAMES;
8881 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8882 Feld[newx][newy] == EL_DIAMOND)
8884 if (IS_MOVING(newx, newy))
8885 RemoveMovingField(newx, newy);
8888 Feld[newx][newy] = EL_EMPTY;
8889 TEST_DrawLevelField(newx, newy);
8892 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8894 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8895 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8897 if (AmoebaNr[newx][newy])
8899 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8900 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8901 Feld[newx][newy] == EL_BD_AMOEBA)
8902 AmoebaCnt[AmoebaNr[newx][newy]]--;
8907 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8909 RemoveMovingField(newx, newy);
8912 if (IS_MOVING(newx, newy))
8914 RemoveMovingField(newx, newy);
8919 Feld[newx][newy] = EL_EMPTY;
8920 TEST_DrawLevelField(newx, newy);
8923 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8925 else if ((element == EL_PACMAN || element == EL_MOLE)
8926 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8928 if (AmoebaNr[newx][newy])
8930 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8931 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8932 Feld[newx][newy] == EL_BD_AMOEBA)
8933 AmoebaCnt[AmoebaNr[newx][newy]]--;
8936 if (element == EL_MOLE)
8938 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8939 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8941 ResetGfxAnimation(x, y);
8942 GfxAction[x][y] = ACTION_DIGGING;
8943 TEST_DrawLevelField(x, y);
8945 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8947 return; /* wait for shrinking amoeba */
8949 else /* element == EL_PACMAN */
8951 Feld[newx][newy] = EL_EMPTY;
8952 TEST_DrawLevelField(newx, newy);
8953 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8956 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8957 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8958 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8960 /* wait for shrinking amoeba to completely disappear */
8963 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8965 /* object was running against a wall */
8970 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8971 if (move_pattern & MV_ANY_DIRECTION &&
8972 move_pattern == MovDir[x][y])
8974 int blocking_element =
8975 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8977 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8980 element = Feld[x][y]; /* element might have changed */
8984 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8985 DrawLevelElementAnimation(x, y, element);
8987 if (DONT_TOUCH(element))
8988 TestIfBadThingTouchesPlayer(x, y);
8993 InitMovingField(x, y, MovDir[x][y]);
8995 PlayLevelSoundAction(x, y, ACTION_MOVING);
8999 ContinueMoving(x, y);
9002 void ContinueMoving(int x, int y)
9004 int element = Feld[x][y];
9005 struct ElementInfo *ei = &element_info[element];
9006 int direction = MovDir[x][y];
9007 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9008 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9009 int newx = x + dx, newy = y + dy;
9010 int stored = Store[x][y];
9011 int stored_new = Store[newx][newy];
9012 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9013 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9014 boolean last_line = (newy == lev_fieldy - 1);
9016 MovPos[x][y] += getElementMoveStepsize(x, y);
9018 if (pushed_by_player) /* special case: moving object pushed by player */
9019 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9021 if (ABS(MovPos[x][y]) < TILEX)
9024 int ee = Feld[x][y];
9025 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9026 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9028 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9029 x, y, ABS(MovPos[x][y]),
9031 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9034 TEST_DrawLevelField(x, y);
9036 return; /* element is still moving */
9039 /* element reached destination field */
9041 Feld[x][y] = EL_EMPTY;
9042 Feld[newx][newy] = element;
9043 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9045 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9047 element = Feld[newx][newy] = EL_ACID;
9049 else if (element == EL_MOLE)
9051 Feld[x][y] = EL_SAND;
9053 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9055 else if (element == EL_QUICKSAND_FILLING)
9057 element = Feld[newx][newy] = get_next_element(element);
9058 Store[newx][newy] = Store[x][y];
9060 else if (element == EL_QUICKSAND_EMPTYING)
9062 Feld[x][y] = get_next_element(element);
9063 element = Feld[newx][newy] = Store[x][y];
9065 else if (element == EL_QUICKSAND_FAST_FILLING)
9067 element = Feld[newx][newy] = get_next_element(element);
9068 Store[newx][newy] = Store[x][y];
9070 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9072 Feld[x][y] = get_next_element(element);
9073 element = Feld[newx][newy] = Store[x][y];
9075 else if (element == EL_MAGIC_WALL_FILLING)
9077 element = Feld[newx][newy] = get_next_element(element);
9078 if (!game.magic_wall_active)
9079 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9080 Store[newx][newy] = Store[x][y];
9082 else if (element == EL_MAGIC_WALL_EMPTYING)
9084 Feld[x][y] = get_next_element(element);
9085 if (!game.magic_wall_active)
9086 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9087 element = Feld[newx][newy] = Store[x][y];
9089 #if USE_NEW_CUSTOM_VALUE
9090 InitField(newx, newy, FALSE);
9093 else if (element == EL_BD_MAGIC_WALL_FILLING)
9095 element = Feld[newx][newy] = get_next_element(element);
9096 if (!game.magic_wall_active)
9097 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9098 Store[newx][newy] = Store[x][y];
9100 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9102 Feld[x][y] = get_next_element(element);
9103 if (!game.magic_wall_active)
9104 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9105 element = Feld[newx][newy] = Store[x][y];
9107 #if USE_NEW_CUSTOM_VALUE
9108 InitField(newx, newy, FALSE);
9111 else if (element == EL_DC_MAGIC_WALL_FILLING)
9113 element = Feld[newx][newy] = get_next_element(element);
9114 if (!game.magic_wall_active)
9115 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9116 Store[newx][newy] = Store[x][y];
9118 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9120 Feld[x][y] = get_next_element(element);
9121 if (!game.magic_wall_active)
9122 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9123 element = Feld[newx][newy] = Store[x][y];
9125 #if USE_NEW_CUSTOM_VALUE
9126 InitField(newx, newy, FALSE);
9129 else if (element == EL_AMOEBA_DROPPING)
9131 Feld[x][y] = get_next_element(element);
9132 element = Feld[newx][newy] = Store[x][y];
9134 else if (element == EL_SOKOBAN_OBJECT)
9137 Feld[x][y] = Back[x][y];
9139 if (Back[newx][newy])
9140 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9142 Back[x][y] = Back[newx][newy] = 0;
9145 Store[x][y] = EL_EMPTY;
9150 MovDelay[newx][newy] = 0;
9152 if (CAN_CHANGE_OR_HAS_ACTION(element))
9154 /* copy element change control values to new field */
9155 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9156 ChangePage[newx][newy] = ChangePage[x][y];
9157 ChangeCount[newx][newy] = ChangeCount[x][y];
9158 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9161 #if USE_NEW_CUSTOM_VALUE
9162 CustomValue[newx][newy] = CustomValue[x][y];
9165 ChangeDelay[x][y] = 0;
9166 ChangePage[x][y] = -1;
9167 ChangeCount[x][y] = 0;
9168 ChangeEvent[x][y] = -1;
9170 #if USE_NEW_CUSTOM_VALUE
9171 CustomValue[x][y] = 0;
9174 /* copy animation control values to new field */
9175 GfxFrame[newx][newy] = GfxFrame[x][y];
9176 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9177 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9178 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9180 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9182 /* some elements can leave other elements behind after moving */
9184 if (ei->move_leave_element != EL_EMPTY &&
9185 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9186 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9188 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9189 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9190 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9193 int move_leave_element = ei->move_leave_element;
9197 /* this makes it possible to leave the removed element again */
9198 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9199 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9201 /* this makes it possible to leave the removed element again */
9202 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9203 move_leave_element = stored;
9206 /* this makes it possible to leave the removed element again */
9207 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9208 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9209 move_leave_element = stored;
9212 Feld[x][y] = move_leave_element;
9214 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9215 MovDir[x][y] = direction;
9217 InitField(x, y, FALSE);
9219 if (GFX_CRUMBLED(Feld[x][y]))
9220 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9222 if (ELEM_IS_PLAYER(move_leave_element))
9223 RelocatePlayer(x, y, move_leave_element);
9226 /* do this after checking for left-behind element */
9227 ResetGfxAnimation(x, y); /* reset animation values for old field */
9229 if (!CAN_MOVE(element) ||
9230 (CAN_FALL(element) && direction == MV_DOWN &&
9231 (element == EL_SPRING ||
9232 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9233 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9234 GfxDir[x][y] = MovDir[newx][newy] = 0;
9236 TEST_DrawLevelField(x, y);
9237 TEST_DrawLevelField(newx, newy);
9239 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9241 /* prevent pushed element from moving on in pushed direction */
9242 if (pushed_by_player && CAN_MOVE(element) &&
9243 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9244 !(element_info[element].move_pattern & direction))
9245 TurnRound(newx, newy);
9247 /* prevent elements on conveyor belt from moving on in last direction */
9248 if (pushed_by_conveyor && CAN_FALL(element) &&
9249 direction & MV_HORIZONTAL)
9250 MovDir[newx][newy] = 0;
9252 if (!pushed_by_player)
9254 int nextx = newx + dx, nexty = newy + dy;
9255 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9257 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9259 if (CAN_FALL(element) && direction == MV_DOWN)
9260 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9262 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9263 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9265 #if USE_FIX_IMPACT_COLLISION
9266 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9267 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9271 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9273 TestIfBadThingTouchesPlayer(newx, newy);
9274 TestIfBadThingTouchesFriend(newx, newy);
9276 if (!IS_CUSTOM_ELEMENT(element))
9277 TestIfBadThingTouchesOtherBadThing(newx, newy);
9279 else if (element == EL_PENGUIN)
9280 TestIfFriendTouchesBadThing(newx, newy);
9282 if (DONT_GET_HIT_BY(element))
9284 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9287 /* give the player one last chance (one more frame) to move away */
9288 if (CAN_FALL(element) && direction == MV_DOWN &&
9289 (last_line || (!IS_FREE(x, newy + 1) &&
9290 (!IS_PLAYER(x, newy + 1) ||
9291 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9294 if (pushed_by_player && !game.use_change_when_pushing_bug)
9296 int push_side = MV_DIR_OPPOSITE(direction);
9297 struct PlayerInfo *player = PLAYERINFO(x, y);
9299 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9300 player->index_bit, push_side);
9301 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9302 player->index_bit, push_side);
9305 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9306 MovDelay[newx][newy] = 1;
9308 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9310 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9313 if (ChangePage[newx][newy] != -1) /* delayed change */
9315 int page = ChangePage[newx][newy];
9316 struct ElementChangeInfo *change = &ei->change_page[page];
9318 ChangePage[newx][newy] = -1;
9320 if (change->can_change)
9322 if (ChangeElement(newx, newy, element, page))
9324 if (change->post_change_function)
9325 change->post_change_function(newx, newy);
9329 if (change->has_action)
9330 ExecuteCustomElementAction(newx, newy, element, page);
9334 TestIfElementHitsCustomElement(newx, newy, direction);
9335 TestIfPlayerTouchesCustomElement(newx, newy);
9336 TestIfElementTouchesCustomElement(newx, newy);
9338 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9339 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9340 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9341 MV_DIR_OPPOSITE(direction));
9344 int AmoebeNachbarNr(int ax, int ay)
9347 int element = Feld[ax][ay];
9349 static int xy[4][2] =
9357 for (i = 0; i < NUM_DIRECTIONS; i++)
9359 int x = ax + xy[i][0];
9360 int y = ay + xy[i][1];
9362 if (!IN_LEV_FIELD(x, y))
9365 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9366 group_nr = AmoebaNr[x][y];
9372 void AmoebenVereinigen(int ax, int ay)
9374 int i, x, y, xx, yy;
9375 int new_group_nr = AmoebaNr[ax][ay];
9376 static int xy[4][2] =
9384 if (new_group_nr == 0)
9387 for (i = 0; i < NUM_DIRECTIONS; i++)
9392 if (!IN_LEV_FIELD(x, y))
9395 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9396 Feld[x][y] == EL_BD_AMOEBA ||
9397 Feld[x][y] == EL_AMOEBA_DEAD) &&
9398 AmoebaNr[x][y] != new_group_nr)
9400 int old_group_nr = AmoebaNr[x][y];
9402 if (old_group_nr == 0)
9405 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9406 AmoebaCnt[old_group_nr] = 0;
9407 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9408 AmoebaCnt2[old_group_nr] = 0;
9410 SCAN_PLAYFIELD(xx, yy)
9412 if (AmoebaNr[xx][yy] == old_group_nr)
9413 AmoebaNr[xx][yy] = new_group_nr;
9419 void AmoebeUmwandeln(int ax, int ay)
9423 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9425 int group_nr = AmoebaNr[ax][ay];
9430 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9431 printf("AmoebeUmwandeln(): This should never happen!\n");
9436 SCAN_PLAYFIELD(x, y)
9438 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9441 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9445 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9446 SND_AMOEBA_TURNING_TO_GEM :
9447 SND_AMOEBA_TURNING_TO_ROCK));
9452 static int xy[4][2] =
9460 for (i = 0; i < NUM_DIRECTIONS; i++)
9465 if (!IN_LEV_FIELD(x, y))
9468 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9470 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9471 SND_AMOEBA_TURNING_TO_GEM :
9472 SND_AMOEBA_TURNING_TO_ROCK));
9479 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9482 int group_nr = AmoebaNr[ax][ay];
9483 boolean done = FALSE;
9488 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9489 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9494 SCAN_PLAYFIELD(x, y)
9496 if (AmoebaNr[x][y] == group_nr &&
9497 (Feld[x][y] == EL_AMOEBA_DEAD ||
9498 Feld[x][y] == EL_BD_AMOEBA ||
9499 Feld[x][y] == EL_AMOEBA_GROWING))
9502 Feld[x][y] = new_element;
9503 InitField(x, y, FALSE);
9504 TEST_DrawLevelField(x, y);
9510 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9511 SND_BD_AMOEBA_TURNING_TO_ROCK :
9512 SND_BD_AMOEBA_TURNING_TO_GEM));
9515 void AmoebeWaechst(int x, int y)
9517 static unsigned int sound_delay = 0;
9518 static unsigned int sound_delay_value = 0;
9520 if (!MovDelay[x][y]) /* start new growing cycle */
9524 if (DelayReached(&sound_delay, sound_delay_value))
9526 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9527 sound_delay_value = 30;
9531 if (MovDelay[x][y]) /* wait some time before growing bigger */
9534 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9536 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9537 6 - MovDelay[x][y]);
9539 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9542 if (!MovDelay[x][y])
9544 Feld[x][y] = Store[x][y];
9546 TEST_DrawLevelField(x, y);
9551 void AmoebaDisappearing(int x, int y)
9553 static unsigned int sound_delay = 0;
9554 static unsigned int sound_delay_value = 0;
9556 if (!MovDelay[x][y]) /* start new shrinking cycle */
9560 if (DelayReached(&sound_delay, sound_delay_value))
9561 sound_delay_value = 30;
9564 if (MovDelay[x][y]) /* wait some time before shrinking */
9567 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9569 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9570 6 - MovDelay[x][y]);
9572 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9575 if (!MovDelay[x][y])
9577 Feld[x][y] = EL_EMPTY;
9578 TEST_DrawLevelField(x, y);
9580 /* don't let mole enter this field in this cycle;
9581 (give priority to objects falling to this field from above) */
9587 void AmoebeAbleger(int ax, int ay)
9590 int element = Feld[ax][ay];
9591 int graphic = el2img(element);
9592 int newax = ax, neway = ay;
9593 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9594 static int xy[4][2] =
9602 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9604 Feld[ax][ay] = EL_AMOEBA_DEAD;
9605 TEST_DrawLevelField(ax, ay);
9609 if (IS_ANIMATED(graphic))
9610 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9612 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9613 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9615 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9618 if (MovDelay[ax][ay])
9622 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9625 int x = ax + xy[start][0];
9626 int y = ay + xy[start][1];
9628 if (!IN_LEV_FIELD(x, y))
9631 if (IS_FREE(x, y) ||
9632 CAN_GROW_INTO(Feld[x][y]) ||
9633 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9634 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9640 if (newax == ax && neway == ay)
9643 else /* normal or "filled" (BD style) amoeba */
9646 boolean waiting_for_player = FALSE;
9648 for (i = 0; i < NUM_DIRECTIONS; i++)
9650 int j = (start + i) % 4;
9651 int x = ax + xy[j][0];
9652 int y = ay + xy[j][1];
9654 if (!IN_LEV_FIELD(x, y))
9657 if (IS_FREE(x, y) ||
9658 CAN_GROW_INTO(Feld[x][y]) ||
9659 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9660 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9666 else if (IS_PLAYER(x, y))
9667 waiting_for_player = TRUE;
9670 if (newax == ax && neway == ay) /* amoeba cannot grow */
9672 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9674 Feld[ax][ay] = EL_AMOEBA_DEAD;
9675 TEST_DrawLevelField(ax, ay);
9676 AmoebaCnt[AmoebaNr[ax][ay]]--;
9678 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9680 if (element == EL_AMOEBA_FULL)
9681 AmoebeUmwandeln(ax, ay);
9682 else if (element == EL_BD_AMOEBA)
9683 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9688 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9690 /* amoeba gets larger by growing in some direction */
9692 int new_group_nr = AmoebaNr[ax][ay];
9695 if (new_group_nr == 0)
9697 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9698 printf("AmoebeAbleger(): This should never happen!\n");
9703 AmoebaNr[newax][neway] = new_group_nr;
9704 AmoebaCnt[new_group_nr]++;
9705 AmoebaCnt2[new_group_nr]++;
9707 /* if amoeba touches other amoeba(s) after growing, unify them */
9708 AmoebenVereinigen(newax, neway);
9710 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9712 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9718 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9719 (neway == lev_fieldy - 1 && newax != ax))
9721 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9722 Store[newax][neway] = element;
9724 else if (neway == ay || element == EL_EMC_DRIPPER)
9726 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9728 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9732 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9733 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9734 Store[ax][ay] = EL_AMOEBA_DROP;
9735 ContinueMoving(ax, ay);
9739 TEST_DrawLevelField(newax, neway);
9742 void Life(int ax, int ay)
9746 int element = Feld[ax][ay];
9747 int graphic = el2img(element);
9748 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9750 boolean changed = FALSE;
9752 if (IS_ANIMATED(graphic))
9753 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9758 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9759 MovDelay[ax][ay] = life_time;
9761 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9764 if (MovDelay[ax][ay])
9768 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9770 int xx = ax+x1, yy = ay+y1;
9773 if (!IN_LEV_FIELD(xx, yy))
9776 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9778 int x = xx+x2, y = yy+y2;
9780 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9783 if (((Feld[x][y] == element ||
9784 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9786 (IS_FREE(x, y) && Stop[x][y]))
9790 if (xx == ax && yy == ay) /* field in the middle */
9792 if (nachbarn < life_parameter[0] ||
9793 nachbarn > life_parameter[1])
9795 Feld[xx][yy] = EL_EMPTY;
9797 TEST_DrawLevelField(xx, yy);
9798 Stop[xx][yy] = TRUE;
9802 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9803 { /* free border field */
9804 if (nachbarn >= life_parameter[2] &&
9805 nachbarn <= life_parameter[3])
9807 Feld[xx][yy] = element;
9808 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9810 TEST_DrawLevelField(xx, yy);
9811 Stop[xx][yy] = TRUE;
9818 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9819 SND_GAME_OF_LIFE_GROWING);
9822 static void InitRobotWheel(int x, int y)
9824 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9827 static void RunRobotWheel(int x, int y)
9829 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9832 static void StopRobotWheel(int x, int y)
9834 if (ZX == x && ZY == y)
9838 game.robot_wheel_active = FALSE;
9842 static void InitTimegateWheel(int x, int y)
9844 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9847 static void RunTimegateWheel(int x, int y)
9849 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9852 static void InitMagicBallDelay(int x, int y)
9855 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9857 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9861 static void ActivateMagicBall(int bx, int by)
9865 if (level.ball_random)
9867 int pos_border = RND(8); /* select one of the eight border elements */
9868 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9869 int xx = pos_content % 3;
9870 int yy = pos_content / 3;
9875 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9876 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9880 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9882 int xx = x - bx + 1;
9883 int yy = y - by + 1;
9885 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9886 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9890 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9893 void CheckExit(int x, int y)
9895 if (local_player->gems_still_needed > 0 ||
9896 local_player->sokobanfields_still_needed > 0 ||
9897 local_player->lights_still_needed > 0)
9899 int element = Feld[x][y];
9900 int graphic = el2img(element);
9902 if (IS_ANIMATED(graphic))
9903 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9908 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9911 Feld[x][y] = EL_EXIT_OPENING;
9913 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9916 void CheckExitEM(int x, int y)
9918 if (local_player->gems_still_needed > 0 ||
9919 local_player->sokobanfields_still_needed > 0 ||
9920 local_player->lights_still_needed > 0)
9922 int element = Feld[x][y];
9923 int graphic = el2img(element);
9925 if (IS_ANIMATED(graphic))
9926 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9931 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9934 Feld[x][y] = EL_EM_EXIT_OPENING;
9936 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9939 void CheckExitSteel(int x, int y)
9941 if (local_player->gems_still_needed > 0 ||
9942 local_player->sokobanfields_still_needed > 0 ||
9943 local_player->lights_still_needed > 0)
9945 int element = Feld[x][y];
9946 int graphic = el2img(element);
9948 if (IS_ANIMATED(graphic))
9949 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9954 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9957 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9959 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9962 void CheckExitSteelEM(int x, int y)
9964 if (local_player->gems_still_needed > 0 ||
9965 local_player->sokobanfields_still_needed > 0 ||
9966 local_player->lights_still_needed > 0)
9968 int element = Feld[x][y];
9969 int graphic = el2img(element);
9971 if (IS_ANIMATED(graphic))
9972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9977 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9980 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9982 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9985 void CheckExitSP(int x, int y)
9987 if (local_player->gems_still_needed > 0)
9989 int element = Feld[x][y];
9990 int graphic = el2img(element);
9992 if (IS_ANIMATED(graphic))
9993 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9998 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10001 Feld[x][y] = EL_SP_EXIT_OPENING;
10003 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10006 static void CloseAllOpenTimegates()
10010 SCAN_PLAYFIELD(x, y)
10012 int element = Feld[x][y];
10014 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10016 Feld[x][y] = EL_TIMEGATE_CLOSING;
10018 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10023 void DrawTwinkleOnField(int x, int y)
10025 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10028 if (Feld[x][y] == EL_BD_DIAMOND)
10031 if (MovDelay[x][y] == 0) /* next animation frame */
10032 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10034 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10038 DrawLevelElementAnimation(x, y, Feld[x][y]);
10040 if (MovDelay[x][y] != 0)
10042 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10043 10 - MovDelay[x][y]);
10045 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10050 void MauerWaechst(int x, int y)
10054 if (!MovDelay[x][y]) /* next animation frame */
10055 MovDelay[x][y] = 3 * delay;
10057 if (MovDelay[x][y]) /* wait some time before next frame */
10061 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10063 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10064 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10066 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10069 if (!MovDelay[x][y])
10071 if (MovDir[x][y] == MV_LEFT)
10073 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10074 TEST_DrawLevelField(x - 1, y);
10076 else if (MovDir[x][y] == MV_RIGHT)
10078 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10079 TEST_DrawLevelField(x + 1, y);
10081 else if (MovDir[x][y] == MV_UP)
10083 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10084 TEST_DrawLevelField(x, y - 1);
10088 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10089 TEST_DrawLevelField(x, y + 1);
10092 Feld[x][y] = Store[x][y];
10094 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10095 TEST_DrawLevelField(x, y);
10100 void MauerAbleger(int ax, int ay)
10102 int element = Feld[ax][ay];
10103 int graphic = el2img(element);
10104 boolean oben_frei = FALSE, unten_frei = FALSE;
10105 boolean links_frei = FALSE, rechts_frei = FALSE;
10106 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10107 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10108 boolean new_wall = FALSE;
10110 if (IS_ANIMATED(graphic))
10111 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10113 if (!MovDelay[ax][ay]) /* start building new wall */
10114 MovDelay[ax][ay] = 6;
10116 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10118 MovDelay[ax][ay]--;
10119 if (MovDelay[ax][ay])
10123 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10125 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10127 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10129 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10130 rechts_frei = TRUE;
10132 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10133 element == EL_EXPANDABLE_WALL_ANY)
10137 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10138 Store[ax][ay-1] = element;
10139 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10140 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10141 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10142 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10147 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10148 Store[ax][ay+1] = element;
10149 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10150 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10151 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10152 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10157 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10158 element == EL_EXPANDABLE_WALL_ANY ||
10159 element == EL_EXPANDABLE_WALL ||
10160 element == EL_BD_EXPANDABLE_WALL)
10164 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10165 Store[ax-1][ay] = element;
10166 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10167 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10168 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10169 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10175 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10176 Store[ax+1][ay] = element;
10177 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10178 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10179 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10180 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10185 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10186 TEST_DrawLevelField(ax, ay);
10188 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10189 oben_massiv = TRUE;
10190 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10191 unten_massiv = TRUE;
10192 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10193 links_massiv = TRUE;
10194 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10195 rechts_massiv = TRUE;
10197 if (((oben_massiv && unten_massiv) ||
10198 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10199 element == EL_EXPANDABLE_WALL) &&
10200 ((links_massiv && rechts_massiv) ||
10201 element == EL_EXPANDABLE_WALL_VERTICAL))
10202 Feld[ax][ay] = EL_WALL;
10205 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10208 void MauerAblegerStahl(int ax, int ay)
10210 int element = Feld[ax][ay];
10211 int graphic = el2img(element);
10212 boolean oben_frei = FALSE, unten_frei = FALSE;
10213 boolean links_frei = FALSE, rechts_frei = FALSE;
10214 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10215 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10216 boolean new_wall = FALSE;
10218 if (IS_ANIMATED(graphic))
10219 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10221 if (!MovDelay[ax][ay]) /* start building new wall */
10222 MovDelay[ax][ay] = 6;
10224 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10226 MovDelay[ax][ay]--;
10227 if (MovDelay[ax][ay])
10231 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10233 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10235 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10237 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10238 rechts_frei = TRUE;
10240 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10241 element == EL_EXPANDABLE_STEELWALL_ANY)
10245 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10246 Store[ax][ay-1] = element;
10247 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10248 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10249 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10250 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10255 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10256 Store[ax][ay+1] = element;
10257 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10258 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10259 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10260 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10265 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10266 element == EL_EXPANDABLE_STEELWALL_ANY)
10270 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10271 Store[ax-1][ay] = element;
10272 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10273 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10274 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10275 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10281 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10282 Store[ax+1][ay] = element;
10283 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10284 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10285 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10286 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10291 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10292 oben_massiv = TRUE;
10293 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10294 unten_massiv = TRUE;
10295 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10296 links_massiv = TRUE;
10297 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10298 rechts_massiv = TRUE;
10300 if (((oben_massiv && unten_massiv) ||
10301 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10302 ((links_massiv && rechts_massiv) ||
10303 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10304 Feld[ax][ay] = EL_STEELWALL;
10307 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10310 void CheckForDragon(int x, int y)
10313 boolean dragon_found = FALSE;
10314 static int xy[4][2] =
10322 for (i = 0; i < NUM_DIRECTIONS; i++)
10324 for (j = 0; j < 4; j++)
10326 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10328 if (IN_LEV_FIELD(xx, yy) &&
10329 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10331 if (Feld[xx][yy] == EL_DRAGON)
10332 dragon_found = TRUE;
10341 for (i = 0; i < NUM_DIRECTIONS; i++)
10343 for (j = 0; j < 3; j++)
10345 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10347 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10349 Feld[xx][yy] = EL_EMPTY;
10350 TEST_DrawLevelField(xx, yy);
10359 static void InitBuggyBase(int x, int y)
10361 int element = Feld[x][y];
10362 int activating_delay = FRAMES_PER_SECOND / 4;
10364 ChangeDelay[x][y] =
10365 (element == EL_SP_BUGGY_BASE ?
10366 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10367 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10369 element == EL_SP_BUGGY_BASE_ACTIVE ?
10370 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10373 static void WarnBuggyBase(int x, int y)
10376 static int xy[4][2] =
10384 for (i = 0; i < NUM_DIRECTIONS; i++)
10386 int xx = x + xy[i][0];
10387 int yy = y + xy[i][1];
10389 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10391 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10398 static void InitTrap(int x, int y)
10400 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10403 static void ActivateTrap(int x, int y)
10405 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10408 static void ChangeActiveTrap(int x, int y)
10410 int graphic = IMG_TRAP_ACTIVE;
10412 /* if new animation frame was drawn, correct crumbled sand border */
10413 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10414 TEST_DrawLevelFieldCrumbled(x, y);
10417 static int getSpecialActionElement(int element, int number, int base_element)
10419 return (element != EL_EMPTY ? element :
10420 number != -1 ? base_element + number - 1 :
10424 static int getModifiedActionNumber(int value_old, int operator, int operand,
10425 int value_min, int value_max)
10427 int value_new = (operator == CA_MODE_SET ? operand :
10428 operator == CA_MODE_ADD ? value_old + operand :
10429 operator == CA_MODE_SUBTRACT ? value_old - operand :
10430 operator == CA_MODE_MULTIPLY ? value_old * operand :
10431 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10432 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10435 return (value_new < value_min ? value_min :
10436 value_new > value_max ? value_max :
10440 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10442 struct ElementInfo *ei = &element_info[element];
10443 struct ElementChangeInfo *change = &ei->change_page[page];
10444 int target_element = change->target_element;
10445 int action_type = change->action_type;
10446 int action_mode = change->action_mode;
10447 int action_arg = change->action_arg;
10448 int action_element = change->action_element;
10451 if (!change->has_action)
10454 /* ---------- determine action paramater values -------------------------- */
10456 int level_time_value =
10457 (level.time > 0 ? TimeLeft :
10460 int action_arg_element_raw =
10461 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10462 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10463 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10464 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10465 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10466 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10467 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10469 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10472 if (action_arg_element_raw == EL_GROUP_START)
10473 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10476 int action_arg_direction =
10477 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10478 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10479 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10480 change->actual_trigger_side :
10481 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10482 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10485 int action_arg_number_min =
10486 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10489 int action_arg_number_max =
10490 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10491 action_type == CA_SET_LEVEL_GEMS ? 999 :
10492 action_type == CA_SET_LEVEL_TIME ? 9999 :
10493 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10494 action_type == CA_SET_CE_VALUE ? 9999 :
10495 action_type == CA_SET_CE_SCORE ? 9999 :
10498 int action_arg_number_reset =
10499 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10500 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10501 action_type == CA_SET_LEVEL_TIME ? level.time :
10502 action_type == CA_SET_LEVEL_SCORE ? 0 :
10503 #if USE_NEW_CUSTOM_VALUE
10504 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10506 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10508 action_type == CA_SET_CE_SCORE ? 0 :
10511 int action_arg_number =
10512 (action_arg <= CA_ARG_MAX ? action_arg :
10513 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10514 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10515 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10516 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10517 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10518 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10519 #if USE_NEW_CUSTOM_VALUE
10520 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10522 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10524 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10525 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10526 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10527 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10528 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10529 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10530 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10531 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10532 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10533 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10534 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10535 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10536 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10537 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10540 int action_arg_number_old =
10541 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10542 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10543 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10544 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10545 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10548 int action_arg_number_new =
10549 getModifiedActionNumber(action_arg_number_old,
10550 action_mode, action_arg_number,
10551 action_arg_number_min, action_arg_number_max);
10554 int trigger_player_bits =
10555 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10556 change->actual_trigger_player_bits : change->trigger_player);
10558 int trigger_player_bits =
10559 (change->actual_trigger_player >= EL_PLAYER_1 &&
10560 change->actual_trigger_player <= EL_PLAYER_4 ?
10561 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10565 int action_arg_player_bits =
10566 (action_arg >= CA_ARG_PLAYER_1 &&
10567 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10568 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10569 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10572 /* ---------- execute action -------------------------------------------- */
10574 switch (action_type)
10581 /* ---------- level actions ------------------------------------------- */
10583 case CA_RESTART_LEVEL:
10585 game.restart_level = TRUE;
10590 case CA_SHOW_ENVELOPE:
10592 int element = getSpecialActionElement(action_arg_element,
10593 action_arg_number, EL_ENVELOPE_1);
10595 if (IS_ENVELOPE(element))
10596 local_player->show_envelope = element;
10601 case CA_SET_LEVEL_TIME:
10603 if (level.time > 0) /* only modify limited time value */
10605 TimeLeft = action_arg_number_new;
10608 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10610 DisplayGameControlValues();
10612 DrawGameValue_Time(TimeLeft);
10615 if (!TimeLeft && setup.time_limit)
10616 for (i = 0; i < MAX_PLAYERS; i++)
10617 KillPlayer(&stored_player[i]);
10623 case CA_SET_LEVEL_SCORE:
10625 local_player->score = action_arg_number_new;
10628 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10630 DisplayGameControlValues();
10632 DrawGameValue_Score(local_player->score);
10638 case CA_SET_LEVEL_GEMS:
10640 local_player->gems_still_needed = action_arg_number_new;
10643 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10645 DisplayGameControlValues();
10647 DrawGameValue_Emeralds(local_player->gems_still_needed);
10653 #if !USE_PLAYER_GRAVITY
10654 case CA_SET_LEVEL_GRAVITY:
10656 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10657 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10658 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10664 case CA_SET_LEVEL_WIND:
10666 game.wind_direction = action_arg_direction;
10671 case CA_SET_LEVEL_RANDOM_SEED:
10674 /* ensure that setting a new random seed while playing is predictable */
10675 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10677 InitRND(action_arg_number_new);
10681 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10689 for (i = 0; i < 9; i++)
10690 printf("%d, ", RND(2));
10698 /* ---------- player actions ------------------------------------------ */
10700 case CA_MOVE_PLAYER:
10702 /* automatically move to the next field in specified direction */
10703 for (i = 0; i < MAX_PLAYERS; i++)
10704 if (trigger_player_bits & (1 << i))
10705 stored_player[i].programmed_action = action_arg_direction;
10710 case CA_EXIT_PLAYER:
10712 for (i = 0; i < MAX_PLAYERS; i++)
10713 if (action_arg_player_bits & (1 << i))
10714 PlayerWins(&stored_player[i]);
10719 case CA_KILL_PLAYER:
10721 for (i = 0; i < MAX_PLAYERS; i++)
10722 if (action_arg_player_bits & (1 << i))
10723 KillPlayer(&stored_player[i]);
10728 case CA_SET_PLAYER_KEYS:
10730 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10731 int element = getSpecialActionElement(action_arg_element,
10732 action_arg_number, EL_KEY_1);
10734 if (IS_KEY(element))
10736 for (i = 0; i < MAX_PLAYERS; i++)
10738 if (trigger_player_bits & (1 << i))
10740 stored_player[i].key[KEY_NR(element)] = key_state;
10742 DrawGameDoorValues();
10750 case CA_SET_PLAYER_SPEED:
10753 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10756 for (i = 0; i < MAX_PLAYERS; i++)
10758 if (trigger_player_bits & (1 << i))
10760 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10762 if (action_arg == CA_ARG_SPEED_FASTER &&
10763 stored_player[i].cannot_move)
10765 action_arg_number = STEPSIZE_VERY_SLOW;
10767 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10768 action_arg == CA_ARG_SPEED_FASTER)
10770 action_arg_number = 2;
10771 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10774 else if (action_arg == CA_ARG_NUMBER_RESET)
10776 action_arg_number = level.initial_player_stepsize[i];
10780 getModifiedActionNumber(move_stepsize,
10783 action_arg_number_min,
10784 action_arg_number_max);
10786 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10793 case CA_SET_PLAYER_SHIELD:
10795 for (i = 0; i < MAX_PLAYERS; i++)
10797 if (trigger_player_bits & (1 << i))
10799 if (action_arg == CA_ARG_SHIELD_OFF)
10801 stored_player[i].shield_normal_time_left = 0;
10802 stored_player[i].shield_deadly_time_left = 0;
10804 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10806 stored_player[i].shield_normal_time_left = 999999;
10808 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10810 stored_player[i].shield_normal_time_left = 999999;
10811 stored_player[i].shield_deadly_time_left = 999999;
10819 #if USE_PLAYER_GRAVITY
10820 case CA_SET_PLAYER_GRAVITY:
10822 for (i = 0; i < MAX_PLAYERS; i++)
10824 if (trigger_player_bits & (1 << i))
10826 stored_player[i].gravity =
10827 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10828 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10829 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10830 stored_player[i].gravity);
10838 case CA_SET_PLAYER_ARTWORK:
10840 for (i = 0; i < MAX_PLAYERS; i++)
10842 if (trigger_player_bits & (1 << i))
10844 int artwork_element = action_arg_element;
10846 if (action_arg == CA_ARG_ELEMENT_RESET)
10848 (level.use_artwork_element[i] ? level.artwork_element[i] :
10849 stored_player[i].element_nr);
10851 #if USE_GFX_RESET_PLAYER_ARTWORK
10852 if (stored_player[i].artwork_element != artwork_element)
10853 stored_player[i].Frame = 0;
10856 stored_player[i].artwork_element = artwork_element;
10858 SetPlayerWaiting(&stored_player[i], FALSE);
10860 /* set number of special actions for bored and sleeping animation */
10861 stored_player[i].num_special_action_bored =
10862 get_num_special_action(artwork_element,
10863 ACTION_BORING_1, ACTION_BORING_LAST);
10864 stored_player[i].num_special_action_sleeping =
10865 get_num_special_action(artwork_element,
10866 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10873 case CA_SET_PLAYER_INVENTORY:
10875 for (i = 0; i < MAX_PLAYERS; i++)
10877 struct PlayerInfo *player = &stored_player[i];
10880 if (trigger_player_bits & (1 << i))
10882 int inventory_element = action_arg_element;
10884 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10885 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10886 action_arg == CA_ARG_ELEMENT_ACTION)
10888 int element = inventory_element;
10889 int collect_count = element_info[element].collect_count_initial;
10891 if (!IS_CUSTOM_ELEMENT(element))
10894 if (collect_count == 0)
10895 player->inventory_infinite_element = element;
10897 for (k = 0; k < collect_count; k++)
10898 if (player->inventory_size < MAX_INVENTORY_SIZE)
10899 player->inventory_element[player->inventory_size++] =
10902 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10903 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10904 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10906 if (player->inventory_infinite_element != EL_UNDEFINED &&
10907 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10908 action_arg_element_raw))
10909 player->inventory_infinite_element = EL_UNDEFINED;
10911 for (k = 0, j = 0; j < player->inventory_size; j++)
10913 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10914 action_arg_element_raw))
10915 player->inventory_element[k++] = player->inventory_element[j];
10918 player->inventory_size = k;
10920 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10922 if (player->inventory_size > 0)
10924 for (j = 0; j < player->inventory_size - 1; j++)
10925 player->inventory_element[j] = player->inventory_element[j + 1];
10927 player->inventory_size--;
10930 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10932 if (player->inventory_size > 0)
10933 player->inventory_size--;
10935 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10937 player->inventory_infinite_element = EL_UNDEFINED;
10938 player->inventory_size = 0;
10940 else if (action_arg == CA_ARG_INVENTORY_RESET)
10942 player->inventory_infinite_element = EL_UNDEFINED;
10943 player->inventory_size = 0;
10945 if (level.use_initial_inventory[i])
10947 for (j = 0; j < level.initial_inventory_size[i]; j++)
10949 int element = level.initial_inventory_content[i][j];
10950 int collect_count = element_info[element].collect_count_initial;
10952 if (!IS_CUSTOM_ELEMENT(element))
10955 if (collect_count == 0)
10956 player->inventory_infinite_element = element;
10958 for (k = 0; k < collect_count; k++)
10959 if (player->inventory_size < MAX_INVENTORY_SIZE)
10960 player->inventory_element[player->inventory_size++] =
10971 /* ---------- CE actions ---------------------------------------------- */
10973 case CA_SET_CE_VALUE:
10975 #if USE_NEW_CUSTOM_VALUE
10976 int last_ce_value = CustomValue[x][y];
10978 CustomValue[x][y] = action_arg_number_new;
10980 if (CustomValue[x][y] != last_ce_value)
10982 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10983 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10985 if (CustomValue[x][y] == 0)
10987 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10988 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10996 case CA_SET_CE_SCORE:
10998 #if USE_NEW_CUSTOM_VALUE
10999 int last_ce_score = ei->collect_score;
11001 ei->collect_score = action_arg_number_new;
11003 if (ei->collect_score != last_ce_score)
11005 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11006 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11008 if (ei->collect_score == 0)
11012 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11013 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11016 This is a very special case that seems to be a mixture between
11017 CheckElementChange() and CheckTriggeredElementChange(): while
11018 the first one only affects single elements that are triggered
11019 directly, the second one affects multiple elements in the playfield
11020 that are triggered indirectly by another element. This is a third
11021 case: Changing the CE score always affects multiple identical CEs,
11022 so every affected CE must be checked, not only the single CE for
11023 which the CE score was changed in the first place (as every instance
11024 of that CE shares the same CE score, and therefore also can change)!
11026 SCAN_PLAYFIELD(xx, yy)
11028 if (Feld[xx][yy] == element)
11029 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11030 CE_SCORE_GETS_ZERO);
11039 case CA_SET_CE_ARTWORK:
11041 int artwork_element = action_arg_element;
11042 boolean reset_frame = FALSE;
11045 if (action_arg == CA_ARG_ELEMENT_RESET)
11046 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11049 if (ei->gfx_element != artwork_element)
11050 reset_frame = TRUE;
11052 ei->gfx_element = artwork_element;
11054 SCAN_PLAYFIELD(xx, yy)
11056 if (Feld[xx][yy] == element)
11060 ResetGfxAnimation(xx, yy);
11061 ResetRandomAnimationValue(xx, yy);
11064 TEST_DrawLevelField(xx, yy);
11071 /* ---------- engine actions ------------------------------------------ */
11073 case CA_SET_ENGINE_SCAN_MODE:
11075 InitPlayfieldScanMode(action_arg);
11085 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11087 int old_element = Feld[x][y];
11088 int new_element = GetElementFromGroupElement(element);
11089 int previous_move_direction = MovDir[x][y];
11090 #if USE_NEW_CUSTOM_VALUE
11091 int last_ce_value = CustomValue[x][y];
11093 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11094 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11095 boolean add_player_onto_element = (new_element_is_player &&
11096 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11097 /* this breaks SnakeBite when a snake is
11098 halfway through a door that closes */
11099 /* NOW FIXED AT LEVEL INIT IN files.c */
11100 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11102 IS_WALKABLE(old_element));
11105 /* check if element under the player changes from accessible to unaccessible
11106 (needed for special case of dropping element which then changes) */
11107 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11108 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11116 if (!add_player_onto_element)
11118 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11119 RemoveMovingField(x, y);
11123 Feld[x][y] = new_element;
11125 #if !USE_GFX_RESET_GFX_ANIMATION
11126 ResetGfxAnimation(x, y);
11127 ResetRandomAnimationValue(x, y);
11130 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11131 MovDir[x][y] = previous_move_direction;
11133 #if USE_NEW_CUSTOM_VALUE
11134 if (element_info[new_element].use_last_ce_value)
11135 CustomValue[x][y] = last_ce_value;
11138 InitField_WithBug1(x, y, FALSE);
11140 new_element = Feld[x][y]; /* element may have changed */
11142 #if USE_GFX_RESET_GFX_ANIMATION
11143 ResetGfxAnimation(x, y);
11144 ResetRandomAnimationValue(x, y);
11147 TEST_DrawLevelField(x, y);
11149 if (GFX_CRUMBLED(new_element))
11150 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11154 /* check if element under the player changes from accessible to unaccessible
11155 (needed for special case of dropping element which then changes) */
11156 /* (must be checked after creating new element for walkable group elements) */
11157 #if USE_FIX_KILLED_BY_NON_WALKABLE
11158 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11159 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11166 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11167 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11176 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11177 if (new_element_is_player)
11178 RelocatePlayer(x, y, new_element);
11181 ChangeCount[x][y]++; /* count number of changes in the same frame */
11183 TestIfBadThingTouchesPlayer(x, y);
11184 TestIfPlayerTouchesCustomElement(x, y);
11185 TestIfElementTouchesCustomElement(x, y);
11188 static void CreateField(int x, int y, int element)
11190 CreateFieldExt(x, y, element, FALSE);
11193 static void CreateElementFromChange(int x, int y, int element)
11195 element = GET_VALID_RUNTIME_ELEMENT(element);
11197 #if USE_STOP_CHANGED_ELEMENTS
11198 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11200 int old_element = Feld[x][y];
11202 /* prevent changed element from moving in same engine frame
11203 unless both old and new element can either fall or move */
11204 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11205 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11210 CreateFieldExt(x, y, element, TRUE);
11213 static boolean ChangeElement(int x, int y, int element, int page)
11215 struct ElementInfo *ei = &element_info[element];
11216 struct ElementChangeInfo *change = &ei->change_page[page];
11217 int ce_value = CustomValue[x][y];
11218 int ce_score = ei->collect_score;
11219 int target_element;
11220 int old_element = Feld[x][y];
11222 /* always use default change event to prevent running into a loop */
11223 if (ChangeEvent[x][y] == -1)
11224 ChangeEvent[x][y] = CE_DELAY;
11226 if (ChangeEvent[x][y] == CE_DELAY)
11228 /* reset actual trigger element, trigger player and action element */
11229 change->actual_trigger_element = EL_EMPTY;
11230 change->actual_trigger_player = EL_EMPTY;
11231 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11232 change->actual_trigger_side = CH_SIDE_NONE;
11233 change->actual_trigger_ce_value = 0;
11234 change->actual_trigger_ce_score = 0;
11237 /* do not change elements more than a specified maximum number of changes */
11238 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11241 ChangeCount[x][y]++; /* count number of changes in the same frame */
11243 if (change->explode)
11250 if (change->use_target_content)
11252 boolean complete_replace = TRUE;
11253 boolean can_replace[3][3];
11256 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11259 boolean is_walkable;
11260 boolean is_diggable;
11261 boolean is_collectible;
11262 boolean is_removable;
11263 boolean is_destructible;
11264 int ex = x + xx - 1;
11265 int ey = y + yy - 1;
11266 int content_element = change->target_content.e[xx][yy];
11269 can_replace[xx][yy] = TRUE;
11271 if (ex == x && ey == y) /* do not check changing element itself */
11274 if (content_element == EL_EMPTY_SPACE)
11276 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11281 if (!IN_LEV_FIELD(ex, ey))
11283 can_replace[xx][yy] = FALSE;
11284 complete_replace = FALSE;
11291 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11292 e = MovingOrBlocked2Element(ex, ey);
11294 is_empty = (IS_FREE(ex, ey) ||
11295 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11297 is_walkable = (is_empty || IS_WALKABLE(e));
11298 is_diggable = (is_empty || IS_DIGGABLE(e));
11299 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11300 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11301 is_removable = (is_diggable || is_collectible);
11303 can_replace[xx][yy] =
11304 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11305 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11306 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11307 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11308 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11309 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11310 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11312 if (!can_replace[xx][yy])
11313 complete_replace = FALSE;
11316 if (!change->only_if_complete || complete_replace)
11318 boolean something_has_changed = FALSE;
11320 if (change->only_if_complete && change->use_random_replace &&
11321 RND(100) < change->random_percentage)
11324 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11326 int ex = x + xx - 1;
11327 int ey = y + yy - 1;
11328 int content_element;
11330 if (can_replace[xx][yy] && (!change->use_random_replace ||
11331 RND(100) < change->random_percentage))
11333 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11334 RemoveMovingField(ex, ey);
11336 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11338 content_element = change->target_content.e[xx][yy];
11339 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11340 ce_value, ce_score);
11342 CreateElementFromChange(ex, ey, target_element);
11344 something_has_changed = TRUE;
11346 /* for symmetry reasons, freeze newly created border elements */
11347 if (ex != x || ey != y)
11348 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11352 if (something_has_changed)
11354 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11355 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11361 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11362 ce_value, ce_score);
11364 if (element == EL_DIAGONAL_GROWING ||
11365 element == EL_DIAGONAL_SHRINKING)
11367 target_element = Store[x][y];
11369 Store[x][y] = EL_EMPTY;
11372 CreateElementFromChange(x, y, target_element);
11374 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11375 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11378 /* this uses direct change before indirect change */
11379 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11384 #if USE_NEW_DELAYED_ACTION
11386 static void HandleElementChange(int x, int y, int page)
11388 int element = MovingOrBlocked2Element(x, y);
11389 struct ElementInfo *ei = &element_info[element];
11390 struct ElementChangeInfo *change = &ei->change_page[page];
11391 boolean handle_action_before_change = FALSE;
11394 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11395 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11398 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11399 x, y, element, element_info[element].token_name);
11400 printf("HandleElementChange(): This should never happen!\n");
11405 /* this can happen with classic bombs on walkable, changing elements */
11406 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11409 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11410 ChangeDelay[x][y] = 0;
11416 if (ChangeDelay[x][y] == 0) /* initialize element change */
11418 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11420 if (change->can_change)
11423 /* !!! not clear why graphic animation should be reset at all here !!! */
11424 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11425 #if USE_GFX_RESET_WHEN_NOT_MOVING
11426 /* when a custom element is about to change (for example by change delay),
11427 do not reset graphic animation when the custom element is moving */
11428 if (!IS_MOVING(x, y))
11431 ResetGfxAnimation(x, y);
11432 ResetRandomAnimationValue(x, y);
11436 if (change->pre_change_function)
11437 change->pre_change_function(x, y);
11441 ChangeDelay[x][y]--;
11443 if (ChangeDelay[x][y] != 0) /* continue element change */
11445 if (change->can_change)
11447 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11449 if (IS_ANIMATED(graphic))
11450 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11452 if (change->change_function)
11453 change->change_function(x, y);
11456 else /* finish element change */
11458 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11460 page = ChangePage[x][y];
11461 ChangePage[x][y] = -1;
11463 change = &ei->change_page[page];
11466 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11468 ChangeDelay[x][y] = 1; /* try change after next move step */
11469 ChangePage[x][y] = page; /* remember page to use for change */
11475 /* special case: set new level random seed before changing element */
11476 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11477 handle_action_before_change = TRUE;
11479 if (change->has_action && handle_action_before_change)
11480 ExecuteCustomElementAction(x, y, element, page);
11483 if (change->can_change)
11485 if (ChangeElement(x, y, element, page))
11487 if (change->post_change_function)
11488 change->post_change_function(x, y);
11492 if (change->has_action && !handle_action_before_change)
11493 ExecuteCustomElementAction(x, y, element, page);
11499 static void HandleElementChange(int x, int y, int page)
11501 int element = MovingOrBlocked2Element(x, y);
11502 struct ElementInfo *ei = &element_info[element];
11503 struct ElementChangeInfo *change = &ei->change_page[page];
11506 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11509 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11510 x, y, element, element_info[element].token_name);
11511 printf("HandleElementChange(): This should never happen!\n");
11516 /* this can happen with classic bombs on walkable, changing elements */
11517 if (!CAN_CHANGE(element))
11520 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11521 ChangeDelay[x][y] = 0;
11527 if (ChangeDelay[x][y] == 0) /* initialize element change */
11529 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11531 ResetGfxAnimation(x, y);
11532 ResetRandomAnimationValue(x, y);
11534 if (change->pre_change_function)
11535 change->pre_change_function(x, y);
11538 ChangeDelay[x][y]--;
11540 if (ChangeDelay[x][y] != 0) /* continue element change */
11542 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11544 if (IS_ANIMATED(graphic))
11545 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11547 if (change->change_function)
11548 change->change_function(x, y);
11550 else /* finish element change */
11552 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11554 page = ChangePage[x][y];
11555 ChangePage[x][y] = -1;
11557 change = &ei->change_page[page];
11560 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11562 ChangeDelay[x][y] = 1; /* try change after next move step */
11563 ChangePage[x][y] = page; /* remember page to use for change */
11568 if (ChangeElement(x, y, element, page))
11570 if (change->post_change_function)
11571 change->post_change_function(x, y);
11578 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11579 int trigger_element,
11581 int trigger_player,
11585 boolean change_done_any = FALSE;
11586 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11589 if (!(trigger_events[trigger_element][trigger_event]))
11593 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11594 trigger_event, recursion_loop_depth, recursion_loop_detected,
11595 recursion_loop_element, EL_NAME(recursion_loop_element));
11598 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11600 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11602 int element = EL_CUSTOM_START + i;
11603 boolean change_done = FALSE;
11606 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11607 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11610 for (p = 0; p < element_info[element].num_change_pages; p++)
11612 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11614 if (change->can_change_or_has_action &&
11615 change->has_event[trigger_event] &&
11616 change->trigger_side & trigger_side &&
11617 change->trigger_player & trigger_player &&
11618 change->trigger_page & trigger_page_bits &&
11619 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11621 change->actual_trigger_element = trigger_element;
11622 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11623 change->actual_trigger_player_bits = trigger_player;
11624 change->actual_trigger_side = trigger_side;
11625 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11626 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11629 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11630 element, EL_NAME(element), p);
11633 if ((change->can_change && !change_done) || change->has_action)
11637 SCAN_PLAYFIELD(x, y)
11639 if (Feld[x][y] == element)
11641 if (change->can_change && !change_done)
11643 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11644 /* if element already changed in this frame, not only prevent
11645 another element change (checked in ChangeElement()), but
11646 also prevent additional element actions for this element */
11648 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11649 !level.use_action_after_change_bug)
11654 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11655 element, EL_NAME(element), p);
11658 ChangeDelay[x][y] = 1;
11659 ChangeEvent[x][y] = trigger_event;
11661 HandleElementChange(x, y, p);
11663 #if USE_NEW_DELAYED_ACTION
11664 else if (change->has_action)
11666 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11667 /* if element already changed in this frame, not only prevent
11668 another element change (checked in ChangeElement()), but
11669 also prevent additional element actions for this element */
11671 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11672 !level.use_action_after_change_bug)
11678 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11679 element, EL_NAME(element), p);
11682 ExecuteCustomElementAction(x, y, element, p);
11683 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11686 if (change->has_action)
11688 ExecuteCustomElementAction(x, y, element, p);
11689 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11695 if (change->can_change)
11697 change_done = TRUE;
11698 change_done_any = TRUE;
11701 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11702 element, EL_NAME(element), p);
11711 RECURSION_LOOP_DETECTION_END();
11713 return change_done_any;
11716 static boolean CheckElementChangeExt(int x, int y,
11718 int trigger_element,
11720 int trigger_player,
11723 boolean change_done = FALSE;
11726 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11727 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11730 if (Feld[x][y] == EL_BLOCKED)
11732 Blocked2Moving(x, y, &x, &y);
11733 element = Feld[x][y];
11737 /* check if element has already changed */
11738 if (Feld[x][y] != element)
11741 /* check if element has already changed or is about to change after moving */
11742 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11743 Feld[x][y] != element) ||
11745 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11746 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11747 ChangePage[x][y] != -1)))
11752 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11753 trigger_event, recursion_loop_depth, recursion_loop_detected,
11754 recursion_loop_element, EL_NAME(recursion_loop_element));
11757 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11760 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11763 for (p = 0; p < element_info[element].num_change_pages; p++)
11765 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11767 /* check trigger element for all events where the element that is checked
11768 for changing interacts with a directly adjacent element -- this is
11769 different to element changes that affect other elements to change on the
11770 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11771 boolean check_trigger_element =
11772 (trigger_event == CE_TOUCHING_X ||
11773 trigger_event == CE_HITTING_X ||
11774 trigger_event == CE_HIT_BY_X ||
11776 /* this one was forgotten until 3.2.3 */
11777 trigger_event == CE_DIGGING_X);
11780 if (change->can_change_or_has_action &&
11781 change->has_event[trigger_event] &&
11782 change->trigger_side & trigger_side &&
11783 change->trigger_player & trigger_player &&
11784 (!check_trigger_element ||
11785 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11787 change->actual_trigger_element = trigger_element;
11788 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11789 change->actual_trigger_player_bits = trigger_player;
11790 change->actual_trigger_side = trigger_side;
11791 change->actual_trigger_ce_value = CustomValue[x][y];
11792 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11794 /* special case: trigger element not at (x,y) position for some events */
11795 if (check_trigger_element)
11807 { 0, 0 }, { 0, 0 }, { 0, 0 },
11811 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11812 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11814 change->actual_trigger_ce_value = CustomValue[xx][yy];
11815 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11818 if (change->can_change && !change_done)
11820 ChangeDelay[x][y] = 1;
11821 ChangeEvent[x][y] = trigger_event;
11823 HandleElementChange(x, y, p);
11825 change_done = TRUE;
11827 #if USE_NEW_DELAYED_ACTION
11828 else if (change->has_action)
11830 ExecuteCustomElementAction(x, y, element, p);
11831 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11834 if (change->has_action)
11836 ExecuteCustomElementAction(x, y, element, p);
11837 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11843 RECURSION_LOOP_DETECTION_END();
11845 return change_done;
11848 static void PlayPlayerSound(struct PlayerInfo *player)
11850 int jx = player->jx, jy = player->jy;
11851 int sound_element = player->artwork_element;
11852 int last_action = player->last_action_waiting;
11853 int action = player->action_waiting;
11855 if (player->is_waiting)
11857 if (action != last_action)
11858 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11860 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11864 if (action != last_action)
11865 StopSound(element_info[sound_element].sound[last_action]);
11867 if (last_action == ACTION_SLEEPING)
11868 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11872 static void PlayAllPlayersSound()
11876 for (i = 0; i < MAX_PLAYERS; i++)
11877 if (stored_player[i].active)
11878 PlayPlayerSound(&stored_player[i]);
11881 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11883 boolean last_waiting = player->is_waiting;
11884 int move_dir = player->MovDir;
11886 player->dir_waiting = move_dir;
11887 player->last_action_waiting = player->action_waiting;
11891 if (!last_waiting) /* not waiting -> waiting */
11893 player->is_waiting = TRUE;
11895 player->frame_counter_bored =
11897 game.player_boring_delay_fixed +
11898 GetSimpleRandom(game.player_boring_delay_random);
11899 player->frame_counter_sleeping =
11901 game.player_sleeping_delay_fixed +
11902 GetSimpleRandom(game.player_sleeping_delay_random);
11904 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11907 if (game.player_sleeping_delay_fixed +
11908 game.player_sleeping_delay_random > 0 &&
11909 player->anim_delay_counter == 0 &&
11910 player->post_delay_counter == 0 &&
11911 FrameCounter >= player->frame_counter_sleeping)
11912 player->is_sleeping = TRUE;
11913 else if (game.player_boring_delay_fixed +
11914 game.player_boring_delay_random > 0 &&
11915 FrameCounter >= player->frame_counter_bored)
11916 player->is_bored = TRUE;
11918 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11919 player->is_bored ? ACTION_BORING :
11922 if (player->is_sleeping && player->use_murphy)
11924 /* special case for sleeping Murphy when leaning against non-free tile */
11926 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11927 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11928 !IS_MOVING(player->jx - 1, player->jy)))
11929 move_dir = MV_LEFT;
11930 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11931 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11932 !IS_MOVING(player->jx + 1, player->jy)))
11933 move_dir = MV_RIGHT;
11935 player->is_sleeping = FALSE;
11937 player->dir_waiting = move_dir;
11940 if (player->is_sleeping)
11942 if (player->num_special_action_sleeping > 0)
11944 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11946 int last_special_action = player->special_action_sleeping;
11947 int num_special_action = player->num_special_action_sleeping;
11948 int special_action =
11949 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11950 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11951 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11952 last_special_action + 1 : ACTION_SLEEPING);
11953 int special_graphic =
11954 el_act_dir2img(player->artwork_element, special_action, move_dir);
11956 player->anim_delay_counter =
11957 graphic_info[special_graphic].anim_delay_fixed +
11958 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11959 player->post_delay_counter =
11960 graphic_info[special_graphic].post_delay_fixed +
11961 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11963 player->special_action_sleeping = special_action;
11966 if (player->anim_delay_counter > 0)
11968 player->action_waiting = player->special_action_sleeping;
11969 player->anim_delay_counter--;
11971 else if (player->post_delay_counter > 0)
11973 player->post_delay_counter--;
11977 else if (player->is_bored)
11979 if (player->num_special_action_bored > 0)
11981 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11983 int special_action =
11984 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11985 int special_graphic =
11986 el_act_dir2img(player->artwork_element, special_action, move_dir);
11988 player->anim_delay_counter =
11989 graphic_info[special_graphic].anim_delay_fixed +
11990 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11991 player->post_delay_counter =
11992 graphic_info[special_graphic].post_delay_fixed +
11993 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11995 player->special_action_bored = special_action;
11998 if (player->anim_delay_counter > 0)
12000 player->action_waiting = player->special_action_bored;
12001 player->anim_delay_counter--;
12003 else if (player->post_delay_counter > 0)
12005 player->post_delay_counter--;
12010 else if (last_waiting) /* waiting -> not waiting */
12012 player->is_waiting = FALSE;
12013 player->is_bored = FALSE;
12014 player->is_sleeping = FALSE;
12016 player->frame_counter_bored = -1;
12017 player->frame_counter_sleeping = -1;
12019 player->anim_delay_counter = 0;
12020 player->post_delay_counter = 0;
12022 player->dir_waiting = player->MovDir;
12023 player->action_waiting = ACTION_DEFAULT;
12025 player->special_action_bored = ACTION_DEFAULT;
12026 player->special_action_sleeping = ACTION_DEFAULT;
12030 static void CheckSingleStepMode(struct PlayerInfo *player)
12032 if (tape.single_step && tape.recording && !tape.pausing)
12034 /* as it is called "single step mode", just return to pause mode when the
12035 player stopped moving after one tile (or never starts moving at all) */
12036 if (!player->is_moving && !player->is_pushing)
12038 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12039 SnapField(player, 0, 0); /* stop snapping */
12044 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12046 int left = player_action & JOY_LEFT;
12047 int right = player_action & JOY_RIGHT;
12048 int up = player_action & JOY_UP;
12049 int down = player_action & JOY_DOWN;
12050 int button1 = player_action & JOY_BUTTON_1;
12051 int button2 = player_action & JOY_BUTTON_2;
12052 int dx = (left ? -1 : right ? 1 : 0);
12053 int dy = (up ? -1 : down ? 1 : 0);
12055 if (!player->active || tape.pausing)
12061 SnapField(player, dx, dy);
12065 DropElement(player);
12067 MovePlayer(player, dx, dy);
12070 CheckSingleStepMode(player);
12072 SetPlayerWaiting(player, FALSE);
12074 return player_action;
12078 /* no actions for this player (no input at player's configured device) */
12080 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12081 SnapField(player, 0, 0);
12082 CheckGravityMovementWhenNotMoving(player);
12084 if (player->MovPos == 0)
12085 SetPlayerWaiting(player, TRUE);
12087 if (player->MovPos == 0) /* needed for tape.playing */
12088 player->is_moving = FALSE;
12090 player->is_dropping = FALSE;
12091 player->is_dropping_pressed = FALSE;
12092 player->drop_pressed_delay = 0;
12094 CheckSingleStepMode(player);
12100 static void CheckLevelTime()
12104 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12105 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12107 if (level.native_em_level->lev->home == 0) /* all players at home */
12109 PlayerWins(local_player);
12111 AllPlayersGone = TRUE;
12113 level.native_em_level->lev->home = -1;
12116 if (level.native_em_level->ply[0]->alive == 0 &&
12117 level.native_em_level->ply[1]->alive == 0 &&
12118 level.native_em_level->ply[2]->alive == 0 &&
12119 level.native_em_level->ply[3]->alive == 0) /* all dead */
12120 AllPlayersGone = TRUE;
12122 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12124 if (game_sp.LevelSolved &&
12125 !game_sp.GameOver) /* game won */
12127 PlayerWins(local_player);
12129 game_sp.GameOver = TRUE;
12131 AllPlayersGone = TRUE;
12134 if (game_sp.GameOver) /* game lost */
12135 AllPlayersGone = TRUE;
12138 if (TimeFrames >= FRAMES_PER_SECOND)
12143 for (i = 0; i < MAX_PLAYERS; i++)
12145 struct PlayerInfo *player = &stored_player[i];
12147 if (SHIELD_ON(player))
12149 player->shield_normal_time_left--;
12151 if (player->shield_deadly_time_left > 0)
12152 player->shield_deadly_time_left--;
12156 if (!local_player->LevelSolved && !level.use_step_counter)
12164 if (TimeLeft <= 10 && setup.time_limit)
12165 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12168 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12169 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12171 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12173 /* (already called by UpdateAndDisplayGameControlValues() below) */
12174 // DisplayGameControlValues();
12176 DrawGameValue_Time(TimeLeft);
12179 if (!TimeLeft && setup.time_limit)
12181 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12182 level.native_em_level->lev->killed_out_of_time = TRUE;
12184 for (i = 0; i < MAX_PLAYERS; i++)
12185 KillPlayer(&stored_player[i]);
12189 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12191 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12193 /* (already called by UpdateAndDisplayGameControlValues() below) */
12194 // DisplayGameControlValues();
12197 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12198 DrawGameValue_Time(TimePlayed);
12201 level.native_em_level->lev->time =
12202 (game.no_time_limit ? TimePlayed : TimeLeft);
12205 if (tape.recording || tape.playing)
12206 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12210 UpdateAndDisplayGameControlValues();
12212 UpdateGameDoorValues();
12213 DrawGameDoorValues();
12217 void AdvanceFrameAndPlayerCounters(int player_nr)
12221 /* advance frame counters (global frame counter and time frame counter) */
12225 /* advance player counters (counters for move delay, move animation etc.) */
12226 for (i = 0; i < MAX_PLAYERS; i++)
12228 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12229 int move_delay_value = stored_player[i].move_delay_value;
12230 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12232 if (!advance_player_counters) /* not all players may be affected */
12235 #if USE_NEW_PLAYER_ANIM
12236 if (move_frames == 0) /* less than one move per game frame */
12238 int stepsize = TILEX / move_delay_value;
12239 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12240 int count = (stored_player[i].is_moving ?
12241 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12243 if (count % delay == 0)
12248 stored_player[i].Frame += move_frames;
12250 if (stored_player[i].MovPos != 0)
12251 stored_player[i].StepFrame += move_frames;
12253 if (stored_player[i].move_delay > 0)
12254 stored_player[i].move_delay--;
12256 /* due to bugs in previous versions, counter must count up, not down */
12257 if (stored_player[i].push_delay != -1)
12258 stored_player[i].push_delay++;
12260 if (stored_player[i].drop_delay > 0)
12261 stored_player[i].drop_delay--;
12263 if (stored_player[i].is_dropping_pressed)
12264 stored_player[i].drop_pressed_delay++;
12268 void StartGameActions(boolean init_network_game, boolean record_tape,
12271 unsigned int new_random_seed = InitRND(random_seed);
12274 TapeStartRecording(new_random_seed);
12276 #if defined(NETWORK_AVALIABLE)
12277 if (init_network_game)
12279 SendToServer_StartPlaying();
12290 static unsigned int game_frame_delay = 0;
12291 unsigned int game_frame_delay_value;
12292 byte *recorded_player_action;
12293 byte summarized_player_action = 0;
12294 byte tape_action[MAX_PLAYERS];
12297 /* detect endless loops, caused by custom element programming */
12298 if (recursion_loop_detected && recursion_loop_depth == 0)
12300 char *message = getStringCat3("Internal Error! Element ",
12301 EL_NAME(recursion_loop_element),
12302 " caused endless loop! Quit the game?");
12304 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12305 EL_NAME(recursion_loop_element));
12307 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12309 recursion_loop_detected = FALSE; /* if game should be continued */
12316 if (game.restart_level)
12317 StartGameActions(options.network, setup.autorecord, level.random_seed);
12319 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12320 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12322 if (level.native_em_level->lev->home == 0) /* all players at home */
12324 PlayerWins(local_player);
12326 AllPlayersGone = TRUE;
12328 level.native_em_level->lev->home = -1;
12331 if (level.native_em_level->ply[0]->alive == 0 &&
12332 level.native_em_level->ply[1]->alive == 0 &&
12333 level.native_em_level->ply[2]->alive == 0 &&
12334 level.native_em_level->ply[3]->alive == 0) /* all dead */
12335 AllPlayersGone = TRUE;
12337 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12339 if (game_sp.LevelSolved &&
12340 !game_sp.GameOver) /* game won */
12342 PlayerWins(local_player);
12344 game_sp.GameOver = TRUE;
12346 AllPlayersGone = TRUE;
12349 if (game_sp.GameOver) /* game lost */
12350 AllPlayersGone = TRUE;
12353 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12356 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12359 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12362 game_frame_delay_value =
12363 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12365 if (tape.playing && tape.warp_forward && !tape.pausing)
12366 game_frame_delay_value = 0;
12368 /* ---------- main game synchronization point ---------- */
12370 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12372 if (network_playing && !network_player_action_received)
12374 /* try to get network player actions in time */
12376 #if defined(NETWORK_AVALIABLE)
12377 /* last chance to get network player actions without main loop delay */
12378 HandleNetworking();
12381 /* game was quit by network peer */
12382 if (game_status != GAME_MODE_PLAYING)
12385 if (!network_player_action_received)
12386 return; /* failed to get network player actions in time */
12388 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12394 /* at this point we know that we really continue executing the game */
12396 network_player_action_received = FALSE;
12398 /* when playing tape, read previously recorded player input from tape data */
12399 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12402 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12407 if (tape.set_centered_player)
12409 game.centered_player_nr_next = tape.centered_player_nr_next;
12410 game.set_centered_player = TRUE;
12413 for (i = 0; i < MAX_PLAYERS; i++)
12415 summarized_player_action |= stored_player[i].action;
12418 if (!network_playing && (game.team_mode || tape.playing))
12419 stored_player[i].effective_action = stored_player[i].action;
12421 if (!network_playing)
12422 stored_player[i].effective_action = stored_player[i].action;
12426 #if defined(NETWORK_AVALIABLE)
12427 if (network_playing)
12428 SendToServer_MovePlayer(summarized_player_action);
12431 if (!options.network && !game.team_mode)
12432 local_player->effective_action = summarized_player_action;
12434 if (tape.recording &&
12436 setup.input_on_focus &&
12437 game.centered_player_nr != -1)
12439 for (i = 0; i < MAX_PLAYERS; i++)
12440 stored_player[i].effective_action =
12441 (i == game.centered_player_nr ? summarized_player_action : 0);
12444 if (recorded_player_action != NULL)
12445 for (i = 0; i < MAX_PLAYERS; i++)
12446 stored_player[i].effective_action = recorded_player_action[i];
12448 for (i = 0; i < MAX_PLAYERS; i++)
12450 tape_action[i] = stored_player[i].effective_action;
12453 /* (this may happen in the RND game engine if a player was not present on
12454 the playfield on level start, but appeared later from a custom element */
12455 if (tape.recording &&
12458 !tape.player_participates[i])
12459 tape.player_participates[i] = TRUE;
12461 /* (this can only happen in the R'n'D game engine) */
12462 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12463 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12467 /* only record actions from input devices, but not programmed actions */
12468 if (tape.recording)
12469 TapeRecordAction(tape_action);
12471 #if USE_NEW_PLAYER_ASSIGNMENTS
12473 if (game.team_mode)
12476 byte mapped_action[MAX_PLAYERS];
12478 #if DEBUG_PLAYER_ACTIONS
12480 for (i = 0; i < MAX_PLAYERS; i++)
12481 printf(" %d, ", stored_player[i].effective_action);
12484 for (i = 0; i < MAX_PLAYERS; i++)
12485 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12487 for (i = 0; i < MAX_PLAYERS; i++)
12488 stored_player[i].effective_action = mapped_action[i];
12490 #if DEBUG_PLAYER_ACTIONS
12492 for (i = 0; i < MAX_PLAYERS; i++)
12493 printf(" %d, ", stored_player[i].effective_action);
12497 #if DEBUG_PLAYER_ACTIONS
12501 for (i = 0; i < MAX_PLAYERS; i++)
12502 printf(" %d, ", stored_player[i].effective_action);
12509 printf("::: summarized_player_action == %d\n",
12510 local_player->effective_action);
12517 #if DEBUG_INIT_PLAYER
12520 printf("Player status (final):\n");
12522 for (i = 0; i < MAX_PLAYERS; i++)
12524 struct PlayerInfo *player = &stored_player[i];
12526 printf("- player %d: present == %d, connected == %d, active == %d",
12532 if (local_player == player)
12533 printf(" (local player)");
12543 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12545 GameActions_EM_Main();
12547 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12549 GameActions_SP_Main();
12557 void GameActions_EM_Main()
12559 byte effective_action[MAX_PLAYERS];
12560 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12563 for (i = 0; i < MAX_PLAYERS; i++)
12564 effective_action[i] = stored_player[i].effective_action;
12566 GameActions_EM(effective_action, warp_mode);
12570 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12573 void GameActions_SP_Main()
12575 byte effective_action[MAX_PLAYERS];
12576 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12579 for (i = 0; i < MAX_PLAYERS; i++)
12580 effective_action[i] = stored_player[i].effective_action;
12582 GameActions_SP(effective_action, warp_mode);
12586 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12589 void GameActions_RND()
12591 int magic_wall_x = 0, magic_wall_y = 0;
12592 int i, x, y, element, graphic;
12594 InitPlayfieldScanModeVars();
12596 #if USE_ONE_MORE_CHANGE_PER_FRAME
12597 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12599 SCAN_PLAYFIELD(x, y)
12601 ChangeCount[x][y] = 0;
12602 ChangeEvent[x][y] = -1;
12607 if (game.set_centered_player)
12609 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12611 /* switching to "all players" only possible if all players fit to screen */
12612 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12614 game.centered_player_nr_next = game.centered_player_nr;
12615 game.set_centered_player = FALSE;
12618 /* do not switch focus to non-existing (or non-active) player */
12619 if (game.centered_player_nr_next >= 0 &&
12620 !stored_player[game.centered_player_nr_next].active)
12622 game.centered_player_nr_next = game.centered_player_nr;
12623 game.set_centered_player = FALSE;
12627 if (game.set_centered_player &&
12628 ScreenMovPos == 0) /* screen currently aligned at tile position */
12632 if (game.centered_player_nr_next == -1)
12634 setScreenCenteredToAllPlayers(&sx, &sy);
12638 sx = stored_player[game.centered_player_nr_next].jx;
12639 sy = stored_player[game.centered_player_nr_next].jy;
12642 game.centered_player_nr = game.centered_player_nr_next;
12643 game.set_centered_player = FALSE;
12645 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12646 DrawGameDoorValues();
12649 for (i = 0; i < MAX_PLAYERS; i++)
12651 int actual_player_action = stored_player[i].effective_action;
12654 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12655 - rnd_equinox_tetrachloride 048
12656 - rnd_equinox_tetrachloride_ii 096
12657 - rnd_emanuel_schmieg 002
12658 - doctor_sloan_ww 001, 020
12660 if (stored_player[i].MovPos == 0)
12661 CheckGravityMovement(&stored_player[i]);
12664 /* overwrite programmed action with tape action */
12665 if (stored_player[i].programmed_action)
12666 actual_player_action = stored_player[i].programmed_action;
12668 PlayerActions(&stored_player[i], actual_player_action);
12670 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12673 ScrollScreen(NULL, SCROLL_GO_ON);
12675 /* for backwards compatibility, the following code emulates a fixed bug that
12676 occured when pushing elements (causing elements that just made their last
12677 pushing step to already (if possible) make their first falling step in the
12678 same game frame, which is bad); this code is also needed to use the famous
12679 "spring push bug" which is used in older levels and might be wanted to be
12680 used also in newer levels, but in this case the buggy pushing code is only
12681 affecting the "spring" element and no other elements */
12683 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12685 for (i = 0; i < MAX_PLAYERS; i++)
12687 struct PlayerInfo *player = &stored_player[i];
12688 int x = player->jx;
12689 int y = player->jy;
12691 if (player->active && player->is_pushing && player->is_moving &&
12693 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12694 Feld[x][y] == EL_SPRING))
12696 ContinueMoving(x, y);
12698 /* continue moving after pushing (this is actually a bug) */
12699 if (!IS_MOVING(x, y))
12700 Stop[x][y] = FALSE;
12706 debug_print_timestamp(0, "start main loop profiling");
12709 SCAN_PLAYFIELD(x, y)
12711 ChangeCount[x][y] = 0;
12712 ChangeEvent[x][y] = -1;
12714 /* this must be handled before main playfield loop */
12715 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12718 if (MovDelay[x][y] <= 0)
12722 #if USE_NEW_SNAP_DELAY
12723 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12726 if (MovDelay[x][y] <= 0)
12729 TEST_DrawLevelField(x, y);
12731 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12737 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12739 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12740 printf("GameActions(): This should never happen!\n");
12742 ChangePage[x][y] = -1;
12746 Stop[x][y] = FALSE;
12747 if (WasJustMoving[x][y] > 0)
12748 WasJustMoving[x][y]--;
12749 if (WasJustFalling[x][y] > 0)
12750 WasJustFalling[x][y]--;
12751 if (CheckCollision[x][y] > 0)
12752 CheckCollision[x][y]--;
12753 if (CheckImpact[x][y] > 0)
12754 CheckImpact[x][y]--;
12758 /* reset finished pushing action (not done in ContinueMoving() to allow
12759 continuous pushing animation for elements with zero push delay) */
12760 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12762 ResetGfxAnimation(x, y);
12763 TEST_DrawLevelField(x, y);
12767 if (IS_BLOCKED(x, y))
12771 Blocked2Moving(x, y, &oldx, &oldy);
12772 if (!IS_MOVING(oldx, oldy))
12774 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12775 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12776 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12777 printf("GameActions(): This should never happen!\n");
12784 debug_print_timestamp(0, "- time for pre-main loop:");
12787 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12788 SCAN_PLAYFIELD(x, y)
12790 element = Feld[x][y];
12791 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12796 int element2 = element;
12797 int graphic2 = graphic;
12799 int element2 = Feld[x][y];
12800 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12802 int last_gfx_frame = GfxFrame[x][y];
12804 if (graphic_info[graphic2].anim_global_sync)
12805 GfxFrame[x][y] = FrameCounter;
12806 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12807 GfxFrame[x][y] = CustomValue[x][y];
12808 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12809 GfxFrame[x][y] = element_info[element2].collect_score;
12810 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12811 GfxFrame[x][y] = ChangeDelay[x][y];
12813 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12814 DrawLevelGraphicAnimation(x, y, graphic2);
12817 ResetGfxFrame(x, y, TRUE);
12821 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12822 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12823 ResetRandomAnimationValue(x, y);
12827 SetRandomAnimationValue(x, y);
12831 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12834 #endif // -------------------- !!! TEST ONLY !!! --------------------
12837 debug_print_timestamp(0, "- time for TEST loop: -->");
12840 SCAN_PLAYFIELD(x, y)
12842 element = Feld[x][y];
12843 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12845 ResetGfxFrame(x, y, TRUE);
12847 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12848 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12849 ResetRandomAnimationValue(x, y);
12851 SetRandomAnimationValue(x, y);
12853 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12855 if (IS_INACTIVE(element))
12857 if (IS_ANIMATED(graphic))
12858 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12863 /* this may take place after moving, so 'element' may have changed */
12864 if (IS_CHANGING(x, y) &&
12865 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12867 int page = element_info[element].event_page_nr[CE_DELAY];
12870 HandleElementChange(x, y, page);
12872 if (CAN_CHANGE(element))
12873 HandleElementChange(x, y, page);
12875 if (HAS_ACTION(element))
12876 ExecuteCustomElementAction(x, y, element, page);
12879 element = Feld[x][y];
12880 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12883 #if 0 // ---------------------------------------------------------------------
12885 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12889 element = Feld[x][y];
12890 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12892 if (IS_ANIMATED(graphic) &&
12893 !IS_MOVING(x, y) &&
12895 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12897 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12898 TEST_DrawTwinkleOnField(x, y);
12900 else if (IS_MOVING(x, y))
12901 ContinueMoving(x, y);
12908 case EL_EM_EXIT_OPEN:
12909 case EL_SP_EXIT_OPEN:
12910 case EL_STEEL_EXIT_OPEN:
12911 case EL_EM_STEEL_EXIT_OPEN:
12912 case EL_SP_TERMINAL:
12913 case EL_SP_TERMINAL_ACTIVE:
12914 case EL_EXTRA_TIME:
12915 case EL_SHIELD_NORMAL:
12916 case EL_SHIELD_DEADLY:
12917 if (IS_ANIMATED(graphic))
12918 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12921 case EL_DYNAMITE_ACTIVE:
12922 case EL_EM_DYNAMITE_ACTIVE:
12923 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12924 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12925 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12926 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12927 case EL_SP_DISK_RED_ACTIVE:
12928 CheckDynamite(x, y);
12931 case EL_AMOEBA_GROWING:
12932 AmoebeWaechst(x, y);
12935 case EL_AMOEBA_SHRINKING:
12936 AmoebaDisappearing(x, y);
12939 #if !USE_NEW_AMOEBA_CODE
12940 case EL_AMOEBA_WET:
12941 case EL_AMOEBA_DRY:
12942 case EL_AMOEBA_FULL:
12944 case EL_EMC_DRIPPER:
12945 AmoebeAbleger(x, y);
12949 case EL_GAME_OF_LIFE:
12954 case EL_EXIT_CLOSED:
12958 case EL_EM_EXIT_CLOSED:
12962 case EL_STEEL_EXIT_CLOSED:
12963 CheckExitSteel(x, y);
12966 case EL_EM_STEEL_EXIT_CLOSED:
12967 CheckExitSteelEM(x, y);
12970 case EL_SP_EXIT_CLOSED:
12974 case EL_EXPANDABLE_WALL_GROWING:
12975 case EL_EXPANDABLE_STEELWALL_GROWING:
12976 MauerWaechst(x, y);
12979 case EL_EXPANDABLE_WALL:
12980 case EL_EXPANDABLE_WALL_HORIZONTAL:
12981 case EL_EXPANDABLE_WALL_VERTICAL:
12982 case EL_EXPANDABLE_WALL_ANY:
12983 case EL_BD_EXPANDABLE_WALL:
12984 MauerAbleger(x, y);
12987 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12988 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12989 case EL_EXPANDABLE_STEELWALL_ANY:
12990 MauerAblegerStahl(x, y);
12994 CheckForDragon(x, y);
13000 case EL_ELEMENT_SNAPPING:
13001 case EL_DIAGONAL_SHRINKING:
13002 case EL_DIAGONAL_GROWING:
13005 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13007 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13012 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13013 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13018 #else // ---------------------------------------------------------------------
13020 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13024 element = Feld[x][y];
13025 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13027 if (IS_ANIMATED(graphic) &&
13028 !IS_MOVING(x, y) &&
13030 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13032 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13033 TEST_DrawTwinkleOnField(x, y);
13035 else if ((element == EL_ACID ||
13036 element == EL_EXIT_OPEN ||
13037 element == EL_EM_EXIT_OPEN ||
13038 element == EL_SP_EXIT_OPEN ||
13039 element == EL_STEEL_EXIT_OPEN ||
13040 element == EL_EM_STEEL_EXIT_OPEN ||
13041 element == EL_SP_TERMINAL ||
13042 element == EL_SP_TERMINAL_ACTIVE ||
13043 element == EL_EXTRA_TIME ||
13044 element == EL_SHIELD_NORMAL ||
13045 element == EL_SHIELD_DEADLY) &&
13046 IS_ANIMATED(graphic))
13047 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13048 else if (IS_MOVING(x, y))
13049 ContinueMoving(x, y);
13050 else if (IS_ACTIVE_BOMB(element))
13051 CheckDynamite(x, y);
13052 else if (element == EL_AMOEBA_GROWING)
13053 AmoebeWaechst(x, y);
13054 else if (element == EL_AMOEBA_SHRINKING)
13055 AmoebaDisappearing(x, y);
13057 #if !USE_NEW_AMOEBA_CODE
13058 else if (IS_AMOEBALIVE(element))
13059 AmoebeAbleger(x, y);
13062 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13064 else if (element == EL_EXIT_CLOSED)
13066 else if (element == EL_EM_EXIT_CLOSED)
13068 else if (element == EL_STEEL_EXIT_CLOSED)
13069 CheckExitSteel(x, y);
13070 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13071 CheckExitSteelEM(x, y);
13072 else if (element == EL_SP_EXIT_CLOSED)
13074 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13075 element == EL_EXPANDABLE_STEELWALL_GROWING)
13076 MauerWaechst(x, y);
13077 else if (element == EL_EXPANDABLE_WALL ||
13078 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13079 element == EL_EXPANDABLE_WALL_VERTICAL ||
13080 element == EL_EXPANDABLE_WALL_ANY ||
13081 element == EL_BD_EXPANDABLE_WALL)
13082 MauerAbleger(x, y);
13083 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13084 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13085 element == EL_EXPANDABLE_STEELWALL_ANY)
13086 MauerAblegerStahl(x, y);
13087 else if (element == EL_FLAMES)
13088 CheckForDragon(x, y);
13089 else if (element == EL_EXPLOSION)
13090 ; /* drawing of correct explosion animation is handled separately */
13091 else if (element == EL_ELEMENT_SNAPPING ||
13092 element == EL_DIAGONAL_SHRINKING ||
13093 element == EL_DIAGONAL_GROWING)
13095 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13097 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13099 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13100 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13102 #endif // ---------------------------------------------------------------------
13104 if (IS_BELT_ACTIVE(element))
13105 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13107 if (game.magic_wall_active)
13109 int jx = local_player->jx, jy = local_player->jy;
13111 /* play the element sound at the position nearest to the player */
13112 if ((element == EL_MAGIC_WALL_FULL ||
13113 element == EL_MAGIC_WALL_ACTIVE ||
13114 element == EL_MAGIC_WALL_EMPTYING ||
13115 element == EL_BD_MAGIC_WALL_FULL ||
13116 element == EL_BD_MAGIC_WALL_ACTIVE ||
13117 element == EL_BD_MAGIC_WALL_EMPTYING ||
13118 element == EL_DC_MAGIC_WALL_FULL ||
13119 element == EL_DC_MAGIC_WALL_ACTIVE ||
13120 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13121 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13130 debug_print_timestamp(0, "- time for MAIN loop: -->");
13133 #if USE_NEW_AMOEBA_CODE
13134 /* new experimental amoeba growth stuff */
13135 if (!(FrameCounter % 8))
13137 static unsigned int random = 1684108901;
13139 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13141 x = RND(lev_fieldx);
13142 y = RND(lev_fieldy);
13143 element = Feld[x][y];
13145 if (!IS_PLAYER(x,y) &&
13146 (element == EL_EMPTY ||
13147 CAN_GROW_INTO(element) ||
13148 element == EL_QUICKSAND_EMPTY ||
13149 element == EL_QUICKSAND_FAST_EMPTY ||
13150 element == EL_ACID_SPLASH_LEFT ||
13151 element == EL_ACID_SPLASH_RIGHT))
13153 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13154 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13155 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13156 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13157 Feld[x][y] = EL_AMOEBA_DROP;
13160 random = random * 129 + 1;
13166 if (game.explosions_delayed)
13169 game.explosions_delayed = FALSE;
13171 SCAN_PLAYFIELD(x, y)
13173 element = Feld[x][y];
13175 if (ExplodeField[x][y])
13176 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13177 else if (element == EL_EXPLOSION)
13178 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13180 ExplodeField[x][y] = EX_TYPE_NONE;
13183 game.explosions_delayed = TRUE;
13186 if (game.magic_wall_active)
13188 if (!(game.magic_wall_time_left % 4))
13190 int element = Feld[magic_wall_x][magic_wall_y];
13192 if (element == EL_BD_MAGIC_WALL_FULL ||
13193 element == EL_BD_MAGIC_WALL_ACTIVE ||
13194 element == EL_BD_MAGIC_WALL_EMPTYING)
13195 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13196 else if (element == EL_DC_MAGIC_WALL_FULL ||
13197 element == EL_DC_MAGIC_WALL_ACTIVE ||
13198 element == EL_DC_MAGIC_WALL_EMPTYING)
13199 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13201 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13204 if (game.magic_wall_time_left > 0)
13206 game.magic_wall_time_left--;
13208 if (!game.magic_wall_time_left)
13210 SCAN_PLAYFIELD(x, y)
13212 element = Feld[x][y];
13214 if (element == EL_MAGIC_WALL_ACTIVE ||
13215 element == EL_MAGIC_WALL_FULL)
13217 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13218 TEST_DrawLevelField(x, y);
13220 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13221 element == EL_BD_MAGIC_WALL_FULL)
13223 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13224 TEST_DrawLevelField(x, y);
13226 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13227 element == EL_DC_MAGIC_WALL_FULL)
13229 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13230 TEST_DrawLevelField(x, y);
13234 game.magic_wall_active = FALSE;
13239 if (game.light_time_left > 0)
13241 game.light_time_left--;
13243 if (game.light_time_left == 0)
13244 RedrawAllLightSwitchesAndInvisibleElements();
13247 if (game.timegate_time_left > 0)
13249 game.timegate_time_left--;
13251 if (game.timegate_time_left == 0)
13252 CloseAllOpenTimegates();
13255 if (game.lenses_time_left > 0)
13257 game.lenses_time_left--;
13259 if (game.lenses_time_left == 0)
13260 RedrawAllInvisibleElementsForLenses();
13263 if (game.magnify_time_left > 0)
13265 game.magnify_time_left--;
13267 if (game.magnify_time_left == 0)
13268 RedrawAllInvisibleElementsForMagnifier();
13271 for (i = 0; i < MAX_PLAYERS; i++)
13273 struct PlayerInfo *player = &stored_player[i];
13275 if (SHIELD_ON(player))
13277 if (player->shield_deadly_time_left)
13278 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13279 else if (player->shield_normal_time_left)
13280 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13284 #if USE_DELAYED_GFX_REDRAW
13285 SCAN_PLAYFIELD(x, y)
13288 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13290 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13291 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13294 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13295 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13297 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13298 DrawLevelField(x, y);
13300 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13301 DrawLevelFieldCrumbled(x, y);
13303 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13304 DrawLevelFieldCrumbledNeighbours(x, y);
13306 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13307 DrawTwinkleOnField(x, y);
13310 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13317 PlayAllPlayersSound();
13319 if (options.debug) /* calculate frames per second */
13321 static unsigned int fps_counter = 0;
13322 static int fps_frames = 0;
13323 unsigned int fps_delay_ms = Counter() - fps_counter;
13327 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13329 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13332 fps_counter = Counter();
13335 redraw_mask |= REDRAW_FPS;
13338 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13340 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13342 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13344 local_player->show_envelope = 0;
13348 debug_print_timestamp(0, "stop main loop profiling ");
13349 printf("----------------------------------------------------------\n");
13352 /* use random number generator in every frame to make it less predictable */
13353 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13357 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13359 int min_x = x, min_y = y, max_x = x, max_y = y;
13362 for (i = 0; i < MAX_PLAYERS; i++)
13364 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13366 if (!stored_player[i].active || &stored_player[i] == player)
13369 min_x = MIN(min_x, jx);
13370 min_y = MIN(min_y, jy);
13371 max_x = MAX(max_x, jx);
13372 max_y = MAX(max_y, jy);
13375 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13378 static boolean AllPlayersInVisibleScreen()
13382 for (i = 0; i < MAX_PLAYERS; i++)
13384 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13386 if (!stored_player[i].active)
13389 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13396 void ScrollLevel(int dx, int dy)
13399 /* (directly solved in BlitBitmap() now) */
13400 static Bitmap *bitmap_db_field2 = NULL;
13401 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13408 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13409 /* only horizontal XOR vertical scroll direction allowed */
13410 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13415 /* (directly solved in BlitBitmap() now) */
13416 if (bitmap_db_field2 == NULL)
13417 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13419 /* needed when blitting directly to same bitmap -- should not be needed with
13420 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13421 BlitBitmap(drawto_field, bitmap_db_field2,
13422 FX + TILEX * (dx == -1) - softscroll_offset,
13423 FY + TILEY * (dy == -1) - softscroll_offset,
13424 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13425 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13426 FX + TILEX * (dx == 1) - softscroll_offset,
13427 FY + TILEY * (dy == 1) - softscroll_offset);
13428 BlitBitmap(bitmap_db_field2, drawto_field,
13429 FX + TILEX * (dx == 1) - softscroll_offset,
13430 FY + TILEY * (dy == 1) - softscroll_offset,
13431 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13432 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13433 FX + TILEX * (dx == 1) - softscroll_offset,
13434 FY + TILEY * (dy == 1) - softscroll_offset);
13439 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13440 int xsize = (BX2 - BX1 + 1);
13441 int ysize = (BY2 - BY1 + 1);
13442 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13443 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13444 int step = (start < end ? +1 : -1);
13446 for (i = start; i != end; i += step)
13448 BlitBitmap(drawto_field, drawto_field,
13449 FX + TILEX * (dx != 0 ? i + step : 0),
13450 FY + TILEY * (dy != 0 ? i + step : 0),
13451 TILEX * (dx != 0 ? 1 : xsize),
13452 TILEY * (dy != 0 ? 1 : ysize),
13453 FX + TILEX * (dx != 0 ? i : 0),
13454 FY + TILEY * (dy != 0 ? i : 0));
13461 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13463 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13467 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13469 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13474 BlitBitmap(drawto_field, drawto_field,
13475 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13476 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13477 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13478 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13479 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13480 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13482 BlitBitmap(drawto_field, drawto_field,
13483 FX + TILEX * (dx == -1) - softscroll_offset,
13484 FY + TILEY * (dy == -1) - softscroll_offset,
13485 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13486 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13487 FX + TILEX * (dx == 1) - softscroll_offset,
13488 FY + TILEY * (dy == 1) - softscroll_offset);
13496 x = (dx == 1 ? BX1 : BX2);
13497 for (y = BY1; y <= BY2; y++)
13498 DrawScreenField(x, y);
13503 y = (dy == 1 ? BY1 : BY2);
13504 for (x = BX1; x <= BX2; x++)
13505 DrawScreenField(x, y);
13508 redraw_mask |= REDRAW_FIELD;
13511 static boolean canFallDown(struct PlayerInfo *player)
13513 int jx = player->jx, jy = player->jy;
13515 return (IN_LEV_FIELD(jx, jy + 1) &&
13516 (IS_FREE(jx, jy + 1) ||
13517 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13518 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13519 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13522 static boolean canPassField(int x, int y, int move_dir)
13524 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13525 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13526 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13527 int nextx = x + dx;
13528 int nexty = y + dy;
13529 int element = Feld[x][y];
13531 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13532 !CAN_MOVE(element) &&
13533 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13534 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13535 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13538 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13540 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13541 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13542 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13546 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13547 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13548 (IS_DIGGABLE(Feld[newx][newy]) ||
13549 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13550 canPassField(newx, newy, move_dir)));
13553 static void CheckGravityMovement(struct PlayerInfo *player)
13555 #if USE_PLAYER_GRAVITY
13556 if (player->gravity && !player->programmed_action)
13558 if (game.gravity && !player->programmed_action)
13561 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13562 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13563 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13564 int jx = player->jx, jy = player->jy;
13565 boolean player_is_moving_to_valid_field =
13566 (!player_is_snapping &&
13567 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13568 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13569 boolean player_can_fall_down = canFallDown(player);
13571 if (player_can_fall_down &&
13572 !player_is_moving_to_valid_field)
13573 player->programmed_action = MV_DOWN;
13577 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13579 return CheckGravityMovement(player);
13581 #if USE_PLAYER_GRAVITY
13582 if (player->gravity && !player->programmed_action)
13584 if (game.gravity && !player->programmed_action)
13587 int jx = player->jx, jy = player->jy;
13588 boolean field_under_player_is_free =
13589 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13590 boolean player_is_standing_on_valid_field =
13591 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13592 (IS_WALKABLE(Feld[jx][jy]) &&
13593 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13595 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13596 player->programmed_action = MV_DOWN;
13601 MovePlayerOneStep()
13602 -----------------------------------------------------------------------------
13603 dx, dy: direction (non-diagonal) to try to move the player to
13604 real_dx, real_dy: direction as read from input device (can be diagonal)
13607 boolean MovePlayerOneStep(struct PlayerInfo *player,
13608 int dx, int dy, int real_dx, int real_dy)
13610 int jx = player->jx, jy = player->jy;
13611 int new_jx = jx + dx, new_jy = jy + dy;
13612 #if !USE_FIXED_DONT_RUN_INTO
13616 boolean player_can_move = !player->cannot_move;
13618 if (!player->active || (!dx && !dy))
13619 return MP_NO_ACTION;
13621 player->MovDir = (dx < 0 ? MV_LEFT :
13622 dx > 0 ? MV_RIGHT :
13624 dy > 0 ? MV_DOWN : MV_NONE);
13626 if (!IN_LEV_FIELD(new_jx, new_jy))
13627 return MP_NO_ACTION;
13629 if (!player_can_move)
13631 if (player->MovPos == 0)
13633 player->is_moving = FALSE;
13634 player->is_digging = FALSE;
13635 player->is_collecting = FALSE;
13636 player->is_snapping = FALSE;
13637 player->is_pushing = FALSE;
13642 if (!options.network && game.centered_player_nr == -1 &&
13643 !AllPlayersInSight(player, new_jx, new_jy))
13644 return MP_NO_ACTION;
13646 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13647 return MP_NO_ACTION;
13650 #if !USE_FIXED_DONT_RUN_INTO
13651 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13653 /* (moved to DigField()) */
13654 if (player_can_move && DONT_RUN_INTO(element))
13656 if (element == EL_ACID && dx == 0 && dy == 1)
13658 SplashAcid(new_jx, new_jy);
13659 Feld[jx][jy] = EL_PLAYER_1;
13660 InitMovingField(jx, jy, MV_DOWN);
13661 Store[jx][jy] = EL_ACID;
13662 ContinueMoving(jx, jy);
13663 BuryPlayer(player);
13666 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13672 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13673 if (can_move != MP_MOVING)
13676 /* check if DigField() has caused relocation of the player */
13677 if (player->jx != jx || player->jy != jy)
13678 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13680 StorePlayer[jx][jy] = 0;
13681 player->last_jx = jx;
13682 player->last_jy = jy;
13683 player->jx = new_jx;
13684 player->jy = new_jy;
13685 StorePlayer[new_jx][new_jy] = player->element_nr;
13687 if (player->move_delay_value_next != -1)
13689 player->move_delay_value = player->move_delay_value_next;
13690 player->move_delay_value_next = -1;
13694 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13696 player->step_counter++;
13698 PlayerVisit[jx][jy] = FrameCounter;
13700 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13701 player->is_moving = TRUE;
13705 /* should better be called in MovePlayer(), but this breaks some tapes */
13706 ScrollPlayer(player, SCROLL_INIT);
13712 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13714 int jx = player->jx, jy = player->jy;
13715 int old_jx = jx, old_jy = jy;
13716 int moved = MP_NO_ACTION;
13718 if (!player->active)
13723 if (player->MovPos == 0)
13725 player->is_moving = FALSE;
13726 player->is_digging = FALSE;
13727 player->is_collecting = FALSE;
13728 player->is_snapping = FALSE;
13729 player->is_pushing = FALSE;
13735 if (player->move_delay > 0)
13738 player->move_delay = -1; /* set to "uninitialized" value */
13740 /* store if player is automatically moved to next field */
13741 player->is_auto_moving = (player->programmed_action != MV_NONE);
13743 /* remove the last programmed player action */
13744 player->programmed_action = 0;
13746 if (player->MovPos)
13748 /* should only happen if pre-1.2 tape recordings are played */
13749 /* this is only for backward compatibility */
13751 int original_move_delay_value = player->move_delay_value;
13754 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13758 /* scroll remaining steps with finest movement resolution */
13759 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13761 while (player->MovPos)
13763 ScrollPlayer(player, SCROLL_GO_ON);
13764 ScrollScreen(NULL, SCROLL_GO_ON);
13766 AdvanceFrameAndPlayerCounters(player->index_nr);
13772 player->move_delay_value = original_move_delay_value;
13775 player->is_active = FALSE;
13777 if (player->last_move_dir & MV_HORIZONTAL)
13779 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13780 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13784 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13785 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13788 #if USE_FIXED_BORDER_RUNNING_GFX
13789 if (!moved && !player->is_active)
13791 player->is_moving = FALSE;
13792 player->is_digging = FALSE;
13793 player->is_collecting = FALSE;
13794 player->is_snapping = FALSE;
13795 player->is_pushing = FALSE;
13803 if (moved & MP_MOVING && !ScreenMovPos &&
13804 (player->index_nr == game.centered_player_nr ||
13805 game.centered_player_nr == -1))
13807 if (moved & MP_MOVING && !ScreenMovPos &&
13808 (player == local_player || !options.network))
13811 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13812 int offset = game.scroll_delay_value;
13814 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13816 /* actual player has left the screen -- scroll in that direction */
13817 if (jx != old_jx) /* player has moved horizontally */
13818 scroll_x += (jx - old_jx);
13819 else /* player has moved vertically */
13820 scroll_y += (jy - old_jy);
13824 if (jx != old_jx) /* player has moved horizontally */
13826 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13827 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13828 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13830 /* don't scroll over playfield boundaries */
13831 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13832 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13834 /* don't scroll more than one field at a time */
13835 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13837 /* don't scroll against the player's moving direction */
13838 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13839 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13840 scroll_x = old_scroll_x;
13842 else /* player has moved vertically */
13844 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13845 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13846 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13848 /* don't scroll over playfield boundaries */
13849 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13850 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13852 /* don't scroll more than one field at a time */
13853 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13855 /* don't scroll against the player's moving direction */
13856 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13857 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13858 scroll_y = old_scroll_y;
13862 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13865 if (!options.network && game.centered_player_nr == -1 &&
13866 !AllPlayersInVisibleScreen())
13868 scroll_x = old_scroll_x;
13869 scroll_y = old_scroll_y;
13873 if (!options.network && !AllPlayersInVisibleScreen())
13875 scroll_x = old_scroll_x;
13876 scroll_y = old_scroll_y;
13881 ScrollScreen(player, SCROLL_INIT);
13882 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13887 player->StepFrame = 0;
13889 if (moved & MP_MOVING)
13891 if (old_jx != jx && old_jy == jy)
13892 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13893 else if (old_jx == jx && old_jy != jy)
13894 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13896 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13898 player->last_move_dir = player->MovDir;
13899 player->is_moving = TRUE;
13900 player->is_snapping = FALSE;
13901 player->is_switching = FALSE;
13902 player->is_dropping = FALSE;
13903 player->is_dropping_pressed = FALSE;
13904 player->drop_pressed_delay = 0;
13907 /* should better be called here than above, but this breaks some tapes */
13908 ScrollPlayer(player, SCROLL_INIT);
13913 CheckGravityMovementWhenNotMoving(player);
13915 player->is_moving = FALSE;
13917 /* at this point, the player is allowed to move, but cannot move right now
13918 (e.g. because of something blocking the way) -- ensure that the player
13919 is also allowed to move in the next frame (in old versions before 3.1.1,
13920 the player was forced to wait again for eight frames before next try) */
13922 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13923 player->move_delay = 0; /* allow direct movement in the next frame */
13926 if (player->move_delay == -1) /* not yet initialized by DigField() */
13927 player->move_delay = player->move_delay_value;
13929 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13931 TestIfPlayerTouchesBadThing(jx, jy);
13932 TestIfPlayerTouchesCustomElement(jx, jy);
13935 if (!player->active)
13936 RemovePlayer(player);
13941 void ScrollPlayer(struct PlayerInfo *player, int mode)
13943 int jx = player->jx, jy = player->jy;
13944 int last_jx = player->last_jx, last_jy = player->last_jy;
13945 int move_stepsize = TILEX / player->move_delay_value;
13947 #if USE_NEW_PLAYER_SPEED
13948 if (!player->active)
13951 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13954 if (!player->active || player->MovPos == 0)
13958 if (mode == SCROLL_INIT)
13960 player->actual_frame_counter = FrameCounter;
13961 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13963 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13964 Feld[last_jx][last_jy] == EL_EMPTY)
13966 int last_field_block_delay = 0; /* start with no blocking at all */
13967 int block_delay_adjustment = player->block_delay_adjustment;
13969 /* if player blocks last field, add delay for exactly one move */
13970 if (player->block_last_field)
13972 last_field_block_delay += player->move_delay_value;
13974 /* when blocking enabled, prevent moving up despite gravity */
13975 #if USE_PLAYER_GRAVITY
13976 if (player->gravity && player->MovDir == MV_UP)
13977 block_delay_adjustment = -1;
13979 if (game.gravity && player->MovDir == MV_UP)
13980 block_delay_adjustment = -1;
13984 /* add block delay adjustment (also possible when not blocking) */
13985 last_field_block_delay += block_delay_adjustment;
13987 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13988 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13991 #if USE_NEW_PLAYER_SPEED
13992 if (player->MovPos != 0) /* player has not yet reached destination */
13998 else if (!FrameReached(&player->actual_frame_counter, 1))
14001 #if USE_NEW_PLAYER_SPEED
14002 if (player->MovPos != 0)
14004 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14005 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14007 /* before DrawPlayer() to draw correct player graphic for this case */
14008 if (player->MovPos == 0)
14009 CheckGravityMovement(player);
14012 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14013 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14015 /* before DrawPlayer() to draw correct player graphic for this case */
14016 if (player->MovPos == 0)
14017 CheckGravityMovement(player);
14020 if (player->MovPos == 0) /* player reached destination field */
14022 if (player->move_delay_reset_counter > 0)
14024 player->move_delay_reset_counter--;
14026 if (player->move_delay_reset_counter == 0)
14028 /* continue with normal speed after quickly moving through gate */
14029 HALVE_PLAYER_SPEED(player);
14031 /* be able to make the next move without delay */
14032 player->move_delay = 0;
14036 player->last_jx = jx;
14037 player->last_jy = jy;
14039 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14040 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14042 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14044 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14045 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14047 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14049 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14050 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14052 DrawPlayer(player); /* needed here only to cleanup last field */
14053 RemovePlayer(player);
14055 if (local_player->friends_still_needed == 0 ||
14056 IS_SP_ELEMENT(Feld[jx][jy]))
14057 PlayerWins(player);
14060 /* this breaks one level: "machine", level 000 */
14062 int move_direction = player->MovDir;
14063 int enter_side = MV_DIR_OPPOSITE(move_direction);
14064 int leave_side = move_direction;
14065 int old_jx = last_jx;
14066 int old_jy = last_jy;
14067 int old_element = Feld[old_jx][old_jy];
14068 int new_element = Feld[jx][jy];
14070 if (IS_CUSTOM_ELEMENT(old_element))
14071 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14073 player->index_bit, leave_side);
14075 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14076 CE_PLAYER_LEAVES_X,
14077 player->index_bit, leave_side);
14079 if (IS_CUSTOM_ELEMENT(new_element))
14080 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14081 player->index_bit, enter_side);
14083 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14084 CE_PLAYER_ENTERS_X,
14085 player->index_bit, enter_side);
14087 #if USE_FIX_CE_ACTION_WITH_PLAYER
14088 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14089 CE_MOVE_OF_X, move_direction);
14091 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14092 CE_MOVE_OF_X, move_direction);
14096 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14098 TestIfPlayerTouchesBadThing(jx, jy);
14099 TestIfPlayerTouchesCustomElement(jx, jy);
14101 /* needed because pushed element has not yet reached its destination,
14102 so it would trigger a change event at its previous field location */
14103 if (!player->is_pushing)
14104 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14106 if (!player->active)
14107 RemovePlayer(player);
14110 if (!local_player->LevelSolved && level.use_step_counter)
14120 if (TimeLeft <= 10 && setup.time_limit)
14121 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14124 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14126 DisplayGameControlValues();
14128 DrawGameValue_Time(TimeLeft);
14131 if (!TimeLeft && setup.time_limit)
14132 for (i = 0; i < MAX_PLAYERS; i++)
14133 KillPlayer(&stored_player[i]);
14136 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14138 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14140 DisplayGameControlValues();
14143 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14144 DrawGameValue_Time(TimePlayed);
14148 if (tape.single_step && tape.recording && !tape.pausing &&
14149 !player->programmed_action)
14150 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14154 void ScrollScreen(struct PlayerInfo *player, int mode)
14156 static unsigned int screen_frame_counter = 0;
14158 if (mode == SCROLL_INIT)
14160 /* set scrolling step size according to actual player's moving speed */
14161 ScrollStepSize = TILEX / player->move_delay_value;
14163 screen_frame_counter = FrameCounter;
14164 ScreenMovDir = player->MovDir;
14165 ScreenMovPos = player->MovPos;
14166 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14169 else if (!FrameReached(&screen_frame_counter, 1))
14174 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14175 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14176 redraw_mask |= REDRAW_FIELD;
14179 ScreenMovDir = MV_NONE;
14182 void TestIfPlayerTouchesCustomElement(int x, int y)
14184 static int xy[4][2] =
14191 static int trigger_sides[4][2] =
14193 /* center side border side */
14194 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14195 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14196 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14197 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14199 static int touch_dir[4] =
14201 MV_LEFT | MV_RIGHT,
14206 int center_element = Feld[x][y]; /* should always be non-moving! */
14209 for (i = 0; i < NUM_DIRECTIONS; i++)
14211 int xx = x + xy[i][0];
14212 int yy = y + xy[i][1];
14213 int center_side = trigger_sides[i][0];
14214 int border_side = trigger_sides[i][1];
14215 int border_element;
14217 if (!IN_LEV_FIELD(xx, yy))
14220 if (IS_PLAYER(x, y)) /* player found at center element */
14222 struct PlayerInfo *player = PLAYERINFO(x, y);
14224 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14225 border_element = Feld[xx][yy]; /* may be moving! */
14226 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14227 border_element = Feld[xx][yy];
14228 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14229 border_element = MovingOrBlocked2Element(xx, yy);
14231 continue; /* center and border element do not touch */
14233 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14234 player->index_bit, border_side);
14235 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14236 CE_PLAYER_TOUCHES_X,
14237 player->index_bit, border_side);
14239 #if USE_FIX_CE_ACTION_WITH_PLAYER
14241 /* use player element that is initially defined in the level playfield,
14242 not the player element that corresponds to the runtime player number
14243 (example: a level that contains EL_PLAYER_3 as the only player would
14244 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14245 int player_element = PLAYERINFO(x, y)->initial_element;
14247 CheckElementChangeBySide(xx, yy, border_element, player_element,
14248 CE_TOUCHING_X, border_side);
14252 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14254 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14256 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14258 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14259 continue; /* center and border element do not touch */
14262 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14263 player->index_bit, center_side);
14264 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14265 CE_PLAYER_TOUCHES_X,
14266 player->index_bit, center_side);
14268 #if USE_FIX_CE_ACTION_WITH_PLAYER
14270 /* use player element that is initially defined in the level playfield,
14271 not the player element that corresponds to the runtime player number
14272 (example: a level that contains EL_PLAYER_3 as the only player would
14273 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14274 int player_element = PLAYERINFO(xx, yy)->initial_element;
14276 CheckElementChangeBySide(x, y, center_element, player_element,
14277 CE_TOUCHING_X, center_side);
14286 #if USE_ELEMENT_TOUCHING_BUGFIX
14288 void TestIfElementTouchesCustomElement(int x, int y)
14290 static int xy[4][2] =
14297 static int trigger_sides[4][2] =
14299 /* center side border side */
14300 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14301 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14302 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14303 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14305 static int touch_dir[4] =
14307 MV_LEFT | MV_RIGHT,
14312 boolean change_center_element = FALSE;
14313 int center_element = Feld[x][y]; /* should always be non-moving! */
14314 int border_element_old[NUM_DIRECTIONS];
14317 for (i = 0; i < NUM_DIRECTIONS; i++)
14319 int xx = x + xy[i][0];
14320 int yy = y + xy[i][1];
14321 int border_element;
14323 border_element_old[i] = -1;
14325 if (!IN_LEV_FIELD(xx, yy))
14328 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14329 border_element = Feld[xx][yy]; /* may be moving! */
14330 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14331 border_element = Feld[xx][yy];
14332 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14333 border_element = MovingOrBlocked2Element(xx, yy);
14335 continue; /* center and border element do not touch */
14337 border_element_old[i] = border_element;
14340 for (i = 0; i < NUM_DIRECTIONS; i++)
14342 int xx = x + xy[i][0];
14343 int yy = y + xy[i][1];
14344 int center_side = trigger_sides[i][0];
14345 int border_element = border_element_old[i];
14347 if (border_element == -1)
14350 /* check for change of border element */
14351 CheckElementChangeBySide(xx, yy, border_element, center_element,
14352 CE_TOUCHING_X, center_side);
14354 /* (center element cannot be player, so we dont have to check this here) */
14357 for (i = 0; i < NUM_DIRECTIONS; i++)
14359 int xx = x + xy[i][0];
14360 int yy = y + xy[i][1];
14361 int border_side = trigger_sides[i][1];
14362 int border_element = border_element_old[i];
14364 if (border_element == -1)
14367 /* check for change of center element (but change it only once) */
14368 if (!change_center_element)
14369 change_center_element =
14370 CheckElementChangeBySide(x, y, center_element, border_element,
14371 CE_TOUCHING_X, border_side);
14373 #if USE_FIX_CE_ACTION_WITH_PLAYER
14374 if (IS_PLAYER(xx, yy))
14376 /* use player element that is initially defined in the level playfield,
14377 not the player element that corresponds to the runtime player number
14378 (example: a level that contains EL_PLAYER_3 as the only player would
14379 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14380 int player_element = PLAYERINFO(xx, yy)->initial_element;
14382 CheckElementChangeBySide(x, y, center_element, player_element,
14383 CE_TOUCHING_X, border_side);
14391 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14393 static int xy[4][2] =
14400 static int trigger_sides[4][2] =
14402 /* center side border side */
14403 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14404 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14405 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14406 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14408 static int touch_dir[4] =
14410 MV_LEFT | MV_RIGHT,
14415 boolean change_center_element = FALSE;
14416 int center_element = Feld[x][y]; /* should always be non-moving! */
14419 for (i = 0; i < NUM_DIRECTIONS; i++)
14421 int xx = x + xy[i][0];
14422 int yy = y + xy[i][1];
14423 int center_side = trigger_sides[i][0];
14424 int border_side = trigger_sides[i][1];
14425 int border_element;
14427 if (!IN_LEV_FIELD(xx, yy))
14430 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14431 border_element = Feld[xx][yy]; /* may be moving! */
14432 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14433 border_element = Feld[xx][yy];
14434 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14435 border_element = MovingOrBlocked2Element(xx, yy);
14437 continue; /* center and border element do not touch */
14439 /* check for change of center element (but change it only once) */
14440 if (!change_center_element)
14441 change_center_element =
14442 CheckElementChangeBySide(x, y, center_element, border_element,
14443 CE_TOUCHING_X, border_side);
14445 /* check for change of border element */
14446 CheckElementChangeBySide(xx, yy, border_element, center_element,
14447 CE_TOUCHING_X, center_side);
14453 void TestIfElementHitsCustomElement(int x, int y, int direction)
14455 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14456 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14457 int hitx = x + dx, hity = y + dy;
14458 int hitting_element = Feld[x][y];
14459 int touched_element;
14461 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14464 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14465 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14467 if (IN_LEV_FIELD(hitx, hity))
14469 int opposite_direction = MV_DIR_OPPOSITE(direction);
14470 int hitting_side = direction;
14471 int touched_side = opposite_direction;
14472 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14473 MovDir[hitx][hity] != direction ||
14474 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14480 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14481 CE_HITTING_X, touched_side);
14483 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14484 CE_HIT_BY_X, hitting_side);
14486 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14487 CE_HIT_BY_SOMETHING, opposite_direction);
14489 #if USE_FIX_CE_ACTION_WITH_PLAYER
14490 if (IS_PLAYER(hitx, hity))
14492 /* use player element that is initially defined in the level playfield,
14493 not the player element that corresponds to the runtime player number
14494 (example: a level that contains EL_PLAYER_3 as the only player would
14495 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14496 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14498 CheckElementChangeBySide(x, y, hitting_element, player_element,
14499 CE_HITTING_X, touched_side);
14505 /* "hitting something" is also true when hitting the playfield border */
14506 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14507 CE_HITTING_SOMETHING, direction);
14511 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14513 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14514 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14515 int hitx = x + dx, hity = y + dy;
14516 int hitting_element = Feld[x][y];
14517 int touched_element;
14519 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14520 !IS_FREE(hitx, hity) &&
14521 (!IS_MOVING(hitx, hity) ||
14522 MovDir[hitx][hity] != direction ||
14523 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14526 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14530 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14534 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14535 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14537 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14538 EP_CAN_SMASH_EVERYTHING, direction);
14540 if (IN_LEV_FIELD(hitx, hity))
14542 int opposite_direction = MV_DIR_OPPOSITE(direction);
14543 int hitting_side = direction;
14544 int touched_side = opposite_direction;
14546 int touched_element = MovingOrBlocked2Element(hitx, hity);
14549 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14550 MovDir[hitx][hity] != direction ||
14551 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14560 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14561 CE_SMASHED_BY_SOMETHING, opposite_direction);
14563 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14564 CE_OTHER_IS_SMASHING, touched_side);
14566 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14567 CE_OTHER_GETS_SMASHED, hitting_side);
14573 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14575 int i, kill_x = -1, kill_y = -1;
14577 int bad_element = -1;
14578 static int test_xy[4][2] =
14585 static int test_dir[4] =
14593 for (i = 0; i < NUM_DIRECTIONS; i++)
14595 int test_x, test_y, test_move_dir, test_element;
14597 test_x = good_x + test_xy[i][0];
14598 test_y = good_y + test_xy[i][1];
14600 if (!IN_LEV_FIELD(test_x, test_y))
14604 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14606 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14608 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14609 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14611 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14612 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14616 bad_element = test_element;
14622 if (kill_x != -1 || kill_y != -1)
14624 if (IS_PLAYER(good_x, good_y))
14626 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14628 if (player->shield_deadly_time_left > 0 &&
14629 !IS_INDESTRUCTIBLE(bad_element))
14630 Bang(kill_x, kill_y);
14631 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14632 KillPlayer(player);
14635 Bang(good_x, good_y);
14639 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14641 int i, kill_x = -1, kill_y = -1;
14642 int bad_element = Feld[bad_x][bad_y];
14643 static int test_xy[4][2] =
14650 static int touch_dir[4] =
14652 MV_LEFT | MV_RIGHT,
14657 static int test_dir[4] =
14665 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14668 for (i = 0; i < NUM_DIRECTIONS; i++)
14670 int test_x, test_y, test_move_dir, test_element;
14672 test_x = bad_x + test_xy[i][0];
14673 test_y = bad_y + test_xy[i][1];
14675 if (!IN_LEV_FIELD(test_x, test_y))
14679 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14681 test_element = Feld[test_x][test_y];
14683 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14684 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14686 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14687 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14689 /* good thing is player or penguin that does not move away */
14690 if (IS_PLAYER(test_x, test_y))
14692 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14694 if (bad_element == EL_ROBOT && player->is_moving)
14695 continue; /* robot does not kill player if he is moving */
14697 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14699 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14700 continue; /* center and border element do not touch */
14708 else if (test_element == EL_PENGUIN)
14718 if (kill_x != -1 || kill_y != -1)
14720 if (IS_PLAYER(kill_x, kill_y))
14722 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14724 if (player->shield_deadly_time_left > 0 &&
14725 !IS_INDESTRUCTIBLE(bad_element))
14726 Bang(bad_x, bad_y);
14727 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14728 KillPlayer(player);
14731 Bang(kill_x, kill_y);
14735 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14737 int bad_element = Feld[bad_x][bad_y];
14738 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14739 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14740 int test_x = bad_x + dx, test_y = bad_y + dy;
14741 int test_move_dir, test_element;
14742 int kill_x = -1, kill_y = -1;
14744 if (!IN_LEV_FIELD(test_x, test_y))
14748 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14750 test_element = Feld[test_x][test_y];
14752 if (test_move_dir != bad_move_dir)
14754 /* good thing can be player or penguin that does not move away */
14755 if (IS_PLAYER(test_x, test_y))
14757 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14759 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14760 player as being hit when he is moving towards the bad thing, because
14761 the "get hit by" condition would be lost after the player stops) */
14762 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14763 return; /* player moves away from bad thing */
14768 else if (test_element == EL_PENGUIN)
14775 if (kill_x != -1 || kill_y != -1)
14777 if (IS_PLAYER(kill_x, kill_y))
14779 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14781 if (player->shield_deadly_time_left > 0 &&
14782 !IS_INDESTRUCTIBLE(bad_element))
14783 Bang(bad_x, bad_y);
14784 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14785 KillPlayer(player);
14788 Bang(kill_x, kill_y);
14792 void TestIfPlayerTouchesBadThing(int x, int y)
14794 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14797 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14799 TestIfGoodThingHitsBadThing(x, y, move_dir);
14802 void TestIfBadThingTouchesPlayer(int x, int y)
14804 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14807 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14809 TestIfBadThingHitsGoodThing(x, y, move_dir);
14812 void TestIfFriendTouchesBadThing(int x, int y)
14814 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14817 void TestIfBadThingTouchesFriend(int x, int y)
14819 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14822 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14824 int i, kill_x = bad_x, kill_y = bad_y;
14825 static int xy[4][2] =
14833 for (i = 0; i < NUM_DIRECTIONS; i++)
14837 x = bad_x + xy[i][0];
14838 y = bad_y + xy[i][1];
14839 if (!IN_LEV_FIELD(x, y))
14842 element = Feld[x][y];
14843 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14844 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14852 if (kill_x != bad_x || kill_y != bad_y)
14853 Bang(bad_x, bad_y);
14856 void KillPlayer(struct PlayerInfo *player)
14858 int jx = player->jx, jy = player->jy;
14860 if (!player->active)
14864 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14865 player->killed, player->active, player->reanimated);
14868 /* the following code was introduced to prevent an infinite loop when calling
14870 -> CheckTriggeredElementChangeExt()
14871 -> ExecuteCustomElementAction()
14873 -> (infinitely repeating the above sequence of function calls)
14874 which occurs when killing the player while having a CE with the setting
14875 "kill player X when explosion of <player X>"; the solution using a new
14876 field "player->killed" was chosen for backwards compatibility, although
14877 clever use of the fields "player->active" etc. would probably also work */
14879 if (player->killed)
14883 player->killed = TRUE;
14885 /* remove accessible field at the player's position */
14886 Feld[jx][jy] = EL_EMPTY;
14888 /* deactivate shield (else Bang()/Explode() would not work right) */
14889 player->shield_normal_time_left = 0;
14890 player->shield_deadly_time_left = 0;
14893 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14894 player->killed, player->active, player->reanimated);
14900 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14901 player->killed, player->active, player->reanimated);
14904 #if USE_PLAYER_REANIMATION
14906 if (player->reanimated) /* killed player may have been reanimated */
14907 player->killed = player->reanimated = FALSE;
14909 BuryPlayer(player);
14911 if (player->killed) /* player may have been reanimated */
14912 BuryPlayer(player);
14915 BuryPlayer(player);
14919 static void KillPlayerUnlessEnemyProtected(int x, int y)
14921 if (!PLAYER_ENEMY_PROTECTED(x, y))
14922 KillPlayer(PLAYERINFO(x, y));
14925 static void KillPlayerUnlessExplosionProtected(int x, int y)
14927 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14928 KillPlayer(PLAYERINFO(x, y));
14931 void BuryPlayer(struct PlayerInfo *player)
14933 int jx = player->jx, jy = player->jy;
14935 if (!player->active)
14938 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14939 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14941 player->GameOver = TRUE;
14942 RemovePlayer(player);
14945 void RemovePlayer(struct PlayerInfo *player)
14947 int jx = player->jx, jy = player->jy;
14948 int i, found = FALSE;
14950 player->present = FALSE;
14951 player->active = FALSE;
14953 if (!ExplodeField[jx][jy])
14954 StorePlayer[jx][jy] = 0;
14956 if (player->is_moving)
14957 TEST_DrawLevelField(player->last_jx, player->last_jy);
14959 for (i = 0; i < MAX_PLAYERS; i++)
14960 if (stored_player[i].active)
14964 AllPlayersGone = TRUE;
14970 #if USE_NEW_SNAP_DELAY
14971 static void setFieldForSnapping(int x, int y, int element, int direction)
14973 struct ElementInfo *ei = &element_info[element];
14974 int direction_bit = MV_DIR_TO_BIT(direction);
14975 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14976 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14977 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14979 Feld[x][y] = EL_ELEMENT_SNAPPING;
14980 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14982 ResetGfxAnimation(x, y);
14984 GfxElement[x][y] = element;
14985 GfxAction[x][y] = action;
14986 GfxDir[x][y] = direction;
14987 GfxFrame[x][y] = -1;
14992 =============================================================================
14993 checkDiagonalPushing()
14994 -----------------------------------------------------------------------------
14995 check if diagonal input device direction results in pushing of object
14996 (by checking if the alternative direction is walkable, diggable, ...)
14997 =============================================================================
15000 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15001 int x, int y, int real_dx, int real_dy)
15003 int jx, jy, dx, dy, xx, yy;
15005 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
15008 /* diagonal direction: check alternative direction */
15013 xx = jx + (dx == 0 ? real_dx : 0);
15014 yy = jy + (dy == 0 ? real_dy : 0);
15016 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15020 =============================================================================
15022 -----------------------------------------------------------------------------
15023 x, y: field next to player (non-diagonal) to try to dig to
15024 real_dx, real_dy: direction as read from input device (can be diagonal)
15025 =============================================================================
15028 static int DigField(struct PlayerInfo *player,
15029 int oldx, int oldy, int x, int y,
15030 int real_dx, int real_dy, int mode)
15032 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15033 boolean player_was_pushing = player->is_pushing;
15034 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15035 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15036 int jx = oldx, jy = oldy;
15037 int dx = x - jx, dy = y - jy;
15038 int nextx = x + dx, nexty = y + dy;
15039 int move_direction = (dx == -1 ? MV_LEFT :
15040 dx == +1 ? MV_RIGHT :
15042 dy == +1 ? MV_DOWN : MV_NONE);
15043 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15044 int dig_side = MV_DIR_OPPOSITE(move_direction);
15045 int old_element = Feld[jx][jy];
15046 #if USE_FIXED_DONT_RUN_INTO
15047 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15053 if (is_player) /* function can also be called by EL_PENGUIN */
15055 if (player->MovPos == 0)
15057 player->is_digging = FALSE;
15058 player->is_collecting = FALSE;
15061 if (player->MovPos == 0) /* last pushing move finished */
15062 player->is_pushing = FALSE;
15064 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15066 player->is_switching = FALSE;
15067 player->push_delay = -1;
15069 return MP_NO_ACTION;
15073 #if !USE_FIXED_DONT_RUN_INTO
15074 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15075 return MP_NO_ACTION;
15078 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15079 old_element = Back[jx][jy];
15081 /* in case of element dropped at player position, check background */
15082 else if (Back[jx][jy] != EL_EMPTY &&
15083 game.engine_version >= VERSION_IDENT(2,2,0,0))
15084 old_element = Back[jx][jy];
15086 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15087 return MP_NO_ACTION; /* field has no opening in this direction */
15089 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15090 return MP_NO_ACTION; /* field has no opening in this direction */
15092 #if USE_FIXED_DONT_RUN_INTO
15093 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15097 Feld[jx][jy] = player->artwork_element;
15098 InitMovingField(jx, jy, MV_DOWN);
15099 Store[jx][jy] = EL_ACID;
15100 ContinueMoving(jx, jy);
15101 BuryPlayer(player);
15103 return MP_DONT_RUN_INTO;
15107 #if USE_FIXED_DONT_RUN_INTO
15108 if (player_can_move && DONT_RUN_INTO(element))
15110 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15112 return MP_DONT_RUN_INTO;
15116 #if USE_FIXED_DONT_RUN_INTO
15117 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15118 return MP_NO_ACTION;
15121 #if !USE_FIXED_DONT_RUN_INTO
15122 element = Feld[x][y];
15125 collect_count = element_info[element].collect_count_initial;
15127 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15128 return MP_NO_ACTION;
15130 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15131 player_can_move = player_can_move_or_snap;
15133 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15134 game.engine_version >= VERSION_IDENT(2,2,0,0))
15136 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15137 player->index_bit, dig_side);
15138 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15139 player->index_bit, dig_side);
15141 if (element == EL_DC_LANDMINE)
15144 if (Feld[x][y] != element) /* field changed by snapping */
15147 return MP_NO_ACTION;
15150 #if USE_PLAYER_GRAVITY
15151 if (player->gravity && is_player && !player->is_auto_moving &&
15152 canFallDown(player) && move_direction != MV_DOWN &&
15153 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15154 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15156 if (game.gravity && is_player && !player->is_auto_moving &&
15157 canFallDown(player) && move_direction != MV_DOWN &&
15158 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15159 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15162 if (player_can_move &&
15163 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15165 int sound_element = SND_ELEMENT(element);
15166 int sound_action = ACTION_WALKING;
15168 if (IS_RND_GATE(element))
15170 if (!player->key[RND_GATE_NR(element)])
15171 return MP_NO_ACTION;
15173 else if (IS_RND_GATE_GRAY(element))
15175 if (!player->key[RND_GATE_GRAY_NR(element)])
15176 return MP_NO_ACTION;
15178 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15180 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15181 return MP_NO_ACTION;
15183 else if (element == EL_EXIT_OPEN ||
15184 element == EL_EM_EXIT_OPEN ||
15186 element == EL_EM_EXIT_OPENING ||
15188 element == EL_STEEL_EXIT_OPEN ||
15189 element == EL_EM_STEEL_EXIT_OPEN ||
15191 element == EL_EM_STEEL_EXIT_OPENING ||
15193 element == EL_SP_EXIT_OPEN ||
15194 element == EL_SP_EXIT_OPENING)
15196 sound_action = ACTION_PASSING; /* player is passing exit */
15198 else if (element == EL_EMPTY)
15200 sound_action = ACTION_MOVING; /* nothing to walk on */
15203 /* play sound from background or player, whatever is available */
15204 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15205 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15207 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15209 else if (player_can_move &&
15210 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15212 if (!ACCESS_FROM(element, opposite_direction))
15213 return MP_NO_ACTION; /* field not accessible from this direction */
15215 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15216 return MP_NO_ACTION;
15218 if (IS_EM_GATE(element))
15220 if (!player->key[EM_GATE_NR(element)])
15221 return MP_NO_ACTION;
15223 else if (IS_EM_GATE_GRAY(element))
15225 if (!player->key[EM_GATE_GRAY_NR(element)])
15226 return MP_NO_ACTION;
15228 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15230 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15231 return MP_NO_ACTION;
15233 else if (IS_EMC_GATE(element))
15235 if (!player->key[EMC_GATE_NR(element)])
15236 return MP_NO_ACTION;
15238 else if (IS_EMC_GATE_GRAY(element))
15240 if (!player->key[EMC_GATE_GRAY_NR(element)])
15241 return MP_NO_ACTION;
15243 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15245 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15246 return MP_NO_ACTION;
15248 else if (element == EL_DC_GATE_WHITE ||
15249 element == EL_DC_GATE_WHITE_GRAY ||
15250 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15252 if (player->num_white_keys == 0)
15253 return MP_NO_ACTION;
15255 player->num_white_keys--;
15257 else if (IS_SP_PORT(element))
15259 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15260 element == EL_SP_GRAVITY_PORT_RIGHT ||
15261 element == EL_SP_GRAVITY_PORT_UP ||
15262 element == EL_SP_GRAVITY_PORT_DOWN)
15263 #if USE_PLAYER_GRAVITY
15264 player->gravity = !player->gravity;
15266 game.gravity = !game.gravity;
15268 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15269 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15270 element == EL_SP_GRAVITY_ON_PORT_UP ||
15271 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15272 #if USE_PLAYER_GRAVITY
15273 player->gravity = TRUE;
15275 game.gravity = TRUE;
15277 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15278 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15279 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15280 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15281 #if USE_PLAYER_GRAVITY
15282 player->gravity = FALSE;
15284 game.gravity = FALSE;
15288 /* automatically move to the next field with double speed */
15289 player->programmed_action = move_direction;
15291 if (player->move_delay_reset_counter == 0)
15293 player->move_delay_reset_counter = 2; /* two double speed steps */
15295 DOUBLE_PLAYER_SPEED(player);
15298 PlayLevelSoundAction(x, y, ACTION_PASSING);
15300 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15304 if (mode != DF_SNAP)
15306 GfxElement[x][y] = GFX_ELEMENT(element);
15307 player->is_digging = TRUE;
15310 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15312 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15313 player->index_bit, dig_side);
15315 if (mode == DF_SNAP)
15317 #if USE_NEW_SNAP_DELAY
15318 if (level.block_snap_field)
15319 setFieldForSnapping(x, y, element, move_direction);
15321 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15323 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15326 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15327 player->index_bit, dig_side);
15330 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15334 if (is_player && mode != DF_SNAP)
15336 GfxElement[x][y] = element;
15337 player->is_collecting = TRUE;
15340 if (element == EL_SPEED_PILL)
15342 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15344 else if (element == EL_EXTRA_TIME && level.time > 0)
15346 TimeLeft += level.extra_time;
15349 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15351 DisplayGameControlValues();
15353 DrawGameValue_Time(TimeLeft);
15356 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15358 player->shield_normal_time_left += level.shield_normal_time;
15359 if (element == EL_SHIELD_DEADLY)
15360 player->shield_deadly_time_left += level.shield_deadly_time;
15362 else if (element == EL_DYNAMITE ||
15363 element == EL_EM_DYNAMITE ||
15364 element == EL_SP_DISK_RED)
15366 if (player->inventory_size < MAX_INVENTORY_SIZE)
15367 player->inventory_element[player->inventory_size++] = element;
15369 DrawGameDoorValues();
15371 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15373 player->dynabomb_count++;
15374 player->dynabombs_left++;
15376 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15378 player->dynabomb_size++;
15380 else if (element == EL_DYNABOMB_INCREASE_POWER)
15382 player->dynabomb_xl = TRUE;
15384 else if (IS_KEY(element))
15386 player->key[KEY_NR(element)] = TRUE;
15388 DrawGameDoorValues();
15390 else if (element == EL_DC_KEY_WHITE)
15392 player->num_white_keys++;
15394 /* display white keys? */
15395 /* DrawGameDoorValues(); */
15397 else if (IS_ENVELOPE(element))
15399 player->show_envelope = element;
15401 else if (element == EL_EMC_LENSES)
15403 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15405 RedrawAllInvisibleElementsForLenses();
15407 else if (element == EL_EMC_MAGNIFIER)
15409 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15411 RedrawAllInvisibleElementsForMagnifier();
15413 else if (IS_DROPPABLE(element) ||
15414 IS_THROWABLE(element)) /* can be collected and dropped */
15418 if (collect_count == 0)
15419 player->inventory_infinite_element = element;
15421 for (i = 0; i < collect_count; i++)
15422 if (player->inventory_size < MAX_INVENTORY_SIZE)
15423 player->inventory_element[player->inventory_size++] = element;
15425 DrawGameDoorValues();
15427 else if (collect_count > 0)
15429 local_player->gems_still_needed -= collect_count;
15430 if (local_player->gems_still_needed < 0)
15431 local_player->gems_still_needed = 0;
15434 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15436 DisplayGameControlValues();
15438 DrawGameValue_Emeralds(local_player->gems_still_needed);
15442 RaiseScoreElement(element);
15443 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15446 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15447 player->index_bit, dig_side);
15449 if (mode == DF_SNAP)
15451 #if USE_NEW_SNAP_DELAY
15452 if (level.block_snap_field)
15453 setFieldForSnapping(x, y, element, move_direction);
15455 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15457 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15460 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15461 player->index_bit, dig_side);
15464 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15466 if (mode == DF_SNAP && element != EL_BD_ROCK)
15467 return MP_NO_ACTION;
15469 if (CAN_FALL(element) && dy)
15470 return MP_NO_ACTION;
15472 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15473 !(element == EL_SPRING && level.use_spring_bug))
15474 return MP_NO_ACTION;
15476 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15477 ((move_direction & MV_VERTICAL &&
15478 ((element_info[element].move_pattern & MV_LEFT &&
15479 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15480 (element_info[element].move_pattern & MV_RIGHT &&
15481 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15482 (move_direction & MV_HORIZONTAL &&
15483 ((element_info[element].move_pattern & MV_UP &&
15484 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15485 (element_info[element].move_pattern & MV_DOWN &&
15486 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15487 return MP_NO_ACTION;
15489 /* do not push elements already moving away faster than player */
15490 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15491 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15492 return MP_NO_ACTION;
15494 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15496 if (player->push_delay_value == -1 || !player_was_pushing)
15497 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15499 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15501 if (player->push_delay_value == -1)
15502 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15504 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15506 if (!player->is_pushing)
15507 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15510 player->is_pushing = TRUE;
15511 player->is_active = TRUE;
15513 if (!(IN_LEV_FIELD(nextx, nexty) &&
15514 (IS_FREE(nextx, nexty) ||
15515 (IS_SB_ELEMENT(element) &&
15516 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15517 (IS_CUSTOM_ELEMENT(element) &&
15518 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15519 return MP_NO_ACTION;
15521 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15522 return MP_NO_ACTION;
15524 if (player->push_delay == -1) /* new pushing; restart delay */
15525 player->push_delay = 0;
15527 if (player->push_delay < player->push_delay_value &&
15528 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15529 element != EL_SPRING && element != EL_BALLOON)
15531 /* make sure that there is no move delay before next try to push */
15532 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15533 player->move_delay = 0;
15535 return MP_NO_ACTION;
15538 if (IS_CUSTOM_ELEMENT(element) &&
15539 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15541 if (!DigFieldByCE(nextx, nexty, element))
15542 return MP_NO_ACTION;
15545 if (IS_SB_ELEMENT(element))
15547 if (element == EL_SOKOBAN_FIELD_FULL)
15549 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15550 local_player->sokobanfields_still_needed++;
15553 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15555 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15556 local_player->sokobanfields_still_needed--;
15559 Feld[x][y] = EL_SOKOBAN_OBJECT;
15561 if (Back[x][y] == Back[nextx][nexty])
15562 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15563 else if (Back[x][y] != 0)
15564 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15567 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15571 if (local_player->sokobanfields_still_needed == 0 &&
15572 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15574 if (local_player->sokobanfields_still_needed == 0 &&
15575 game.emulation == EMU_SOKOBAN)
15578 PlayerWins(player);
15580 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15584 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15586 InitMovingField(x, y, move_direction);
15587 GfxAction[x][y] = ACTION_PUSHING;
15589 if (mode == DF_SNAP)
15590 ContinueMoving(x, y);
15592 MovPos[x][y] = (dx != 0 ? dx : dy);
15594 Pushed[x][y] = TRUE;
15595 Pushed[nextx][nexty] = TRUE;
15597 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15598 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15600 player->push_delay_value = -1; /* get new value later */
15602 /* check for element change _after_ element has been pushed */
15603 if (game.use_change_when_pushing_bug)
15605 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15606 player->index_bit, dig_side);
15607 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15608 player->index_bit, dig_side);
15611 else if (IS_SWITCHABLE(element))
15613 if (PLAYER_SWITCHING(player, x, y))
15615 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15616 player->index_bit, dig_side);
15621 player->is_switching = TRUE;
15622 player->switch_x = x;
15623 player->switch_y = y;
15625 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15627 if (element == EL_ROBOT_WHEEL)
15629 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15633 game.robot_wheel_active = TRUE;
15635 TEST_DrawLevelField(x, y);
15637 else if (element == EL_SP_TERMINAL)
15641 SCAN_PLAYFIELD(xx, yy)
15643 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15645 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15646 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15649 else if (IS_BELT_SWITCH(element))
15651 ToggleBeltSwitch(x, y);
15653 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15654 element == EL_SWITCHGATE_SWITCH_DOWN ||
15655 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15656 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15658 ToggleSwitchgateSwitch(x, y);
15660 else if (element == EL_LIGHT_SWITCH ||
15661 element == EL_LIGHT_SWITCH_ACTIVE)
15663 ToggleLightSwitch(x, y);
15665 else if (element == EL_TIMEGATE_SWITCH ||
15666 element == EL_DC_TIMEGATE_SWITCH)
15668 ActivateTimegateSwitch(x, y);
15670 else if (element == EL_BALLOON_SWITCH_LEFT ||
15671 element == EL_BALLOON_SWITCH_RIGHT ||
15672 element == EL_BALLOON_SWITCH_UP ||
15673 element == EL_BALLOON_SWITCH_DOWN ||
15674 element == EL_BALLOON_SWITCH_NONE ||
15675 element == EL_BALLOON_SWITCH_ANY)
15677 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15678 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15679 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15680 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15681 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15684 else if (element == EL_LAMP)
15686 Feld[x][y] = EL_LAMP_ACTIVE;
15687 local_player->lights_still_needed--;
15689 ResetGfxAnimation(x, y);
15690 TEST_DrawLevelField(x, y);
15692 else if (element == EL_TIME_ORB_FULL)
15694 Feld[x][y] = EL_TIME_ORB_EMPTY;
15696 if (level.time > 0 || level.use_time_orb_bug)
15698 TimeLeft += level.time_orb_time;
15699 game.no_time_limit = FALSE;
15702 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15704 DisplayGameControlValues();
15706 DrawGameValue_Time(TimeLeft);
15710 ResetGfxAnimation(x, y);
15711 TEST_DrawLevelField(x, y);
15713 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15714 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15718 game.ball_state = !game.ball_state;
15720 SCAN_PLAYFIELD(xx, yy)
15722 int e = Feld[xx][yy];
15724 if (game.ball_state)
15726 if (e == EL_EMC_MAGIC_BALL)
15727 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15728 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15729 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15733 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15734 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15735 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15736 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15741 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15742 player->index_bit, dig_side);
15744 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15745 player->index_bit, dig_side);
15747 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15748 player->index_bit, dig_side);
15754 if (!PLAYER_SWITCHING(player, x, y))
15756 player->is_switching = TRUE;
15757 player->switch_x = x;
15758 player->switch_y = y;
15760 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15761 player->index_bit, dig_side);
15762 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15763 player->index_bit, dig_side);
15765 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15766 player->index_bit, dig_side);
15767 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15768 player->index_bit, dig_side);
15771 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15772 player->index_bit, dig_side);
15773 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15774 player->index_bit, dig_side);
15776 return MP_NO_ACTION;
15779 player->push_delay = -1;
15781 if (is_player) /* function can also be called by EL_PENGUIN */
15783 if (Feld[x][y] != element) /* really digged/collected something */
15785 player->is_collecting = !player->is_digging;
15786 player->is_active = TRUE;
15793 static boolean DigFieldByCE(int x, int y, int digging_element)
15795 int element = Feld[x][y];
15797 if (!IS_FREE(x, y))
15799 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15800 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15803 /* no element can dig solid indestructible elements */
15804 if (IS_INDESTRUCTIBLE(element) &&
15805 !IS_DIGGABLE(element) &&
15806 !IS_COLLECTIBLE(element))
15809 if (AmoebaNr[x][y] &&
15810 (element == EL_AMOEBA_FULL ||
15811 element == EL_BD_AMOEBA ||
15812 element == EL_AMOEBA_GROWING))
15814 AmoebaCnt[AmoebaNr[x][y]]--;
15815 AmoebaCnt2[AmoebaNr[x][y]]--;
15818 if (IS_MOVING(x, y))
15819 RemoveMovingField(x, y);
15823 TEST_DrawLevelField(x, y);
15826 /* if digged element was about to explode, prevent the explosion */
15827 ExplodeField[x][y] = EX_TYPE_NONE;
15829 PlayLevelSoundAction(x, y, action);
15832 Store[x][y] = EL_EMPTY;
15835 /* this makes it possible to leave the removed element again */
15836 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15837 Store[x][y] = element;
15839 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15841 int move_leave_element = element_info[digging_element].move_leave_element;
15843 /* this makes it possible to leave the removed element again */
15844 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15845 element : move_leave_element);
15852 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15854 int jx = player->jx, jy = player->jy;
15855 int x = jx + dx, y = jy + dy;
15856 int snap_direction = (dx == -1 ? MV_LEFT :
15857 dx == +1 ? MV_RIGHT :
15859 dy == +1 ? MV_DOWN : MV_NONE);
15860 boolean can_continue_snapping = (level.continuous_snapping &&
15861 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15863 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15866 if (!player->active || !IN_LEV_FIELD(x, y))
15874 if (player->MovPos == 0)
15875 player->is_pushing = FALSE;
15877 player->is_snapping = FALSE;
15879 if (player->MovPos == 0)
15881 player->is_moving = FALSE;
15882 player->is_digging = FALSE;
15883 player->is_collecting = FALSE;
15889 #if USE_NEW_CONTINUOUS_SNAPPING
15890 /* prevent snapping with already pressed snap key when not allowed */
15891 if (player->is_snapping && !can_continue_snapping)
15894 if (player->is_snapping)
15898 player->MovDir = snap_direction;
15900 if (player->MovPos == 0)
15902 player->is_moving = FALSE;
15903 player->is_digging = FALSE;
15904 player->is_collecting = FALSE;
15907 player->is_dropping = FALSE;
15908 player->is_dropping_pressed = FALSE;
15909 player->drop_pressed_delay = 0;
15911 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15914 player->is_snapping = TRUE;
15915 player->is_active = TRUE;
15917 if (player->MovPos == 0)
15919 player->is_moving = FALSE;
15920 player->is_digging = FALSE;
15921 player->is_collecting = FALSE;
15924 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15925 TEST_DrawLevelField(player->last_jx, player->last_jy);
15927 TEST_DrawLevelField(x, y);
15932 static boolean DropElement(struct PlayerInfo *player)
15934 int old_element, new_element;
15935 int dropx = player->jx, dropy = player->jy;
15936 int drop_direction = player->MovDir;
15937 int drop_side = drop_direction;
15939 int drop_element = get_next_dropped_element(player);
15941 int drop_element = (player->inventory_size > 0 ?
15942 player->inventory_element[player->inventory_size - 1] :
15943 player->inventory_infinite_element != EL_UNDEFINED ?
15944 player->inventory_infinite_element :
15945 player->dynabombs_left > 0 ?
15946 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15950 player->is_dropping_pressed = TRUE;
15952 /* do not drop an element on top of another element; when holding drop key
15953 pressed without moving, dropped element must move away before the next
15954 element can be dropped (this is especially important if the next element
15955 is dynamite, which can be placed on background for historical reasons) */
15956 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15959 if (IS_THROWABLE(drop_element))
15961 dropx += GET_DX_FROM_DIR(drop_direction);
15962 dropy += GET_DY_FROM_DIR(drop_direction);
15964 if (!IN_LEV_FIELD(dropx, dropy))
15968 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15969 new_element = drop_element; /* default: no change when dropping */
15971 /* check if player is active, not moving and ready to drop */
15972 if (!player->active || player->MovPos || player->drop_delay > 0)
15975 /* check if player has anything that can be dropped */
15976 if (new_element == EL_UNDEFINED)
15979 /* check if drop key was pressed long enough for EM style dynamite */
15980 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15983 /* check if anything can be dropped at the current position */
15984 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15987 /* collected custom elements can only be dropped on empty fields */
15988 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15991 if (old_element != EL_EMPTY)
15992 Back[dropx][dropy] = old_element; /* store old element on this field */
15994 ResetGfxAnimation(dropx, dropy);
15995 ResetRandomAnimationValue(dropx, dropy);
15997 if (player->inventory_size > 0 ||
15998 player->inventory_infinite_element != EL_UNDEFINED)
16000 if (player->inventory_size > 0)
16002 player->inventory_size--;
16004 DrawGameDoorValues();
16006 if (new_element == EL_DYNAMITE)
16007 new_element = EL_DYNAMITE_ACTIVE;
16008 else if (new_element == EL_EM_DYNAMITE)
16009 new_element = EL_EM_DYNAMITE_ACTIVE;
16010 else if (new_element == EL_SP_DISK_RED)
16011 new_element = EL_SP_DISK_RED_ACTIVE;
16014 Feld[dropx][dropy] = new_element;
16016 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16017 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16018 el2img(Feld[dropx][dropy]), 0);
16020 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16022 /* needed if previous element just changed to "empty" in the last frame */
16023 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16025 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16026 player->index_bit, drop_side);
16027 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16029 player->index_bit, drop_side);
16031 TestIfElementTouchesCustomElement(dropx, dropy);
16033 else /* player is dropping a dyna bomb */
16035 player->dynabombs_left--;
16037 Feld[dropx][dropy] = new_element;
16039 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16040 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16041 el2img(Feld[dropx][dropy]), 0);
16043 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16046 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16047 InitField_WithBug1(dropx, dropy, FALSE);
16049 new_element = Feld[dropx][dropy]; /* element might have changed */
16051 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16052 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16055 int move_direction;
16059 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16060 MovDir[dropx][dropy] = drop_direction;
16063 move_direction = MovDir[dropx][dropy];
16064 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16065 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16068 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16070 #if USE_FIX_IMPACT_COLLISION
16071 /* do not cause impact style collision by dropping elements that can fall */
16072 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16074 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16078 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16079 player->is_dropping = TRUE;
16081 player->drop_pressed_delay = 0;
16082 player->is_dropping_pressed = FALSE;
16084 player->drop_x = dropx;
16085 player->drop_y = dropy;
16090 /* ------------------------------------------------------------------------- */
16091 /* game sound playing functions */
16092 /* ------------------------------------------------------------------------- */
16094 static int *loop_sound_frame = NULL;
16095 static int *loop_sound_volume = NULL;
16097 void InitPlayLevelSound()
16099 int num_sounds = getSoundListSize();
16101 checked_free(loop_sound_frame);
16102 checked_free(loop_sound_volume);
16104 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16105 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16108 static void PlayLevelSound(int x, int y, int nr)
16110 int sx = SCREENX(x), sy = SCREENY(y);
16111 int volume, stereo_position;
16112 int max_distance = 8;
16113 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16115 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16116 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16119 if (!IN_LEV_FIELD(x, y) ||
16120 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16121 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16124 volume = SOUND_MAX_VOLUME;
16126 if (!IN_SCR_FIELD(sx, sy))
16128 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16129 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16131 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16134 stereo_position = (SOUND_MAX_LEFT +
16135 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16136 (SCR_FIELDX + 2 * max_distance));
16138 if (IS_LOOP_SOUND(nr))
16140 /* This assures that quieter loop sounds do not overwrite louder ones,
16141 while restarting sound volume comparison with each new game frame. */
16143 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16146 loop_sound_volume[nr] = volume;
16147 loop_sound_frame[nr] = FrameCounter;
16150 PlaySoundExt(nr, volume, stereo_position, type);
16153 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16155 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16156 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16157 y < LEVELY(BY1) ? LEVELY(BY1) :
16158 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16162 static void PlayLevelSoundAction(int x, int y, int action)
16164 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16167 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16169 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16171 if (sound_effect != SND_UNDEFINED)
16172 PlayLevelSound(x, y, sound_effect);
16175 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16178 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16180 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16181 PlayLevelSound(x, y, sound_effect);
16184 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16186 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16188 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16189 PlayLevelSound(x, y, sound_effect);
16192 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16194 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16196 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16197 StopSound(sound_effect);
16200 static void PlayLevelMusic()
16202 if (levelset.music[level_nr] != MUS_UNDEFINED)
16203 PlayMusic(levelset.music[level_nr]); /* from config file */
16205 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16208 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16210 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16211 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16212 int x = xx - 1 - offset;
16213 int y = yy - 1 - offset;
16218 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16222 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16226 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16230 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16234 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16238 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16242 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16245 case SAMPLE_android_clone:
16246 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16249 case SAMPLE_android_move:
16250 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16253 case SAMPLE_spring:
16254 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16258 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16262 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16265 case SAMPLE_eater_eat:
16266 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16270 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16273 case SAMPLE_collect:
16274 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16277 case SAMPLE_diamond:
16278 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16281 case SAMPLE_squash:
16282 /* !!! CHECK THIS !!! */
16284 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16286 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16290 case SAMPLE_wonderfall:
16291 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16295 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16299 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16303 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16307 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16311 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16315 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16318 case SAMPLE_wonder:
16319 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16323 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16326 case SAMPLE_exit_open:
16327 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16330 case SAMPLE_exit_leave:
16331 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16334 case SAMPLE_dynamite:
16335 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16339 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16343 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16347 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16351 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16355 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16359 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16363 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16368 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16370 int element = map_element_SP_to_RND(element_sp);
16371 int action = map_action_SP_to_RND(action_sp);
16372 int offset = (setup.sp_show_border_elements ? 0 : 1);
16373 int x = xx - offset;
16374 int y = yy - offset;
16377 printf("::: %d -> %d\n", element_sp, action_sp);
16380 PlayLevelSoundElementAction(x, y, element, action);
16383 void RaiseScore(int value)
16385 local_player->score += value;
16388 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16390 DisplayGameControlValues();
16392 DrawGameValue_Score(local_player->score);
16396 void RaiseScoreElement(int element)
16401 case EL_BD_DIAMOND:
16402 case EL_EMERALD_YELLOW:
16403 case EL_EMERALD_RED:
16404 case EL_EMERALD_PURPLE:
16405 case EL_SP_INFOTRON:
16406 RaiseScore(level.score[SC_EMERALD]);
16409 RaiseScore(level.score[SC_DIAMOND]);
16412 RaiseScore(level.score[SC_CRYSTAL]);
16415 RaiseScore(level.score[SC_PEARL]);
16418 case EL_BD_BUTTERFLY:
16419 case EL_SP_ELECTRON:
16420 RaiseScore(level.score[SC_BUG]);
16423 case EL_BD_FIREFLY:
16424 case EL_SP_SNIKSNAK:
16425 RaiseScore(level.score[SC_SPACESHIP]);
16428 case EL_DARK_YAMYAM:
16429 RaiseScore(level.score[SC_YAMYAM]);
16432 RaiseScore(level.score[SC_ROBOT]);
16435 RaiseScore(level.score[SC_PACMAN]);
16438 RaiseScore(level.score[SC_NUT]);
16441 case EL_EM_DYNAMITE:
16442 case EL_SP_DISK_RED:
16443 case EL_DYNABOMB_INCREASE_NUMBER:
16444 case EL_DYNABOMB_INCREASE_SIZE:
16445 case EL_DYNABOMB_INCREASE_POWER:
16446 RaiseScore(level.score[SC_DYNAMITE]);
16448 case EL_SHIELD_NORMAL:
16449 case EL_SHIELD_DEADLY:
16450 RaiseScore(level.score[SC_SHIELD]);
16452 case EL_EXTRA_TIME:
16453 RaiseScore(level.extra_time_score);
16467 case EL_DC_KEY_WHITE:
16468 RaiseScore(level.score[SC_KEY]);
16471 RaiseScore(element_info[element].collect_score);
16476 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16478 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16481 /* closing door required in case of envelope style request dialogs */
16483 CloseDoor(DOOR_CLOSE_1);
16486 #if defined(NETWORK_AVALIABLE)
16487 if (options.network)
16488 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16497 FadeSkipNextFadeIn();
16499 fading = fading_none;
16503 OpenDoor(DOOR_CLOSE_1);
16506 game_status = GAME_MODE_MAIN;
16509 DrawAndFadeInMainMenu(REDRAW_FIELD);
16517 FadeOut(REDRAW_FIELD);
16520 game_status = GAME_MODE_MAIN;
16522 DrawAndFadeInMainMenu(REDRAW_FIELD);
16526 else /* continue playing the game */
16528 if (tape.playing && tape.deactivate_display)
16529 TapeDeactivateDisplayOff(TRUE);
16531 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16533 if (tape.playing && tape.deactivate_display)
16534 TapeDeactivateDisplayOn();
16538 void RequestQuitGame(boolean ask_if_really_quit)
16540 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16541 boolean skip_request = AllPlayersGone || quick_quit;
16543 RequestQuitGameExt(skip_request, quick_quit,
16544 "Do you really want to quit the game?");
16548 /* ------------------------------------------------------------------------- */
16549 /* random generator functions */
16550 /* ------------------------------------------------------------------------- */
16552 unsigned int InitEngineRandom_RND(int seed)
16554 game.num_random_calls = 0;
16557 unsigned int rnd_seed = InitEngineRandom(seed);
16559 printf("::: START RND: %d\n", rnd_seed);
16564 return InitEngineRandom(seed);
16570 unsigned int RND(int max)
16574 game.num_random_calls++;
16576 return GetEngineRandom(max);
16583 /* ------------------------------------------------------------------------- */
16584 /* game engine snapshot handling functions */
16585 /* ------------------------------------------------------------------------- */
16587 struct EngineSnapshotInfo
16589 /* runtime values for custom element collect score */
16590 int collect_score[NUM_CUSTOM_ELEMENTS];
16592 /* runtime values for group element choice position */
16593 int choice_pos[NUM_GROUP_ELEMENTS];
16595 /* runtime values for belt position animations */
16596 int belt_graphic[4][NUM_BELT_PARTS];
16597 int belt_anim_mode[4][NUM_BELT_PARTS];
16600 static struct EngineSnapshotInfo engine_snapshot_rnd;
16601 static char *snapshot_level_identifier = NULL;
16602 static int snapshot_level_nr = -1;
16604 static void SaveEngineSnapshotValues_RND()
16606 static int belt_base_active_element[4] =
16608 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16609 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16610 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16611 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16615 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16617 int element = EL_CUSTOM_START + i;
16619 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16622 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16624 int element = EL_GROUP_START + i;
16626 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16629 for (i = 0; i < 4; i++)
16631 for (j = 0; j < NUM_BELT_PARTS; j++)
16633 int element = belt_base_active_element[i] + j;
16634 int graphic = el2img(element);
16635 int anim_mode = graphic_info[graphic].anim_mode;
16637 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16638 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16643 static void LoadEngineSnapshotValues_RND()
16645 unsigned int num_random_calls = game.num_random_calls;
16648 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16650 int element = EL_CUSTOM_START + i;
16652 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16655 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16657 int element = EL_GROUP_START + i;
16659 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16662 for (i = 0; i < 4; i++)
16664 for (j = 0; j < NUM_BELT_PARTS; j++)
16666 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16667 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16669 graphic_info[graphic].anim_mode = anim_mode;
16673 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16675 InitRND(tape.random_seed);
16676 for (i = 0; i < num_random_calls; i++)
16680 if (game.num_random_calls != num_random_calls)
16682 Error(ERR_INFO, "number of random calls out of sync");
16683 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16684 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16685 Error(ERR_EXIT, "this should not happen -- please debug");
16689 void SaveEngineSnapshot()
16691 /* do not save snapshots from editor */
16692 if (level_editor_test_game)
16695 /* free previous snapshot buffers, if needed */
16696 FreeEngineSnapshotBuffers();
16698 /* copy some special values to a structure better suited for the snapshot */
16700 SaveEngineSnapshotValues_RND();
16701 SaveEngineSnapshotValues_EM();
16702 SaveEngineSnapshotValues_SP();
16704 /* save values stored in special snapshot structure */
16706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16710 /* save further RND engine values */
16712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16721 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16723 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16745 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16748 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16749 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16752 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16754 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16764 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16767 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16768 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16769 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16770 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16771 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16773 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16774 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16776 /* save level identification information */
16778 setString(&snapshot_level_identifier, leveldir_current->identifier);
16779 snapshot_level_nr = level_nr;
16782 ListNode *node = engine_snapshot_list_rnd;
16785 while (node != NULL)
16787 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16792 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16796 void LoadEngineSnapshot()
16798 /* restore generically stored snapshot buffers */
16800 LoadEngineSnapshotBuffers();
16802 /* restore special values from snapshot structure */
16804 LoadEngineSnapshotValues_RND();
16805 LoadEngineSnapshotValues_EM();
16806 LoadEngineSnapshotValues_SP();
16809 boolean CheckEngineSnapshot()
16811 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16812 snapshot_level_nr == level_nr);
16816 /* ---------- new game button stuff ---------------------------------------- */
16824 } gamebutton_info[NUM_GAME_BUTTONS] =
16827 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16828 GAME_CTRL_ID_STOP, "stop game"
16831 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16832 GAME_CTRL_ID_PAUSE, "pause game"
16835 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16836 GAME_CTRL_ID_PLAY, "play game"
16839 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16840 SOUND_CTRL_ID_MUSIC, "background music on/off"
16843 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16844 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16847 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16848 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16851 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
16852 GAME_CTRL_ID_SAVE, "save game"
16855 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
16856 GAME_CTRL_ID_LOAD, "load game"
16860 void CreateGameButtons()
16864 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16866 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16867 struct Rect *pos = gamebutton_info[i].pos;
16868 struct GadgetInfo *gi;
16871 unsigned int event_mask;
16872 int base_x = (tape.show_game_buttons ? VX : DX);
16873 int base_y = (tape.show_game_buttons ? VY : DY);
16874 int gd_x = gfx->src_x;
16875 int gd_y = gfx->src_y;
16876 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16877 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16878 int gd_xa = gfx->src_x + gfx->active_xoffset;
16879 int gd_ya = gfx->src_y + gfx->active_yoffset;
16880 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16881 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16884 if (gfx->bitmap == NULL)
16886 game_gadget[id] = NULL;
16891 if (id == GAME_CTRL_ID_STOP ||
16892 id == GAME_CTRL_ID_PAUSE ||
16893 id == GAME_CTRL_ID_PLAY ||
16894 id == GAME_CTRL_ID_SAVE ||
16895 id == GAME_CTRL_ID_LOAD)
16897 button_type = GD_TYPE_NORMAL_BUTTON;
16899 event_mask = GD_EVENT_RELEASED;
16903 button_type = GD_TYPE_CHECK_BUTTON;
16905 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16906 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16907 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16908 event_mask = GD_EVENT_PRESSED;
16911 gi = CreateGadget(GDI_CUSTOM_ID, id,
16912 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16913 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
16914 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
16915 GDI_WIDTH, gfx->width,
16916 GDI_HEIGHT, gfx->height,
16917 GDI_TYPE, button_type,
16918 GDI_STATE, GD_BUTTON_UNPRESSED,
16919 GDI_CHECKED, checked,
16920 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16921 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16922 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16923 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16924 GDI_DIRECT_DRAW, FALSE,
16925 GDI_EVENT_MASK, event_mask,
16926 GDI_CALLBACK_ACTION, HandleGameButtons,
16930 Error(ERR_EXIT, "cannot create gadget");
16932 game_gadget[id] = gi;
16936 void FreeGameButtons()
16940 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16941 FreeGadget(game_gadget[i]);
16944 void MapGameButtons()
16948 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16949 MapGadget(game_gadget[i]);
16952 void UnmapGameButtons()
16956 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16957 UnmapGadget(game_gadget[i]);
16960 void RedrawGameButtons()
16964 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16965 RedrawGadget(game_gadget[i]);
16968 static void HandleGameButtonsExt(int id)
16970 boolean handle_game_buttons =
16971 (game_status == GAME_MODE_PLAYING ||
16972 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16974 if (!handle_game_buttons)
16979 case GAME_CTRL_ID_STOP:
16980 if (game_status == GAME_MODE_MAIN)
16986 RequestQuitGame(TRUE);
16990 case GAME_CTRL_ID_PAUSE:
16991 if (options.network && game_status == GAME_MODE_PLAYING)
16993 #if defined(NETWORK_AVALIABLE)
16995 SendToServer_ContinuePlaying();
16997 SendToServer_PausePlaying();
17001 TapeTogglePause(TAPE_TOGGLE_MANUAL);
17004 case GAME_CTRL_ID_PLAY:
17005 if (game_status == GAME_MODE_MAIN)
17007 StartGameActions(options.network, setup.autorecord, level.random_seed);
17009 else if (tape.pausing)
17011 #if defined(NETWORK_AVALIABLE)
17012 if (options.network)
17013 SendToServer_ContinuePlaying();
17017 tape.pausing = FALSE;
17018 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17023 case SOUND_CTRL_ID_MUSIC:
17024 if (setup.sound_music)
17026 setup.sound_music = FALSE;
17030 else if (audio.music_available)
17032 setup.sound = setup.sound_music = TRUE;
17034 SetAudioMode(setup.sound);
17040 case SOUND_CTRL_ID_LOOPS:
17041 if (setup.sound_loops)
17042 setup.sound_loops = FALSE;
17043 else if (audio.loops_available)
17045 setup.sound = setup.sound_loops = TRUE;
17047 SetAudioMode(setup.sound);
17051 case SOUND_CTRL_ID_SIMPLE:
17052 if (setup.sound_simple)
17053 setup.sound_simple = FALSE;
17054 else if (audio.sound_available)
17056 setup.sound = setup.sound_simple = TRUE;
17058 SetAudioMode(setup.sound);
17062 case GAME_CTRL_ID_SAVE:
17066 case GAME_CTRL_ID_LOAD:
17075 static void HandleGameButtons(struct GadgetInfo *gi)
17077 HandleGameButtonsExt(gi->custom_id);
17080 void HandleSoundButtonKeys(Key key)
17083 if (key == setup.shortcut.sound_simple)
17084 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17085 else if (key == setup.shortcut.sound_loops)
17086 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17087 else if (key == setup.shortcut.sound_music)
17088 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17090 if (key == setup.shortcut.sound_simple)
17091 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17092 else if (key == setup.shortcut.sound_loops)
17093 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17094 else if (key == setup.shortcut.sound_music)
17095 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);