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;
3537 game_status = GAME_MODE_PLAYING;
3544 if (!game.restart_level)
3545 CloseDoor(DOOR_CLOSE_1);
3548 if (level_editor_test_game)
3549 FadeSkipNextFadeIn();
3551 FadeSetEnterScreen();
3553 if (level_editor_test_game)
3554 fading = fading_none;
3556 fading = menu.destination;
3560 FadeOut(REDRAW_FIELD);
3563 FadeOut(REDRAW_FIELD);
3569 printf("::: FADING OUT: DONE\n");
3574 game_status = GAME_MODE_PLAYING;
3578 /* needed if different viewport properties defined for playing */
3579 ChangeViewportPropertiesIfNeeded();
3583 DrawCompleteVideoDisplay();
3587 InitGameControlValues();
3589 /* don't play tapes over network */
3590 network_playing = (options.network && !tape.playing);
3592 for (i = 0; i < MAX_PLAYERS; i++)
3594 struct PlayerInfo *player = &stored_player[i];
3596 player->index_nr = i;
3597 player->index_bit = (1 << i);
3598 player->element_nr = EL_PLAYER_1 + i;
3600 player->present = FALSE;
3601 player->active = FALSE;
3602 player->mapped = FALSE;
3604 player->killed = FALSE;
3605 player->reanimated = FALSE;
3608 player->effective_action = 0;
3609 player->programmed_action = 0;
3612 player->score_final = 0;
3614 player->gems_still_needed = level.gems_needed;
3615 player->sokobanfields_still_needed = 0;
3616 player->lights_still_needed = 0;
3617 player->friends_still_needed = 0;
3619 for (j = 0; j < MAX_NUM_KEYS; j++)
3620 player->key[j] = FALSE;
3622 player->num_white_keys = 0;
3624 player->dynabomb_count = 0;
3625 player->dynabomb_size = 1;
3626 player->dynabombs_left = 0;
3627 player->dynabomb_xl = FALSE;
3629 player->MovDir = initial_move_dir;
3632 player->GfxDir = initial_move_dir;
3633 player->GfxAction = ACTION_DEFAULT;
3635 player->StepFrame = 0;
3637 player->initial_element = player->element_nr;
3638 player->artwork_element =
3639 (level.use_artwork_element[i] ? level.artwork_element[i] :
3640 player->element_nr);
3641 player->use_murphy = FALSE;
3643 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3644 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3646 player->gravity = level.initial_player_gravity[i];
3648 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3650 player->actual_frame_counter = 0;
3652 player->step_counter = 0;
3654 player->last_move_dir = initial_move_dir;
3656 player->is_active = FALSE;
3658 player->is_waiting = FALSE;
3659 player->is_moving = FALSE;
3660 player->is_auto_moving = FALSE;
3661 player->is_digging = FALSE;
3662 player->is_snapping = FALSE;
3663 player->is_collecting = FALSE;
3664 player->is_pushing = FALSE;
3665 player->is_switching = FALSE;
3666 player->is_dropping = FALSE;
3667 player->is_dropping_pressed = FALSE;
3669 player->is_bored = FALSE;
3670 player->is_sleeping = FALSE;
3672 player->frame_counter_bored = -1;
3673 player->frame_counter_sleeping = -1;
3675 player->anim_delay_counter = 0;
3676 player->post_delay_counter = 0;
3678 player->dir_waiting = initial_move_dir;
3679 player->action_waiting = ACTION_DEFAULT;
3680 player->last_action_waiting = ACTION_DEFAULT;
3681 player->special_action_bored = ACTION_DEFAULT;
3682 player->special_action_sleeping = ACTION_DEFAULT;
3684 player->switch_x = -1;
3685 player->switch_y = -1;
3687 player->drop_x = -1;
3688 player->drop_y = -1;
3690 player->show_envelope = 0;
3692 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3694 player->push_delay = -1; /* initialized when pushing starts */
3695 player->push_delay_value = game.initial_push_delay_value;
3697 player->drop_delay = 0;
3698 player->drop_pressed_delay = 0;
3700 player->last_jx = -1;
3701 player->last_jy = -1;
3705 player->shield_normal_time_left = 0;
3706 player->shield_deadly_time_left = 0;
3708 player->inventory_infinite_element = EL_UNDEFINED;
3709 player->inventory_size = 0;
3711 if (level.use_initial_inventory[i])
3713 for (j = 0; j < level.initial_inventory_size[i]; j++)
3715 int element = level.initial_inventory_content[i][j];
3716 int collect_count = element_info[element].collect_count_initial;
3719 if (!IS_CUSTOM_ELEMENT(element))
3722 if (collect_count == 0)
3723 player->inventory_infinite_element = element;
3725 for (k = 0; k < collect_count; k++)
3726 if (player->inventory_size < MAX_INVENTORY_SIZE)
3727 player->inventory_element[player->inventory_size++] = element;
3731 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3732 SnapField(player, 0, 0);
3734 player->LevelSolved = FALSE;
3735 player->GameOver = FALSE;
3737 player->LevelSolved_GameWon = FALSE;
3738 player->LevelSolved_GameEnd = FALSE;
3739 player->LevelSolved_PanelOff = FALSE;
3740 player->LevelSolved_SaveTape = FALSE;
3741 player->LevelSolved_SaveScore = FALSE;
3742 player->LevelSolved_CountingTime = 0;
3743 player->LevelSolved_CountingScore = 0;
3745 map_player_action[i] = i;
3748 network_player_action_received = FALSE;
3750 #if defined(NETWORK_AVALIABLE)
3751 /* initial null action */
3752 if (network_playing)
3753 SendToServer_MovePlayer(MV_NONE);
3762 TimeLeft = level.time;
3765 ScreenMovDir = MV_NONE;
3769 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3771 AllPlayersGone = FALSE;
3773 game.no_time_limit = (level.time == 0);
3775 game.yamyam_content_nr = 0;
3776 game.robot_wheel_active = FALSE;
3777 game.magic_wall_active = FALSE;
3778 game.magic_wall_time_left = 0;
3779 game.light_time_left = 0;
3780 game.timegate_time_left = 0;
3781 game.switchgate_pos = 0;
3782 game.wind_direction = level.wind_direction_initial;
3784 #if !USE_PLAYER_GRAVITY
3785 game.gravity = FALSE;
3786 game.explosions_delayed = TRUE;
3789 game.lenses_time_left = 0;
3790 game.magnify_time_left = 0;
3792 game.ball_state = level.ball_state_initial;
3793 game.ball_content_nr = 0;
3795 game.envelope_active = FALSE;
3797 /* set focus to local player for network games, else to all players */
3798 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3799 game.centered_player_nr_next = game.centered_player_nr;
3800 game.set_centered_player = FALSE;
3802 if (network_playing && tape.recording)
3804 /* store client dependent player focus when recording network games */
3805 tape.centered_player_nr_next = game.centered_player_nr_next;
3806 tape.set_centered_player = TRUE;
3809 for (i = 0; i < NUM_BELTS; i++)
3811 game.belt_dir[i] = MV_NONE;
3812 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3815 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3816 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3818 #if DEBUG_INIT_PLAYER
3821 printf("Player status at level initialization:\n");
3825 SCAN_PLAYFIELD(x, y)
3827 Feld[x][y] = level.field[x][y];
3828 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3829 ChangeDelay[x][y] = 0;
3830 ChangePage[x][y] = -1;
3831 #if USE_NEW_CUSTOM_VALUE
3832 CustomValue[x][y] = 0; /* initialized in InitField() */
3834 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3836 WasJustMoving[x][y] = 0;
3837 WasJustFalling[x][y] = 0;
3838 CheckCollision[x][y] = 0;
3839 CheckImpact[x][y] = 0;
3841 Pushed[x][y] = FALSE;
3843 ChangeCount[x][y] = 0;
3844 ChangeEvent[x][y] = -1;
3846 ExplodePhase[x][y] = 0;
3847 ExplodeDelay[x][y] = 0;
3848 ExplodeField[x][y] = EX_TYPE_NONE;
3850 RunnerVisit[x][y] = 0;
3851 PlayerVisit[x][y] = 0;
3854 GfxRandom[x][y] = INIT_GFX_RANDOM();
3855 GfxElement[x][y] = EL_UNDEFINED;
3856 GfxAction[x][y] = ACTION_DEFAULT;
3857 GfxDir[x][y] = MV_NONE;
3858 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3861 SCAN_PLAYFIELD(x, y)
3863 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3865 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3867 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3870 InitField(x, y, TRUE);
3872 ResetGfxAnimation(x, y);
3877 for (i = 0; i < MAX_PLAYERS; i++)
3879 struct PlayerInfo *player = &stored_player[i];
3881 /* set number of special actions for bored and sleeping animation */
3882 player->num_special_action_bored =
3883 get_num_special_action(player->artwork_element,
3884 ACTION_BORING_1, ACTION_BORING_LAST);
3885 player->num_special_action_sleeping =
3886 get_num_special_action(player->artwork_element,
3887 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3890 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3891 emulate_sb ? EMU_SOKOBAN :
3892 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3894 #if USE_NEW_ALL_SLIPPERY
3895 /* initialize type of slippery elements */
3896 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3898 if (!IS_CUSTOM_ELEMENT(i))
3900 /* default: elements slip down either to the left or right randomly */
3901 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3903 /* SP style elements prefer to slip down on the left side */
3904 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3905 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3907 /* BD style elements prefer to slip down on the left side */
3908 if (game.emulation == EMU_BOULDERDASH)
3909 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3914 /* initialize explosion and ignition delay */
3915 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3917 if (!IS_CUSTOM_ELEMENT(i))
3920 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3921 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3922 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3923 int last_phase = (num_phase + 1) * delay;
3924 int half_phase = (num_phase / 2) * delay;
3926 element_info[i].explosion_delay = last_phase - 1;
3927 element_info[i].ignition_delay = half_phase;
3929 if (i == EL_BLACK_ORB)
3930 element_info[i].ignition_delay = 1;
3934 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3935 element_info[i].explosion_delay = 1;
3937 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3938 element_info[i].ignition_delay = 1;
3942 /* correct non-moving belts to start moving left */
3943 for (i = 0; i < NUM_BELTS; i++)
3944 if (game.belt_dir[i] == MV_NONE)
3945 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3947 #if USE_NEW_PLAYER_ASSIGNMENTS
3948 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3949 /* choose default local player */
3950 local_player = &stored_player[0];
3952 for (i = 0; i < MAX_PLAYERS; i++)
3953 stored_player[i].connected = FALSE;
3955 local_player->connected = TRUE;
3956 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3959 printf("::: TEAM MODE: %d\n", game.team_mode);
3965 for (i = 0; i < MAX_PLAYERS; i++)
3966 stored_player[i].connected = tape.player_participates[i];
3968 /* try to guess locally connected team mode players (needed for correct
3969 assignment of player figures from level to locally playing players) */
3971 for (i = 0; i < MAX_PLAYERS; i++)
3972 if (tape.player_participates[i])
3973 stored_player[i].connected = TRUE;
3976 else if (game.team_mode && !options.network)
3978 /* try to guess locally connected team mode players (needed for correct
3979 assignment of player figures from level to locally playing players) */
3981 for (i = 0; i < MAX_PLAYERS; i++)
3982 if (setup.input[i].use_joystick ||
3983 setup.input[i].key.left != KSYM_UNDEFINED)
3984 stored_player[i].connected = TRUE;
3987 #if DEBUG_INIT_PLAYER
3990 printf("Player status after level initialization:\n");
3992 for (i = 0; i < MAX_PLAYERS; i++)
3994 struct PlayerInfo *player = &stored_player[i];
3996 printf("- player %d: present == %d, connected == %d, active == %d",
4002 if (local_player == player)
4003 printf(" (local player)");
4010 #if DEBUG_INIT_PLAYER
4012 printf("Reassigning players ...\n");
4015 /* check if any connected player was not found in playfield */
4016 for (i = 0; i < MAX_PLAYERS; i++)
4018 struct PlayerInfo *player = &stored_player[i];
4020 if (player->connected && !player->present)
4022 struct PlayerInfo *field_player = NULL;
4024 #if DEBUG_INIT_PLAYER
4026 printf("- looking for field player for player %d ...\n", i + 1);
4029 /* assign first free player found that is present in the playfield */
4032 /* first try: look for unmapped playfield player that is not connected */
4033 for (j = 0; j < MAX_PLAYERS; j++)
4034 if (field_player == NULL &&
4035 stored_player[j].present &&
4036 !stored_player[j].mapped &&
4037 !stored_player[j].connected)
4038 field_player = &stored_player[j];
4040 /* second try: look for *any* unmapped playfield player */
4041 for (j = 0; j < MAX_PLAYERS; j++)
4042 if (field_player == NULL &&
4043 stored_player[j].present &&
4044 !stored_player[j].mapped)
4045 field_player = &stored_player[j];
4047 /* first try: look for unmapped playfield player that is not connected */
4048 if (field_player == NULL)
4049 for (j = 0; j < MAX_PLAYERS; j++)
4050 if (stored_player[j].present &&
4051 !stored_player[j].mapped &&
4052 !stored_player[j].connected)
4053 field_player = &stored_player[j];
4055 /* second try: look for *any* unmapped playfield player */
4056 if (field_player == NULL)
4057 for (j = 0; j < MAX_PLAYERS; j++)
4058 if (stored_player[j].present &&
4059 !stored_player[j].mapped)
4060 field_player = &stored_player[j];
4063 if (field_player != NULL)
4065 int jx = field_player->jx, jy = field_player->jy;
4067 #if DEBUG_INIT_PLAYER
4069 printf("- found player %d\n", field_player->index_nr + 1);
4072 player->present = FALSE;
4073 player->active = FALSE;
4075 field_player->present = TRUE;
4076 field_player->active = TRUE;
4079 player->initial_element = field_player->initial_element;
4080 player->artwork_element = field_player->artwork_element;
4082 player->block_last_field = field_player->block_last_field;
4083 player->block_delay_adjustment = field_player->block_delay_adjustment;
4086 StorePlayer[jx][jy] = field_player->element_nr;
4088 field_player->jx = field_player->last_jx = jx;
4089 field_player->jy = field_player->last_jy = jy;
4091 if (local_player == player)
4092 local_player = field_player;
4094 map_player_action[field_player->index_nr] = i;
4096 field_player->mapped = TRUE;
4098 #if DEBUG_INIT_PLAYER
4100 printf("- map_player_action[%d] == %d\n",
4101 field_player->index_nr + 1, i + 1);
4106 if (player->connected && player->present)
4107 player->mapped = TRUE;
4110 #if DEBUG_INIT_PLAYER
4113 printf("Player status after player assignment (first stage):\n");
4115 for (i = 0; i < MAX_PLAYERS; i++)
4117 struct PlayerInfo *player = &stored_player[i];
4119 printf("- player %d: present == %d, connected == %d, active == %d",
4125 if (local_player == player)
4126 printf(" (local player)");
4135 /* check if any connected player was not found in playfield */
4136 for (i = 0; i < MAX_PLAYERS; i++)
4138 struct PlayerInfo *player = &stored_player[i];
4140 if (player->connected && !player->present)
4142 for (j = 0; j < MAX_PLAYERS; j++)
4144 struct PlayerInfo *field_player = &stored_player[j];
4145 int jx = field_player->jx, jy = field_player->jy;
4147 /* assign first free player found that is present in the playfield */
4148 if (field_player->present && !field_player->connected)
4150 player->present = TRUE;
4151 player->active = TRUE;
4153 field_player->present = FALSE;
4154 field_player->active = FALSE;
4156 player->initial_element = field_player->initial_element;
4157 player->artwork_element = field_player->artwork_element;
4159 player->block_last_field = field_player->block_last_field;
4160 player->block_delay_adjustment = field_player->block_delay_adjustment;
4162 StorePlayer[jx][jy] = player->element_nr;
4164 player->jx = player->last_jx = jx;
4165 player->jy = player->last_jy = jy;
4175 printf("::: local_player->present == %d\n", local_player->present);
4180 /* when playing a tape, eliminate all players who do not participate */
4182 #if USE_NEW_PLAYER_ASSIGNMENTS
4185 if (!game.team_mode)
4188 for (i = 0; i < MAX_PLAYERS; i++)
4190 if (stored_player[i].active &&
4191 !tape.player_participates[map_player_action[i]])
4193 struct PlayerInfo *player = &stored_player[i];
4194 int jx = player->jx, jy = player->jy;
4196 #if DEBUG_INIT_PLAYER
4198 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4201 player->active = FALSE;
4202 StorePlayer[jx][jy] = 0;
4203 Feld[jx][jy] = EL_EMPTY;
4209 for (i = 0; i < MAX_PLAYERS; i++)
4211 if (stored_player[i].active &&
4212 !tape.player_participates[i])
4214 struct PlayerInfo *player = &stored_player[i];
4215 int jx = player->jx, jy = player->jy;
4217 player->active = FALSE;
4218 StorePlayer[jx][jy] = 0;
4219 Feld[jx][jy] = EL_EMPTY;
4224 else if (!options.network && !game.team_mode) /* && !tape.playing */
4226 /* when in single player mode, eliminate all but the first active player */
4228 for (i = 0; i < MAX_PLAYERS; i++)
4230 if (stored_player[i].active)
4232 for (j = i + 1; j < MAX_PLAYERS; j++)
4234 if (stored_player[j].active)
4236 struct PlayerInfo *player = &stored_player[j];
4237 int jx = player->jx, jy = player->jy;
4239 player->active = FALSE;
4240 player->present = FALSE;
4242 StorePlayer[jx][jy] = 0;
4243 Feld[jx][jy] = EL_EMPTY;
4250 /* when recording the game, store which players take part in the game */
4253 #if USE_NEW_PLAYER_ASSIGNMENTS
4254 for (i = 0; i < MAX_PLAYERS; i++)
4255 if (stored_player[i].connected)
4256 tape.player_participates[i] = TRUE;
4258 for (i = 0; i < MAX_PLAYERS; i++)
4259 if (stored_player[i].active)
4260 tape.player_participates[i] = TRUE;
4264 #if DEBUG_INIT_PLAYER
4267 printf("Player status after player assignment (final stage):\n");
4269 for (i = 0; i < MAX_PLAYERS; i++)
4271 struct PlayerInfo *player = &stored_player[i];
4273 printf("- player %d: present == %d, connected == %d, active == %d",
4279 if (local_player == player)
4280 printf(" (local player)");
4287 if (BorderElement == EL_EMPTY)
4290 SBX_Right = lev_fieldx - SCR_FIELDX;
4292 SBY_Lower = lev_fieldy - SCR_FIELDY;
4297 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4299 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4304 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4305 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4307 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4308 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4310 if (EVEN(SCR_FIELDX))
4312 if (EVEN(SCR_FIELDY))
4317 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4318 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4320 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4321 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4324 /* if local player not found, look for custom element that might create
4325 the player (make some assumptions about the right custom element) */
4326 if (!local_player->present)
4328 int start_x = 0, start_y = 0;
4329 int found_rating = 0;
4330 int found_element = EL_UNDEFINED;
4331 int player_nr = local_player->index_nr;
4333 SCAN_PLAYFIELD(x, y)
4335 int element = Feld[x][y];
4340 if (level.use_start_element[player_nr] &&
4341 level.start_element[player_nr] == element &&
4348 found_element = element;
4351 if (!IS_CUSTOM_ELEMENT(element))
4354 if (CAN_CHANGE(element))
4356 for (i = 0; i < element_info[element].num_change_pages; i++)
4358 /* check for player created from custom element as single target */
4359 content = element_info[element].change_page[i].target_element;
4360 is_player = ELEM_IS_PLAYER(content);
4362 if (is_player && (found_rating < 3 ||
4363 (found_rating == 3 && element < found_element)))
4369 found_element = element;
4374 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4376 /* check for player created from custom element as explosion content */
4377 content = element_info[element].content.e[xx][yy];
4378 is_player = ELEM_IS_PLAYER(content);
4380 if (is_player && (found_rating < 2 ||
4381 (found_rating == 2 && element < found_element)))
4383 start_x = x + xx - 1;
4384 start_y = y + yy - 1;
4387 found_element = element;
4390 if (!CAN_CHANGE(element))
4393 for (i = 0; i < element_info[element].num_change_pages; i++)
4395 /* check for player created from custom element as extended target */
4397 element_info[element].change_page[i].target_content.e[xx][yy];
4399 is_player = ELEM_IS_PLAYER(content);
4401 if (is_player && (found_rating < 1 ||
4402 (found_rating == 1 && element < found_element)))
4404 start_x = x + xx - 1;
4405 start_y = y + yy - 1;
4408 found_element = element;
4414 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4415 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4418 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4419 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4424 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4425 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4426 local_player->jx - MIDPOSX);
4428 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4429 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4430 local_player->jy - MIDPOSY);
4434 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4438 /* do not use PLAYING mask for fading out from main screen */
4439 game_status = GAME_MODE_MAIN;
4446 if (!game.restart_level)
4447 CloseDoor(DOOR_CLOSE_1);
4450 if (level_editor_test_game)
4451 FadeSkipNextFadeIn();
4453 FadeSetEnterScreen();
4455 if (level_editor_test_game)
4456 fading = fading_none;
4458 fading = menu.destination;
4462 FadeOut(REDRAW_FIELD);
4465 FadeOut(REDRAW_FIELD);
4471 game_status = GAME_MODE_PLAYING;
4474 /* !!! FIX THIS (START) !!! */
4475 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4477 InitGameEngine_EM();
4479 /* blit playfield from scroll buffer to normal back buffer for fading in */
4480 BlitScreenToBitmap_EM(backbuffer);
4482 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4484 InitGameEngine_SP();
4486 /* blit playfield from scroll buffer to normal back buffer for fading in */
4487 BlitScreenToBitmap_SP(backbuffer);
4494 /* after drawing the level, correct some elements */
4495 if (game.timegate_time_left == 0)
4496 CloseAllOpenTimegates();
4499 BlitScreenToBitmap(backbuffer);
4501 /* blit playfield from scroll buffer to normal back buffer for fading in */
4502 if (setup.soft_scrolling)
4503 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4506 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4508 /* !!! FIX THIS (END) !!! */
4511 FadeIn(REDRAW_FIELD);
4514 FadeIn(REDRAW_FIELD);
4519 if (!game.restart_level)
4521 /* copy default game door content to main double buffer */
4524 /* !!! CHECK AGAIN !!! */
4525 SetPanelBackground();
4526 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4527 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4529 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4531 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4532 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4533 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4534 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4537 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4538 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4542 SetPanelBackground();
4543 SetDrawBackgroundMask(REDRAW_DOOR_1);
4546 UpdateAndDisplayGameControlValues();
4548 UpdateGameDoorValues();
4549 DrawGameDoorValues();
4552 if (!game.restart_level)
4556 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4557 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4558 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4562 /* copy actual game door content to door double buffer for OpenDoor() */
4564 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4566 BlitBitmap(drawto, bitmap_db_door,
4567 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4570 OpenDoor(DOOR_OPEN_ALL);
4572 PlaySound(SND_GAME_STARTING);
4574 if (setup.sound_music)
4577 KeyboardAutoRepeatOffUnlessAutoplay();
4579 #if DEBUG_INIT_PLAYER
4582 printf("Player status (final):\n");
4584 for (i = 0; i < MAX_PLAYERS; i++)
4586 struct PlayerInfo *player = &stored_player[i];
4588 printf("- player %d: present == %d, connected == %d, active == %d",
4594 if (local_player == player)
4595 printf(" (local player)");
4610 if (!game.restart_level && !tape.playing)
4612 LevelStats_incPlayed(level_nr);
4614 SaveLevelSetup_SeriesInfo();
4617 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4621 game.restart_level = FALSE;
4624 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4626 /* this is used for non-R'n'D game engines to update certain engine values */
4628 /* needed to determine if sounds are played within the visible screen area */
4629 scroll_x = actual_scroll_x;
4630 scroll_y = actual_scroll_y;
4633 void InitMovDir(int x, int y)
4635 int i, element = Feld[x][y];
4636 static int xy[4][2] =
4643 static int direction[3][4] =
4645 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4646 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4647 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4656 Feld[x][y] = EL_BUG;
4657 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4660 case EL_SPACESHIP_RIGHT:
4661 case EL_SPACESHIP_UP:
4662 case EL_SPACESHIP_LEFT:
4663 case EL_SPACESHIP_DOWN:
4664 Feld[x][y] = EL_SPACESHIP;
4665 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4668 case EL_BD_BUTTERFLY_RIGHT:
4669 case EL_BD_BUTTERFLY_UP:
4670 case EL_BD_BUTTERFLY_LEFT:
4671 case EL_BD_BUTTERFLY_DOWN:
4672 Feld[x][y] = EL_BD_BUTTERFLY;
4673 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4676 case EL_BD_FIREFLY_RIGHT:
4677 case EL_BD_FIREFLY_UP:
4678 case EL_BD_FIREFLY_LEFT:
4679 case EL_BD_FIREFLY_DOWN:
4680 Feld[x][y] = EL_BD_FIREFLY;
4681 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4684 case EL_PACMAN_RIGHT:
4686 case EL_PACMAN_LEFT:
4687 case EL_PACMAN_DOWN:
4688 Feld[x][y] = EL_PACMAN;
4689 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4692 case EL_YAMYAM_LEFT:
4693 case EL_YAMYAM_RIGHT:
4695 case EL_YAMYAM_DOWN:
4696 Feld[x][y] = EL_YAMYAM;
4697 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4700 case EL_SP_SNIKSNAK:
4701 MovDir[x][y] = MV_UP;
4704 case EL_SP_ELECTRON:
4705 MovDir[x][y] = MV_LEFT;
4712 Feld[x][y] = EL_MOLE;
4713 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4717 if (IS_CUSTOM_ELEMENT(element))
4719 struct ElementInfo *ei = &element_info[element];
4720 int move_direction_initial = ei->move_direction_initial;
4721 int move_pattern = ei->move_pattern;
4723 if (move_direction_initial == MV_START_PREVIOUS)
4725 if (MovDir[x][y] != MV_NONE)
4728 move_direction_initial = MV_START_AUTOMATIC;
4731 if (move_direction_initial == MV_START_RANDOM)
4732 MovDir[x][y] = 1 << RND(4);
4733 else if (move_direction_initial & MV_ANY_DIRECTION)
4734 MovDir[x][y] = move_direction_initial;
4735 else if (move_pattern == MV_ALL_DIRECTIONS ||
4736 move_pattern == MV_TURNING_LEFT ||
4737 move_pattern == MV_TURNING_RIGHT ||
4738 move_pattern == MV_TURNING_LEFT_RIGHT ||
4739 move_pattern == MV_TURNING_RIGHT_LEFT ||
4740 move_pattern == MV_TURNING_RANDOM)
4741 MovDir[x][y] = 1 << RND(4);
4742 else if (move_pattern == MV_HORIZONTAL)
4743 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4744 else if (move_pattern == MV_VERTICAL)
4745 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4746 else if (move_pattern & MV_ANY_DIRECTION)
4747 MovDir[x][y] = element_info[element].move_pattern;
4748 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4749 move_pattern == MV_ALONG_RIGHT_SIDE)
4751 /* use random direction as default start direction */
4752 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4753 MovDir[x][y] = 1 << RND(4);
4755 for (i = 0; i < NUM_DIRECTIONS; i++)
4757 int x1 = x + xy[i][0];
4758 int y1 = y + xy[i][1];
4760 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4762 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4763 MovDir[x][y] = direction[0][i];
4765 MovDir[x][y] = direction[1][i];
4774 MovDir[x][y] = 1 << RND(4);
4776 if (element != EL_BUG &&
4777 element != EL_SPACESHIP &&
4778 element != EL_BD_BUTTERFLY &&
4779 element != EL_BD_FIREFLY)
4782 for (i = 0; i < NUM_DIRECTIONS; i++)
4784 int x1 = x + xy[i][0];
4785 int y1 = y + xy[i][1];
4787 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4789 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4791 MovDir[x][y] = direction[0][i];
4794 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4795 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4797 MovDir[x][y] = direction[1][i];
4806 GfxDir[x][y] = MovDir[x][y];
4809 void InitAmoebaNr(int x, int y)
4812 int group_nr = AmoebeNachbarNr(x, y);
4816 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4818 if (AmoebaCnt[i] == 0)
4826 AmoebaNr[x][y] = group_nr;
4827 AmoebaCnt[group_nr]++;
4828 AmoebaCnt2[group_nr]++;
4831 static void PlayerWins(struct PlayerInfo *player)
4833 player->LevelSolved = TRUE;
4834 player->GameOver = TRUE;
4836 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4837 level.native_em_level->lev->score : player->score);
4839 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4841 player->LevelSolved_CountingScore = player->score_final;
4846 static int time, time_final;
4847 static int score, score_final;
4848 static int game_over_delay_1 = 0;
4849 static int game_over_delay_2 = 0;
4850 int game_over_delay_value_1 = 50;
4851 int game_over_delay_value_2 = 50;
4853 if (!local_player->LevelSolved_GameWon)
4857 /* do not start end game actions before the player stops moving (to exit) */
4858 if (local_player->MovPos)
4861 local_player->LevelSolved_GameWon = TRUE;
4862 local_player->LevelSolved_SaveTape = tape.recording;
4863 local_player->LevelSolved_SaveScore = !tape.playing;
4867 LevelStats_incSolved(level_nr);
4869 SaveLevelSetup_SeriesInfo();
4872 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4876 if (tape.auto_play) /* tape might already be stopped here */
4877 tape.auto_play_level_solved = TRUE;
4883 game_over_delay_1 = game_over_delay_value_1;
4884 game_over_delay_2 = game_over_delay_value_2;
4886 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4887 score = score_final = local_player->score_final;
4892 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4894 else if (game.no_time_limit && TimePlayed < 999)
4897 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4900 local_player->score_final = score_final;
4902 if (level_editor_test_game)
4905 score = score_final;
4908 local_player->LevelSolved_CountingTime = time;
4909 local_player->LevelSolved_CountingScore = score;
4911 game_panel_controls[GAME_PANEL_TIME].value = time;
4912 game_panel_controls[GAME_PANEL_SCORE].value = score;
4914 DisplayGameControlValues();
4916 DrawGameValue_Time(time);
4917 DrawGameValue_Score(score);
4921 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4923 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4925 /* close exit door after last player */
4926 if ((AllPlayersGone &&
4927 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4928 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4929 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4930 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4931 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4933 int element = Feld[ExitX][ExitY];
4936 if (element == EL_EM_EXIT_OPEN ||
4937 element == EL_EM_STEEL_EXIT_OPEN)
4944 Feld[ExitX][ExitY] =
4945 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4946 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4947 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4948 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4949 EL_EM_STEEL_EXIT_CLOSING);
4951 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4955 /* player disappears */
4956 DrawLevelField(ExitX, ExitY);
4959 for (i = 0; i < MAX_PLAYERS; i++)
4961 struct PlayerInfo *player = &stored_player[i];
4963 if (player->present)
4965 RemovePlayer(player);
4967 /* player disappears */
4968 DrawLevelField(player->jx, player->jy);
4973 PlaySound(SND_GAME_WINNING);
4976 if (game_over_delay_1 > 0)
4978 game_over_delay_1--;
4983 if (time != time_final)
4985 int time_to_go = ABS(time_final - time);
4986 int time_count_dir = (time < time_final ? +1 : -1);
4987 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4989 time += time_count_steps * time_count_dir;
4990 score += time_count_steps * level.score[SC_TIME_BONUS];
4993 local_player->LevelSolved_CountingTime = time;
4994 local_player->LevelSolved_CountingScore = score;
4996 game_panel_controls[GAME_PANEL_TIME].value = time;
4997 game_panel_controls[GAME_PANEL_SCORE].value = score;
4999 DisplayGameControlValues();
5001 DrawGameValue_Time(time);
5002 DrawGameValue_Score(score);
5005 if (time == time_final)
5006 StopSound(SND_GAME_LEVELTIME_BONUS);
5007 else if (setup.sound_loops)
5008 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5010 PlaySound(SND_GAME_LEVELTIME_BONUS);
5015 local_player->LevelSolved_PanelOff = TRUE;
5017 if (game_over_delay_2 > 0)
5019 game_over_delay_2--;
5032 boolean raise_level = FALSE;
5034 local_player->LevelSolved_GameEnd = TRUE;
5036 CloseDoor(DOOR_CLOSE_1);
5038 if (local_player->LevelSolved_SaveTape)
5045 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5047 SaveTape(tape.level_nr); /* ask to save tape */
5051 if (level_editor_test_game)
5053 game_status = GAME_MODE_MAIN;
5056 DrawAndFadeInMainMenu(REDRAW_FIELD);
5064 if (!local_player->LevelSolved_SaveScore)
5067 FadeOut(REDRAW_FIELD);
5070 game_status = GAME_MODE_MAIN;
5072 DrawAndFadeInMainMenu(REDRAW_FIELD);
5077 if (level_nr == leveldir_current->handicap_level)
5079 leveldir_current->handicap_level++;
5081 SaveLevelSetup_SeriesInfo();
5084 if (level_nr < leveldir_current->last_level)
5085 raise_level = TRUE; /* advance to next level */
5087 if ((hi_pos = NewHiScore()) >= 0)
5089 game_status = GAME_MODE_SCORES;
5091 DrawHallOfFame(hi_pos);
5102 FadeOut(REDRAW_FIELD);
5105 game_status = GAME_MODE_MAIN;
5113 DrawAndFadeInMainMenu(REDRAW_FIELD);
5122 LoadScore(level_nr);
5124 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5125 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5128 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5130 if (local_player->score_final > highscore[k].Score)
5132 /* player has made it to the hall of fame */
5134 if (k < MAX_SCORE_ENTRIES - 1)
5136 int m = MAX_SCORE_ENTRIES - 1;
5139 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5140 if (strEqual(setup.player_name, highscore[l].Name))
5142 if (m == k) /* player's new highscore overwrites his old one */
5146 for (l = m; l > k; l--)
5148 strcpy(highscore[l].Name, highscore[l - 1].Name);
5149 highscore[l].Score = highscore[l - 1].Score;
5156 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5157 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5158 highscore[k].Score = local_player->score_final;
5164 else if (!strncmp(setup.player_name, highscore[k].Name,
5165 MAX_PLAYER_NAME_LEN))
5166 break; /* player already there with a higher score */
5172 SaveScore(level_nr);
5177 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5179 int element = Feld[x][y];
5180 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5181 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5182 int horiz_move = (dx != 0);
5183 int sign = (horiz_move ? dx : dy);
5184 int step = sign * element_info[element].move_stepsize;
5186 /* special values for move stepsize for spring and things on conveyor belt */
5189 if (CAN_FALL(element) &&
5190 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5191 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5192 else if (element == EL_SPRING)
5193 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5199 inline static int getElementMoveStepsize(int x, int y)
5201 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5204 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5206 if (player->GfxAction != action || player->GfxDir != dir)
5209 printf("Player frame reset! (%d => %d, %d => %d)\n",
5210 player->GfxAction, action, player->GfxDir, dir);
5213 player->GfxAction = action;
5214 player->GfxDir = dir;
5216 player->StepFrame = 0;
5220 #if USE_GFX_RESET_GFX_ANIMATION
5221 static void ResetGfxFrame(int x, int y, boolean redraw)
5223 int element = Feld[x][y];
5224 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5225 int last_gfx_frame = GfxFrame[x][y];
5227 if (graphic_info[graphic].anim_global_sync)
5228 GfxFrame[x][y] = FrameCounter;
5229 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5230 GfxFrame[x][y] = CustomValue[x][y];
5231 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5232 GfxFrame[x][y] = element_info[element].collect_score;
5233 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5234 GfxFrame[x][y] = ChangeDelay[x][y];
5236 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5237 DrawLevelGraphicAnimation(x, y, graphic);
5241 static void ResetGfxAnimation(int x, int y)
5243 GfxAction[x][y] = ACTION_DEFAULT;
5244 GfxDir[x][y] = MovDir[x][y];
5247 #if USE_GFX_RESET_GFX_ANIMATION
5248 ResetGfxFrame(x, y, FALSE);
5252 static void ResetRandomAnimationValue(int x, int y)
5254 GfxRandom[x][y] = INIT_GFX_RANDOM();
5257 void InitMovingField(int x, int y, int direction)
5259 int element = Feld[x][y];
5260 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5261 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5264 boolean is_moving_before, is_moving_after;
5266 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5269 /* check if element was/is moving or being moved before/after mode change */
5272 is_moving_before = (WasJustMoving[x][y] != 0);
5274 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5275 is_moving_before = WasJustMoving[x][y];
5278 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5280 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5282 /* reset animation only for moving elements which change direction of moving
5283 or which just started or stopped moving
5284 (else CEs with property "can move" / "not moving" are reset each frame) */
5285 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5287 if (is_moving_before != is_moving_after ||
5288 direction != MovDir[x][y])
5289 ResetGfxAnimation(x, y);
5291 if ((is_moving_before || is_moving_after) && !continues_moving)
5292 ResetGfxAnimation(x, y);
5295 if (!continues_moving)
5296 ResetGfxAnimation(x, y);
5299 MovDir[x][y] = direction;
5300 GfxDir[x][y] = direction;
5302 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5303 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5304 direction == MV_DOWN && CAN_FALL(element) ?
5305 ACTION_FALLING : ACTION_MOVING);
5307 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5308 ACTION_FALLING : ACTION_MOVING);
5311 /* this is needed for CEs with property "can move" / "not moving" */
5313 if (is_moving_after)
5315 if (Feld[newx][newy] == EL_EMPTY)
5316 Feld[newx][newy] = EL_BLOCKED;
5318 MovDir[newx][newy] = MovDir[x][y];
5320 #if USE_NEW_CUSTOM_VALUE
5321 CustomValue[newx][newy] = CustomValue[x][y];
5324 GfxFrame[newx][newy] = GfxFrame[x][y];
5325 GfxRandom[newx][newy] = GfxRandom[x][y];
5326 GfxAction[newx][newy] = GfxAction[x][y];
5327 GfxDir[newx][newy] = GfxDir[x][y];
5331 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5333 int direction = MovDir[x][y];
5334 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5335 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5341 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5343 int oldx = x, oldy = y;
5344 int direction = MovDir[x][y];
5346 if (direction == MV_LEFT)
5348 else if (direction == MV_RIGHT)
5350 else if (direction == MV_UP)
5352 else if (direction == MV_DOWN)
5355 *comes_from_x = oldx;
5356 *comes_from_y = oldy;
5359 int MovingOrBlocked2Element(int x, int y)
5361 int element = Feld[x][y];
5363 if (element == EL_BLOCKED)
5367 Blocked2Moving(x, y, &oldx, &oldy);
5368 return Feld[oldx][oldy];
5374 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5376 /* like MovingOrBlocked2Element(), but if element is moving
5377 and (x,y) is the field the moving element is just leaving,
5378 return EL_BLOCKED instead of the element value */
5379 int element = Feld[x][y];
5381 if (IS_MOVING(x, y))
5383 if (element == EL_BLOCKED)
5387 Blocked2Moving(x, y, &oldx, &oldy);
5388 return Feld[oldx][oldy];
5397 static void RemoveField(int x, int y)
5399 Feld[x][y] = EL_EMPTY;
5405 #if USE_NEW_CUSTOM_VALUE
5406 CustomValue[x][y] = 0;
5410 ChangeDelay[x][y] = 0;
5411 ChangePage[x][y] = -1;
5412 Pushed[x][y] = FALSE;
5415 ExplodeField[x][y] = EX_TYPE_NONE;
5418 GfxElement[x][y] = EL_UNDEFINED;
5419 GfxAction[x][y] = ACTION_DEFAULT;
5420 GfxDir[x][y] = MV_NONE;
5422 /* !!! this would prevent the removed tile from being redrawn !!! */
5423 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5427 void RemoveMovingField(int x, int y)
5429 int oldx = x, oldy = y, newx = x, newy = y;
5430 int element = Feld[x][y];
5431 int next_element = EL_UNDEFINED;
5433 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5436 if (IS_MOVING(x, y))
5438 Moving2Blocked(x, y, &newx, &newy);
5440 if (Feld[newx][newy] != EL_BLOCKED)
5442 /* element is moving, but target field is not free (blocked), but
5443 already occupied by something different (example: acid pool);
5444 in this case, only remove the moving field, but not the target */
5446 RemoveField(oldx, oldy);
5448 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5450 TEST_DrawLevelField(oldx, oldy);
5455 else if (element == EL_BLOCKED)
5457 Blocked2Moving(x, y, &oldx, &oldy);
5458 if (!IS_MOVING(oldx, oldy))
5462 if (element == EL_BLOCKED &&
5463 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5464 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5465 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5466 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5467 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5468 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5469 next_element = get_next_element(Feld[oldx][oldy]);
5471 RemoveField(oldx, oldy);
5472 RemoveField(newx, newy);
5474 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5476 if (next_element != EL_UNDEFINED)
5477 Feld[oldx][oldy] = next_element;
5479 TEST_DrawLevelField(oldx, oldy);
5480 TEST_DrawLevelField(newx, newy);
5483 void DrawDynamite(int x, int y)
5485 int sx = SCREENX(x), sy = SCREENY(y);
5486 int graphic = el2img(Feld[x][y]);
5489 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5492 if (IS_WALKABLE_INSIDE(Back[x][y]))
5496 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5497 else if (Store[x][y])
5498 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5500 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5502 if (Back[x][y] || Store[x][y])
5503 DrawGraphicThruMask(sx, sy, graphic, frame);
5505 DrawGraphic(sx, sy, graphic, frame);
5508 void CheckDynamite(int x, int y)
5510 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5514 if (MovDelay[x][y] != 0)
5517 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5523 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5528 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5530 boolean num_checked_players = 0;
5533 for (i = 0; i < MAX_PLAYERS; i++)
5535 if (stored_player[i].active)
5537 int sx = stored_player[i].jx;
5538 int sy = stored_player[i].jy;
5540 if (num_checked_players == 0)
5547 *sx1 = MIN(*sx1, sx);
5548 *sy1 = MIN(*sy1, sy);
5549 *sx2 = MAX(*sx2, sx);
5550 *sy2 = MAX(*sy2, sy);
5553 num_checked_players++;
5558 static boolean checkIfAllPlayersFitToScreen_RND()
5560 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5562 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5564 return (sx2 - sx1 < SCR_FIELDX &&
5565 sy2 - sy1 < SCR_FIELDY);
5568 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5570 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5572 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5574 *sx = (sx1 + sx2) / 2;
5575 *sy = (sy1 + sy2) / 2;
5578 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5579 boolean center_screen, boolean quick_relocation)
5581 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5582 boolean no_delay = (tape.warp_forward);
5583 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5584 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5586 if (quick_relocation)
5588 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5590 if (!level.shifted_relocation || center_screen)
5592 /* quick relocation (without scrolling), with centering of screen */
5594 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5595 x > SBX_Right + MIDPOSX ? SBX_Right :
5598 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5599 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5604 /* quick relocation (without scrolling), but do not center screen */
5606 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5607 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5610 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5611 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5614 int offset_x = x + (scroll_x - center_scroll_x);
5615 int offset_y = y + (scroll_y - center_scroll_y);
5617 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5618 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5619 offset_x - MIDPOSX);
5621 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5622 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5623 offset_y - MIDPOSY);
5629 if (!level.shifted_relocation || center_screen)
5631 /* quick relocation (without scrolling), with centering of screen */
5633 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5634 x > SBX_Right + MIDPOSX ? SBX_Right :
5637 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5638 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5643 /* quick relocation (without scrolling), but do not center screen */
5645 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5646 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5649 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5650 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5653 int offset_x = x + (scroll_x - center_scroll_x);
5654 int offset_y = y + (scroll_y - center_scroll_y);
5656 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5657 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5658 offset_x - MIDPOSX);
5660 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5661 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5662 offset_y - MIDPOSY);
5665 /* quick relocation (without scrolling), inside visible screen area */
5667 int offset = game.scroll_delay_value;
5669 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5670 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5671 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5673 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5674 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5675 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5677 /* don't scroll over playfield boundaries */
5678 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5679 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5681 /* don't scroll over playfield boundaries */
5682 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5683 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5687 RedrawPlayfield(TRUE, 0,0,0,0);
5692 int scroll_xx, scroll_yy;
5694 if (!level.shifted_relocation || center_screen)
5696 /* visible relocation (with scrolling), with centering of screen */
5698 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5699 x > SBX_Right + MIDPOSX ? SBX_Right :
5702 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5703 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5708 /* visible relocation (with scrolling), but do not center screen */
5710 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5711 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5714 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5715 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5718 int offset_x = x + (scroll_x - center_scroll_x);
5719 int offset_y = y + (scroll_y - center_scroll_y);
5721 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5722 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5723 offset_x - MIDPOSX);
5725 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5726 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5727 offset_y - MIDPOSY);
5732 /* visible relocation (with scrolling), with centering of screen */
5734 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5735 x > SBX_Right + MIDPOSX ? SBX_Right :
5738 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5739 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5743 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5745 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5748 int fx = FX, fy = FY;
5750 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5751 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5753 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5759 fx += dx * TILEX / 2;
5760 fy += dy * TILEY / 2;
5762 ScrollLevel(dx, dy);
5765 /* scroll in two steps of half tile size to make things smoother */
5766 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5768 Delay(wait_delay_value);
5770 /* scroll second step to align at full tile size */
5772 Delay(wait_delay_value);
5777 Delay(wait_delay_value);
5781 void RelocatePlayer(int jx, int jy, int el_player_raw)
5783 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5784 int player_nr = GET_PLAYER_NR(el_player);
5785 struct PlayerInfo *player = &stored_player[player_nr];
5786 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5787 boolean no_delay = (tape.warp_forward);
5788 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5789 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5790 int old_jx = player->jx;
5791 int old_jy = player->jy;
5792 int old_element = Feld[old_jx][old_jy];
5793 int element = Feld[jx][jy];
5794 boolean player_relocated = (old_jx != jx || old_jy != jy);
5796 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5797 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5798 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5799 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5800 int leave_side_horiz = move_dir_horiz;
5801 int leave_side_vert = move_dir_vert;
5802 int enter_side = enter_side_horiz | enter_side_vert;
5803 int leave_side = leave_side_horiz | leave_side_vert;
5805 if (player->GameOver) /* do not reanimate dead player */
5808 if (!player_relocated) /* no need to relocate the player */
5811 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5813 RemoveField(jx, jy); /* temporarily remove newly placed player */
5814 DrawLevelField(jx, jy);
5817 if (player->present)
5819 while (player->MovPos)
5821 ScrollPlayer(player, SCROLL_GO_ON);
5822 ScrollScreen(NULL, SCROLL_GO_ON);
5824 AdvanceFrameAndPlayerCounters(player->index_nr);
5829 Delay(wait_delay_value);
5832 DrawPlayer(player); /* needed here only to cleanup last field */
5833 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5835 player->is_moving = FALSE;
5838 if (IS_CUSTOM_ELEMENT(old_element))
5839 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5841 player->index_bit, leave_side);
5843 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5845 player->index_bit, leave_side);
5847 Feld[jx][jy] = el_player;
5848 InitPlayerField(jx, jy, el_player, TRUE);
5850 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5851 possible that the relocation target field did not contain a player element,
5852 but a walkable element, to which the new player was relocated -- in this
5853 case, restore that (already initialized!) element on the player field */
5854 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5856 Feld[jx][jy] = element; /* restore previously existing element */
5858 /* !!! do not initialize already initialized element a second time !!! */
5859 /* (this causes at least problems with "element creation" CE trigger for
5860 already existing elements, and existing Sokoban fields counted twice) */
5861 InitField(jx, jy, FALSE);
5865 /* only visually relocate centered player */
5866 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5867 FALSE, level.instant_relocation);
5869 TestIfPlayerTouchesBadThing(jx, jy);
5870 TestIfPlayerTouchesCustomElement(jx, jy);
5872 if (IS_CUSTOM_ELEMENT(element))
5873 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5874 player->index_bit, enter_side);
5876 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5877 player->index_bit, enter_side);
5880 if (player->is_switching)
5882 /* ensure that relocation while still switching an element does not cause
5883 a new element to be treated as also switched directly after relocation
5884 (this is important for teleporter switches that teleport the player to
5885 a place where another teleporter switch is in the same direction, which
5886 would then incorrectly be treated as immediately switched before the
5887 direction key that caused the switch was released) */
5889 player->switch_x += jx - old_jx;
5890 player->switch_y += jy - old_jy;
5895 void Explode(int ex, int ey, int phase, int mode)
5901 /* !!! eliminate this variable !!! */
5902 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5904 if (game.explosions_delayed)
5906 ExplodeField[ex][ey] = mode;
5910 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5912 int center_element = Feld[ex][ey];
5913 int artwork_element, explosion_element; /* set these values later */
5916 /* --- This is only really needed (and now handled) in "Impact()". --- */
5917 /* do not explode moving elements that left the explode field in time */
5918 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5919 center_element == EL_EMPTY &&
5920 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5925 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5926 if (mode == EX_TYPE_NORMAL ||
5927 mode == EX_TYPE_CENTER ||
5928 mode == EX_TYPE_CROSS)
5929 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5932 /* remove things displayed in background while burning dynamite */
5933 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5936 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5938 /* put moving element to center field (and let it explode there) */
5939 center_element = MovingOrBlocked2Element(ex, ey);
5940 RemoveMovingField(ex, ey);
5941 Feld[ex][ey] = center_element;
5944 /* now "center_element" is finally determined -- set related values now */
5945 artwork_element = center_element; /* for custom player artwork */
5946 explosion_element = center_element; /* for custom player artwork */
5948 if (IS_PLAYER(ex, ey))
5950 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5952 artwork_element = stored_player[player_nr].artwork_element;
5954 if (level.use_explosion_element[player_nr])
5956 explosion_element = level.explosion_element[player_nr];
5957 artwork_element = explosion_element;
5962 if (mode == EX_TYPE_NORMAL ||
5963 mode == EX_TYPE_CENTER ||
5964 mode == EX_TYPE_CROSS)
5965 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5968 last_phase = element_info[explosion_element].explosion_delay + 1;
5970 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5972 int xx = x - ex + 1;
5973 int yy = y - ey + 1;
5976 if (!IN_LEV_FIELD(x, y) ||
5977 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5978 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5981 element = Feld[x][y];
5983 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5985 element = MovingOrBlocked2Element(x, y);
5987 if (!IS_EXPLOSION_PROOF(element))
5988 RemoveMovingField(x, y);
5991 /* indestructible elements can only explode in center (but not flames) */
5992 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5993 mode == EX_TYPE_BORDER)) ||
5994 element == EL_FLAMES)
5997 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5998 behaviour, for example when touching a yamyam that explodes to rocks
5999 with active deadly shield, a rock is created under the player !!! */
6000 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6002 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6003 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6004 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6006 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6009 if (IS_ACTIVE_BOMB(element))
6011 /* re-activate things under the bomb like gate or penguin */
6012 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6019 /* save walkable background elements while explosion on same tile */
6020 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6021 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6022 Back[x][y] = element;
6024 /* ignite explodable elements reached by other explosion */
6025 if (element == EL_EXPLOSION)
6026 element = Store2[x][y];
6028 if (AmoebaNr[x][y] &&
6029 (element == EL_AMOEBA_FULL ||
6030 element == EL_BD_AMOEBA ||
6031 element == EL_AMOEBA_GROWING))
6033 AmoebaCnt[AmoebaNr[x][y]]--;
6034 AmoebaCnt2[AmoebaNr[x][y]]--;
6039 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6041 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6043 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6045 if (PLAYERINFO(ex, ey)->use_murphy)
6046 Store[x][y] = EL_EMPTY;
6049 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6050 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6051 else if (ELEM_IS_PLAYER(center_element))
6052 Store[x][y] = EL_EMPTY;
6053 else if (center_element == EL_YAMYAM)
6054 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6055 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6056 Store[x][y] = element_info[center_element].content.e[xx][yy];
6058 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6059 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6060 otherwise) -- FIX THIS !!! */
6061 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6062 Store[x][y] = element_info[element].content.e[1][1];
6064 else if (!CAN_EXPLODE(element))
6065 Store[x][y] = element_info[element].content.e[1][1];
6068 Store[x][y] = EL_EMPTY;
6070 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6071 center_element == EL_AMOEBA_TO_DIAMOND)
6072 Store2[x][y] = element;
6074 Feld[x][y] = EL_EXPLOSION;
6075 GfxElement[x][y] = artwork_element;
6077 ExplodePhase[x][y] = 1;
6078 ExplodeDelay[x][y] = last_phase;
6083 if (center_element == EL_YAMYAM)
6084 game.yamyam_content_nr =
6085 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6097 GfxFrame[x][y] = 0; /* restart explosion animation */
6099 last_phase = ExplodeDelay[x][y];
6101 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6105 /* activate this even in non-DEBUG version until cause for crash in
6106 getGraphicAnimationFrame() (see below) is found and eliminated */
6112 /* this can happen if the player leaves an explosion just in time */
6113 if (GfxElement[x][y] == EL_UNDEFINED)
6114 GfxElement[x][y] = EL_EMPTY;
6116 if (GfxElement[x][y] == EL_UNDEFINED)
6119 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6120 printf("Explode(): This should never happen!\n");
6123 GfxElement[x][y] = EL_EMPTY;
6129 border_element = Store2[x][y];
6130 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6131 border_element = StorePlayer[x][y];
6133 if (phase == element_info[border_element].ignition_delay ||
6134 phase == last_phase)
6136 boolean border_explosion = FALSE;
6138 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6139 !PLAYER_EXPLOSION_PROTECTED(x, y))
6141 KillPlayerUnlessExplosionProtected(x, y);
6142 border_explosion = TRUE;
6144 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6146 Feld[x][y] = Store2[x][y];
6149 border_explosion = TRUE;
6151 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6153 AmoebeUmwandeln(x, y);
6155 border_explosion = TRUE;
6158 /* if an element just explodes due to another explosion (chain-reaction),
6159 do not immediately end the new explosion when it was the last frame of
6160 the explosion (as it would be done in the following "if"-statement!) */
6161 if (border_explosion && phase == last_phase)
6165 if (phase == last_phase)
6169 element = Feld[x][y] = Store[x][y];
6170 Store[x][y] = Store2[x][y] = 0;
6171 GfxElement[x][y] = EL_UNDEFINED;
6173 /* player can escape from explosions and might therefore be still alive */
6174 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6175 element <= EL_PLAYER_IS_EXPLODING_4)
6177 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6178 int explosion_element = EL_PLAYER_1 + player_nr;
6179 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6180 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6182 if (level.use_explosion_element[player_nr])
6183 explosion_element = level.explosion_element[player_nr];
6185 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6186 element_info[explosion_element].content.e[xx][yy]);
6189 /* restore probably existing indestructible background element */
6190 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6191 element = Feld[x][y] = Back[x][y];
6194 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6195 GfxDir[x][y] = MV_NONE;
6196 ChangeDelay[x][y] = 0;
6197 ChangePage[x][y] = -1;
6199 #if USE_NEW_CUSTOM_VALUE
6200 CustomValue[x][y] = 0;
6203 InitField_WithBug2(x, y, FALSE);
6205 TEST_DrawLevelField(x, y);
6207 TestIfElementTouchesCustomElement(x, y);
6209 if (GFX_CRUMBLED(element))
6210 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6212 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6213 StorePlayer[x][y] = 0;
6215 if (ELEM_IS_PLAYER(element))
6216 RelocatePlayer(x, y, element);
6218 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6220 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6221 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6224 TEST_DrawLevelFieldCrumbled(x, y);
6226 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6228 DrawLevelElement(x, y, Back[x][y]);
6229 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6231 else if (IS_WALKABLE_UNDER(Back[x][y]))
6233 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6234 DrawLevelElementThruMask(x, y, Back[x][y]);
6236 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6237 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6241 void DynaExplode(int ex, int ey)
6244 int dynabomb_element = Feld[ex][ey];
6245 int dynabomb_size = 1;
6246 boolean dynabomb_xl = FALSE;
6247 struct PlayerInfo *player;
6248 static int xy[4][2] =
6256 if (IS_ACTIVE_BOMB(dynabomb_element))
6258 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6259 dynabomb_size = player->dynabomb_size;
6260 dynabomb_xl = player->dynabomb_xl;
6261 player->dynabombs_left++;
6264 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6266 for (i = 0; i < NUM_DIRECTIONS; i++)
6268 for (j = 1; j <= dynabomb_size; j++)
6270 int x = ex + j * xy[i][0];
6271 int y = ey + j * xy[i][1];
6274 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6277 element = Feld[x][y];
6279 /* do not restart explosions of fields with active bombs */
6280 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6283 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6285 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6286 !IS_DIGGABLE(element) && !dynabomb_xl)
6292 void Bang(int x, int y)
6294 int element = MovingOrBlocked2Element(x, y);
6295 int explosion_type = EX_TYPE_NORMAL;
6297 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6299 struct PlayerInfo *player = PLAYERINFO(x, y);
6301 #if USE_FIX_CE_ACTION_WITH_PLAYER
6302 element = Feld[x][y] = player->initial_element;
6304 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6305 player->element_nr);
6308 if (level.use_explosion_element[player->index_nr])
6310 int explosion_element = level.explosion_element[player->index_nr];
6312 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6313 explosion_type = EX_TYPE_CROSS;
6314 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6315 explosion_type = EX_TYPE_CENTER;
6323 case EL_BD_BUTTERFLY:
6326 case EL_DARK_YAMYAM:
6330 RaiseScoreElement(element);
6333 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6334 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6335 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6336 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6337 case EL_DYNABOMB_INCREASE_NUMBER:
6338 case EL_DYNABOMB_INCREASE_SIZE:
6339 case EL_DYNABOMB_INCREASE_POWER:
6340 explosion_type = EX_TYPE_DYNA;
6343 case EL_DC_LANDMINE:
6345 case EL_EM_EXIT_OPEN:
6346 case EL_EM_STEEL_EXIT_OPEN:
6348 explosion_type = EX_TYPE_CENTER;
6353 case EL_LAMP_ACTIVE:
6354 case EL_AMOEBA_TO_DIAMOND:
6355 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6356 explosion_type = EX_TYPE_CENTER;
6360 if (element_info[element].explosion_type == EXPLODES_CROSS)
6361 explosion_type = EX_TYPE_CROSS;
6362 else if (element_info[element].explosion_type == EXPLODES_1X1)
6363 explosion_type = EX_TYPE_CENTER;
6367 if (explosion_type == EX_TYPE_DYNA)
6370 Explode(x, y, EX_PHASE_START, explosion_type);
6372 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6375 void SplashAcid(int x, int y)
6377 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6378 (!IN_LEV_FIELD(x - 1, y - 2) ||
6379 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6380 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6382 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6383 (!IN_LEV_FIELD(x + 1, y - 2) ||
6384 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6385 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6387 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6390 static void InitBeltMovement()
6392 static int belt_base_element[4] =
6394 EL_CONVEYOR_BELT_1_LEFT,
6395 EL_CONVEYOR_BELT_2_LEFT,
6396 EL_CONVEYOR_BELT_3_LEFT,
6397 EL_CONVEYOR_BELT_4_LEFT
6399 static int belt_base_active_element[4] =
6401 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6402 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6403 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6404 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6409 /* set frame order for belt animation graphic according to belt direction */
6410 for (i = 0; i < NUM_BELTS; i++)
6414 for (j = 0; j < NUM_BELT_PARTS; j++)
6416 int element = belt_base_active_element[belt_nr] + j;
6417 int graphic_1 = el2img(element);
6418 int graphic_2 = el2panelimg(element);
6420 if (game.belt_dir[i] == MV_LEFT)
6422 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6423 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6427 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6428 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6433 SCAN_PLAYFIELD(x, y)
6435 int element = Feld[x][y];
6437 for (i = 0; i < NUM_BELTS; i++)
6439 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6441 int e_belt_nr = getBeltNrFromBeltElement(element);
6444 if (e_belt_nr == belt_nr)
6446 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6448 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6455 static void ToggleBeltSwitch(int x, int y)
6457 static int belt_base_element[4] =
6459 EL_CONVEYOR_BELT_1_LEFT,
6460 EL_CONVEYOR_BELT_2_LEFT,
6461 EL_CONVEYOR_BELT_3_LEFT,
6462 EL_CONVEYOR_BELT_4_LEFT
6464 static int belt_base_active_element[4] =
6466 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6467 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6468 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6469 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6471 static int belt_base_switch_element[4] =
6473 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6474 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6475 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6476 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6478 static int belt_move_dir[4] =
6486 int element = Feld[x][y];
6487 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6488 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6489 int belt_dir = belt_move_dir[belt_dir_nr];
6492 if (!IS_BELT_SWITCH(element))
6495 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6496 game.belt_dir[belt_nr] = belt_dir;
6498 if (belt_dir_nr == 3)
6501 /* set frame order for belt animation graphic according to belt direction */
6502 for (i = 0; i < NUM_BELT_PARTS; i++)
6504 int element = belt_base_active_element[belt_nr] + i;
6505 int graphic_1 = el2img(element);
6506 int graphic_2 = el2panelimg(element);
6508 if (belt_dir == MV_LEFT)
6510 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6511 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6515 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6516 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6520 SCAN_PLAYFIELD(xx, yy)
6522 int element = Feld[xx][yy];
6524 if (IS_BELT_SWITCH(element))
6526 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6528 if (e_belt_nr == belt_nr)
6530 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6531 TEST_DrawLevelField(xx, yy);
6534 else if (IS_BELT(element) && belt_dir != MV_NONE)
6536 int e_belt_nr = getBeltNrFromBeltElement(element);
6538 if (e_belt_nr == belt_nr)
6540 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6542 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6543 TEST_DrawLevelField(xx, yy);
6546 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6548 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6550 if (e_belt_nr == belt_nr)
6552 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6554 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6555 TEST_DrawLevelField(xx, yy);
6561 static void ToggleSwitchgateSwitch(int x, int y)
6565 game.switchgate_pos = !game.switchgate_pos;
6567 SCAN_PLAYFIELD(xx, yy)
6569 int element = Feld[xx][yy];
6571 #if !USE_BOTH_SWITCHGATE_SWITCHES
6572 if (element == EL_SWITCHGATE_SWITCH_UP ||
6573 element == EL_SWITCHGATE_SWITCH_DOWN)
6575 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6576 TEST_DrawLevelField(xx, yy);
6578 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6579 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6581 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6582 TEST_DrawLevelField(xx, yy);
6585 if (element == EL_SWITCHGATE_SWITCH_UP)
6587 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6588 TEST_DrawLevelField(xx, yy);
6590 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6592 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6593 TEST_DrawLevelField(xx, yy);
6595 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6597 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6598 TEST_DrawLevelField(xx, yy);
6600 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6602 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6603 TEST_DrawLevelField(xx, yy);
6606 else if (element == EL_SWITCHGATE_OPEN ||
6607 element == EL_SWITCHGATE_OPENING)
6609 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6611 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6613 else if (element == EL_SWITCHGATE_CLOSED ||
6614 element == EL_SWITCHGATE_CLOSING)
6616 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6618 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6623 static int getInvisibleActiveFromInvisibleElement(int element)
6625 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6626 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6627 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6631 static int getInvisibleFromInvisibleActiveElement(int element)
6633 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6634 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6635 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6639 static void RedrawAllLightSwitchesAndInvisibleElements()
6643 SCAN_PLAYFIELD(x, y)
6645 int element = Feld[x][y];
6647 if (element == EL_LIGHT_SWITCH &&
6648 game.light_time_left > 0)
6650 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6651 TEST_DrawLevelField(x, y);
6653 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6654 game.light_time_left == 0)
6656 Feld[x][y] = EL_LIGHT_SWITCH;
6657 TEST_DrawLevelField(x, y);
6659 else if (element == EL_EMC_DRIPPER &&
6660 game.light_time_left > 0)
6662 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6663 TEST_DrawLevelField(x, y);
6665 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6666 game.light_time_left == 0)
6668 Feld[x][y] = EL_EMC_DRIPPER;
6669 TEST_DrawLevelField(x, y);
6671 else if (element == EL_INVISIBLE_STEELWALL ||
6672 element == EL_INVISIBLE_WALL ||
6673 element == EL_INVISIBLE_SAND)
6675 if (game.light_time_left > 0)
6676 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6678 TEST_DrawLevelField(x, y);
6680 /* uncrumble neighbour fields, if needed */
6681 if (element == EL_INVISIBLE_SAND)
6682 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6684 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6685 element == EL_INVISIBLE_WALL_ACTIVE ||
6686 element == EL_INVISIBLE_SAND_ACTIVE)
6688 if (game.light_time_left == 0)
6689 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6691 TEST_DrawLevelField(x, y);
6693 /* re-crumble neighbour fields, if needed */
6694 if (element == EL_INVISIBLE_SAND)
6695 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6700 static void RedrawAllInvisibleElementsForLenses()
6704 SCAN_PLAYFIELD(x, y)
6706 int element = Feld[x][y];
6708 if (element == EL_EMC_DRIPPER &&
6709 game.lenses_time_left > 0)
6711 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6712 TEST_DrawLevelField(x, y);
6714 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6715 game.lenses_time_left == 0)
6717 Feld[x][y] = EL_EMC_DRIPPER;
6718 TEST_DrawLevelField(x, y);
6720 else if (element == EL_INVISIBLE_STEELWALL ||
6721 element == EL_INVISIBLE_WALL ||
6722 element == EL_INVISIBLE_SAND)
6724 if (game.lenses_time_left > 0)
6725 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6727 TEST_DrawLevelField(x, y);
6729 /* uncrumble neighbour fields, if needed */
6730 if (element == EL_INVISIBLE_SAND)
6731 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6733 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6734 element == EL_INVISIBLE_WALL_ACTIVE ||
6735 element == EL_INVISIBLE_SAND_ACTIVE)
6737 if (game.lenses_time_left == 0)
6738 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6740 TEST_DrawLevelField(x, y);
6742 /* re-crumble neighbour fields, if needed */
6743 if (element == EL_INVISIBLE_SAND)
6744 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6749 static void RedrawAllInvisibleElementsForMagnifier()
6753 SCAN_PLAYFIELD(x, y)
6755 int element = Feld[x][y];
6757 if (element == EL_EMC_FAKE_GRASS &&
6758 game.magnify_time_left > 0)
6760 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6761 TEST_DrawLevelField(x, y);
6763 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6764 game.magnify_time_left == 0)
6766 Feld[x][y] = EL_EMC_FAKE_GRASS;
6767 TEST_DrawLevelField(x, y);
6769 else if (IS_GATE_GRAY(element) &&
6770 game.magnify_time_left > 0)
6772 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6773 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6774 IS_EM_GATE_GRAY(element) ?
6775 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6776 IS_EMC_GATE_GRAY(element) ?
6777 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6778 IS_DC_GATE_GRAY(element) ?
6779 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6781 TEST_DrawLevelField(x, y);
6783 else if (IS_GATE_GRAY_ACTIVE(element) &&
6784 game.magnify_time_left == 0)
6786 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6787 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6788 IS_EM_GATE_GRAY_ACTIVE(element) ?
6789 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6790 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6791 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6792 IS_DC_GATE_GRAY_ACTIVE(element) ?
6793 EL_DC_GATE_WHITE_GRAY :
6795 TEST_DrawLevelField(x, y);
6800 static void ToggleLightSwitch(int x, int y)
6802 int element = Feld[x][y];
6804 game.light_time_left =
6805 (element == EL_LIGHT_SWITCH ?
6806 level.time_light * FRAMES_PER_SECOND : 0);
6808 RedrawAllLightSwitchesAndInvisibleElements();
6811 static void ActivateTimegateSwitch(int x, int y)
6815 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6817 SCAN_PLAYFIELD(xx, yy)
6819 int element = Feld[xx][yy];
6821 if (element == EL_TIMEGATE_CLOSED ||
6822 element == EL_TIMEGATE_CLOSING)
6824 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6825 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6829 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6831 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6832 TEST_DrawLevelField(xx, yy);
6839 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6840 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6842 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6846 void Impact(int x, int y)
6848 boolean last_line = (y == lev_fieldy - 1);
6849 boolean object_hit = FALSE;
6850 boolean impact = (last_line || object_hit);
6851 int element = Feld[x][y];
6852 int smashed = EL_STEELWALL;
6854 if (!last_line) /* check if element below was hit */
6856 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6859 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6860 MovDir[x][y + 1] != MV_DOWN ||
6861 MovPos[x][y + 1] <= TILEY / 2));
6863 /* do not smash moving elements that left the smashed field in time */
6864 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6865 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6868 #if USE_QUICKSAND_IMPACT_BUGFIX
6869 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6871 RemoveMovingField(x, y + 1);
6872 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6873 Feld[x][y + 2] = EL_ROCK;
6874 TEST_DrawLevelField(x, y + 2);
6879 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6881 RemoveMovingField(x, y + 1);
6882 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6883 Feld[x][y + 2] = EL_ROCK;
6884 TEST_DrawLevelField(x, y + 2);
6891 smashed = MovingOrBlocked2Element(x, y + 1);
6893 impact = (last_line || object_hit);
6896 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6898 SplashAcid(x, y + 1);
6902 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6903 /* only reset graphic animation if graphic really changes after impact */
6905 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6907 ResetGfxAnimation(x, y);
6908 TEST_DrawLevelField(x, y);
6911 if (impact && CAN_EXPLODE_IMPACT(element))
6916 else if (impact && element == EL_PEARL &&
6917 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6919 ResetGfxAnimation(x, y);
6921 Feld[x][y] = EL_PEARL_BREAKING;
6922 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6925 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6927 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6932 if (impact && element == EL_AMOEBA_DROP)
6934 if (object_hit && IS_PLAYER(x, y + 1))
6935 KillPlayerUnlessEnemyProtected(x, y + 1);
6936 else if (object_hit && smashed == EL_PENGUIN)
6940 Feld[x][y] = EL_AMOEBA_GROWING;
6941 Store[x][y] = EL_AMOEBA_WET;
6943 ResetRandomAnimationValue(x, y);
6948 if (object_hit) /* check which object was hit */
6950 if ((CAN_PASS_MAGIC_WALL(element) &&
6951 (smashed == EL_MAGIC_WALL ||
6952 smashed == EL_BD_MAGIC_WALL)) ||
6953 (CAN_PASS_DC_MAGIC_WALL(element) &&
6954 smashed == EL_DC_MAGIC_WALL))
6957 int activated_magic_wall =
6958 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6959 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6960 EL_DC_MAGIC_WALL_ACTIVE);
6962 /* activate magic wall / mill */
6963 SCAN_PLAYFIELD(xx, yy)
6965 if (Feld[xx][yy] == smashed)
6966 Feld[xx][yy] = activated_magic_wall;
6969 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6970 game.magic_wall_active = TRUE;
6972 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6973 SND_MAGIC_WALL_ACTIVATING :
6974 smashed == EL_BD_MAGIC_WALL ?
6975 SND_BD_MAGIC_WALL_ACTIVATING :
6976 SND_DC_MAGIC_WALL_ACTIVATING));
6979 if (IS_PLAYER(x, y + 1))
6981 if (CAN_SMASH_PLAYER(element))
6983 KillPlayerUnlessEnemyProtected(x, y + 1);
6987 else if (smashed == EL_PENGUIN)
6989 if (CAN_SMASH_PLAYER(element))
6995 else if (element == EL_BD_DIAMOND)
6997 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7003 else if (((element == EL_SP_INFOTRON ||
7004 element == EL_SP_ZONK) &&
7005 (smashed == EL_SP_SNIKSNAK ||
7006 smashed == EL_SP_ELECTRON ||
7007 smashed == EL_SP_DISK_ORANGE)) ||
7008 (element == EL_SP_INFOTRON &&
7009 smashed == EL_SP_DISK_YELLOW))
7014 else if (CAN_SMASH_EVERYTHING(element))
7016 if (IS_CLASSIC_ENEMY(smashed) ||
7017 CAN_EXPLODE_SMASHED(smashed))
7022 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7024 if (smashed == EL_LAMP ||
7025 smashed == EL_LAMP_ACTIVE)
7030 else if (smashed == EL_NUT)
7032 Feld[x][y + 1] = EL_NUT_BREAKING;
7033 PlayLevelSound(x, y, SND_NUT_BREAKING);
7034 RaiseScoreElement(EL_NUT);
7037 else if (smashed == EL_PEARL)
7039 ResetGfxAnimation(x, y);
7041 Feld[x][y + 1] = EL_PEARL_BREAKING;
7042 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7045 else if (smashed == EL_DIAMOND)
7047 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7048 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7051 else if (IS_BELT_SWITCH(smashed))
7053 ToggleBeltSwitch(x, y + 1);
7055 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7056 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7057 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7058 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7060 ToggleSwitchgateSwitch(x, y + 1);
7062 else if (smashed == EL_LIGHT_SWITCH ||
7063 smashed == EL_LIGHT_SWITCH_ACTIVE)
7065 ToggleLightSwitch(x, y + 1);
7070 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7073 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7075 CheckElementChangeBySide(x, y + 1, smashed, element,
7076 CE_SWITCHED, CH_SIDE_TOP);
7077 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7083 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7088 /* play sound of magic wall / mill */
7090 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7091 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7092 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7094 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7095 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7096 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7097 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7098 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7099 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7104 /* play sound of object that hits the ground */
7105 if (last_line || object_hit)
7106 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7109 inline static void TurnRoundExt(int x, int y)
7121 { 0, 0 }, { 0, 0 }, { 0, 0 },
7126 int left, right, back;
7130 { MV_DOWN, MV_UP, MV_RIGHT },
7131 { MV_UP, MV_DOWN, MV_LEFT },
7133 { MV_LEFT, MV_RIGHT, MV_DOWN },
7137 { MV_RIGHT, MV_LEFT, MV_UP }
7140 int element = Feld[x][y];
7141 int move_pattern = element_info[element].move_pattern;
7143 int old_move_dir = MovDir[x][y];
7144 int left_dir = turn[old_move_dir].left;
7145 int right_dir = turn[old_move_dir].right;
7146 int back_dir = turn[old_move_dir].back;
7148 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7149 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7150 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7151 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7153 int left_x = x + left_dx, left_y = y + left_dy;
7154 int right_x = x + right_dx, right_y = y + right_dy;
7155 int move_x = x + move_dx, move_y = y + move_dy;
7159 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7161 TestIfBadThingTouchesOtherBadThing(x, y);
7163 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7164 MovDir[x][y] = right_dir;
7165 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7166 MovDir[x][y] = left_dir;
7168 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7170 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7173 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7175 TestIfBadThingTouchesOtherBadThing(x, y);
7177 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7178 MovDir[x][y] = left_dir;
7179 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7180 MovDir[x][y] = right_dir;
7182 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7184 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7187 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7189 TestIfBadThingTouchesOtherBadThing(x, y);
7191 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7192 MovDir[x][y] = left_dir;
7193 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7194 MovDir[x][y] = right_dir;
7196 if (MovDir[x][y] != old_move_dir)
7199 else if (element == EL_YAMYAM)
7201 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7202 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7204 if (can_turn_left && can_turn_right)
7205 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7206 else if (can_turn_left)
7207 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7208 else if (can_turn_right)
7209 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7211 MovDir[x][y] = back_dir;
7213 MovDelay[x][y] = 16 + 16 * RND(3);
7215 else if (element == EL_DARK_YAMYAM)
7217 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7219 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7222 if (can_turn_left && can_turn_right)
7223 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7224 else if (can_turn_left)
7225 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7226 else if (can_turn_right)
7227 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7229 MovDir[x][y] = back_dir;
7231 MovDelay[x][y] = 16 + 16 * RND(3);
7233 else if (element == EL_PACMAN)
7235 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7236 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7238 if (can_turn_left && can_turn_right)
7239 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7240 else if (can_turn_left)
7241 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7242 else if (can_turn_right)
7243 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7245 MovDir[x][y] = back_dir;
7247 MovDelay[x][y] = 6 + RND(40);
7249 else if (element == EL_PIG)
7251 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7252 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7253 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7254 boolean should_turn_left, should_turn_right, should_move_on;
7256 int rnd = RND(rnd_value);
7258 should_turn_left = (can_turn_left &&
7260 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7261 y + back_dy + left_dy)));
7262 should_turn_right = (can_turn_right &&
7264 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7265 y + back_dy + right_dy)));
7266 should_move_on = (can_move_on &&
7269 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7270 y + move_dy + left_dy) ||
7271 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7272 y + move_dy + right_dy)));
7274 if (should_turn_left || should_turn_right || should_move_on)
7276 if (should_turn_left && should_turn_right && should_move_on)
7277 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7278 rnd < 2 * rnd_value / 3 ? right_dir :
7280 else if (should_turn_left && should_turn_right)
7281 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7282 else if (should_turn_left && should_move_on)
7283 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7284 else if (should_turn_right && should_move_on)
7285 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7286 else if (should_turn_left)
7287 MovDir[x][y] = left_dir;
7288 else if (should_turn_right)
7289 MovDir[x][y] = right_dir;
7290 else if (should_move_on)
7291 MovDir[x][y] = old_move_dir;
7293 else if (can_move_on && rnd > rnd_value / 8)
7294 MovDir[x][y] = old_move_dir;
7295 else if (can_turn_left && can_turn_right)
7296 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7297 else if (can_turn_left && rnd > rnd_value / 8)
7298 MovDir[x][y] = left_dir;
7299 else if (can_turn_right && rnd > rnd_value/8)
7300 MovDir[x][y] = right_dir;
7302 MovDir[x][y] = back_dir;
7304 xx = x + move_xy[MovDir[x][y]].dx;
7305 yy = y + move_xy[MovDir[x][y]].dy;
7307 if (!IN_LEV_FIELD(xx, yy) ||
7308 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7309 MovDir[x][y] = old_move_dir;
7313 else if (element == EL_DRAGON)
7315 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7316 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7317 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7319 int rnd = RND(rnd_value);
7321 if (can_move_on && rnd > rnd_value / 8)
7322 MovDir[x][y] = old_move_dir;
7323 else if (can_turn_left && can_turn_right)
7324 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325 else if (can_turn_left && rnd > rnd_value / 8)
7326 MovDir[x][y] = left_dir;
7327 else if (can_turn_right && rnd > rnd_value / 8)
7328 MovDir[x][y] = right_dir;
7330 MovDir[x][y] = back_dir;
7332 xx = x + move_xy[MovDir[x][y]].dx;
7333 yy = y + move_xy[MovDir[x][y]].dy;
7335 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7336 MovDir[x][y] = old_move_dir;
7340 else if (element == EL_MOLE)
7342 boolean can_move_on =
7343 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7344 IS_AMOEBOID(Feld[move_x][move_y]) ||
7345 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7348 boolean can_turn_left =
7349 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7350 IS_AMOEBOID(Feld[left_x][left_y])));
7352 boolean can_turn_right =
7353 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7354 IS_AMOEBOID(Feld[right_x][right_y])));
7356 if (can_turn_left && can_turn_right)
7357 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7358 else if (can_turn_left)
7359 MovDir[x][y] = left_dir;
7361 MovDir[x][y] = right_dir;
7364 if (MovDir[x][y] != old_move_dir)
7367 else if (element == EL_BALLOON)
7369 MovDir[x][y] = game.wind_direction;
7372 else if (element == EL_SPRING)
7374 #if USE_NEW_SPRING_BUMPER
7375 if (MovDir[x][y] & MV_HORIZONTAL)
7377 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7378 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7380 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7381 ResetGfxAnimation(move_x, move_y);
7382 TEST_DrawLevelField(move_x, move_y);
7384 MovDir[x][y] = back_dir;
7386 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7387 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7388 MovDir[x][y] = MV_NONE;
7391 if (MovDir[x][y] & MV_HORIZONTAL &&
7392 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7393 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7394 MovDir[x][y] = MV_NONE;
7399 else if (element == EL_ROBOT ||
7400 element == EL_SATELLITE ||
7401 element == EL_PENGUIN ||
7402 element == EL_EMC_ANDROID)
7404 int attr_x = -1, attr_y = -1;
7415 for (i = 0; i < MAX_PLAYERS; i++)
7417 struct PlayerInfo *player = &stored_player[i];
7418 int jx = player->jx, jy = player->jy;
7420 if (!player->active)
7424 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7432 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7433 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7434 game.engine_version < VERSION_IDENT(3,1,0,0)))
7440 if (element == EL_PENGUIN)
7443 static int xy[4][2] =
7451 for (i = 0; i < NUM_DIRECTIONS; i++)
7453 int ex = x + xy[i][0];
7454 int ey = y + xy[i][1];
7456 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7457 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7458 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7459 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7468 MovDir[x][y] = MV_NONE;
7470 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7471 else if (attr_x > x)
7472 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7474 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7475 else if (attr_y > y)
7476 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7478 if (element == EL_ROBOT)
7482 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7483 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7484 Moving2Blocked(x, y, &newx, &newy);
7486 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7487 MovDelay[x][y] = 8 + 8 * !RND(3);
7489 MovDelay[x][y] = 16;
7491 else if (element == EL_PENGUIN)
7497 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7499 boolean first_horiz = RND(2);
7500 int new_move_dir = MovDir[x][y];
7503 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7504 Moving2Blocked(x, y, &newx, &newy);
7506 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7510 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7511 Moving2Blocked(x, y, &newx, &newy);
7513 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7516 MovDir[x][y] = old_move_dir;
7520 else if (element == EL_SATELLITE)
7526 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7528 boolean first_horiz = RND(2);
7529 int new_move_dir = MovDir[x][y];
7532 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7533 Moving2Blocked(x, y, &newx, &newy);
7535 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7539 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7540 Moving2Blocked(x, y, &newx, &newy);
7542 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7545 MovDir[x][y] = old_move_dir;
7549 else if (element == EL_EMC_ANDROID)
7551 static int check_pos[16] =
7553 -1, /* 0 => (invalid) */
7554 7, /* 1 => MV_LEFT */
7555 3, /* 2 => MV_RIGHT */
7556 -1, /* 3 => (invalid) */
7558 0, /* 5 => MV_LEFT | MV_UP */
7559 2, /* 6 => MV_RIGHT | MV_UP */
7560 -1, /* 7 => (invalid) */
7561 5, /* 8 => MV_DOWN */
7562 6, /* 9 => MV_LEFT | MV_DOWN */
7563 4, /* 10 => MV_RIGHT | MV_DOWN */
7564 -1, /* 11 => (invalid) */
7565 -1, /* 12 => (invalid) */
7566 -1, /* 13 => (invalid) */
7567 -1, /* 14 => (invalid) */
7568 -1, /* 15 => (invalid) */
7576 { -1, -1, MV_LEFT | MV_UP },
7578 { +1, -1, MV_RIGHT | MV_UP },
7579 { +1, 0, MV_RIGHT },
7580 { +1, +1, MV_RIGHT | MV_DOWN },
7582 { -1, +1, MV_LEFT | MV_DOWN },
7585 int start_pos, check_order;
7586 boolean can_clone = FALSE;
7589 /* check if there is any free field around current position */
7590 for (i = 0; i < 8; i++)
7592 int newx = x + check_xy[i].dx;
7593 int newy = y + check_xy[i].dy;
7595 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7603 if (can_clone) /* randomly find an element to clone */
7607 start_pos = check_pos[RND(8)];
7608 check_order = (RND(2) ? -1 : +1);
7610 for (i = 0; i < 8; i++)
7612 int pos_raw = start_pos + i * check_order;
7613 int pos = (pos_raw + 8) % 8;
7614 int newx = x + check_xy[pos].dx;
7615 int newy = y + check_xy[pos].dy;
7617 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7619 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7620 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7622 Store[x][y] = Feld[newx][newy];
7631 if (can_clone) /* randomly find a direction to move */
7635 start_pos = check_pos[RND(8)];
7636 check_order = (RND(2) ? -1 : +1);
7638 for (i = 0; i < 8; i++)
7640 int pos_raw = start_pos + i * check_order;
7641 int pos = (pos_raw + 8) % 8;
7642 int newx = x + check_xy[pos].dx;
7643 int newy = y + check_xy[pos].dy;
7644 int new_move_dir = check_xy[pos].dir;
7646 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7648 MovDir[x][y] = new_move_dir;
7649 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7658 if (can_clone) /* cloning and moving successful */
7661 /* cannot clone -- try to move towards player */
7663 start_pos = check_pos[MovDir[x][y] & 0x0f];
7664 check_order = (RND(2) ? -1 : +1);
7666 for (i = 0; i < 3; i++)
7668 /* first check start_pos, then previous/next or (next/previous) pos */
7669 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7670 int pos = (pos_raw + 8) % 8;
7671 int newx = x + check_xy[pos].dx;
7672 int newy = y + check_xy[pos].dy;
7673 int new_move_dir = check_xy[pos].dir;
7675 if (IS_PLAYER(newx, newy))
7678 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7680 MovDir[x][y] = new_move_dir;
7681 MovDelay[x][y] = level.android_move_time * 8 + 1;
7688 else if (move_pattern == MV_TURNING_LEFT ||
7689 move_pattern == MV_TURNING_RIGHT ||
7690 move_pattern == MV_TURNING_LEFT_RIGHT ||
7691 move_pattern == MV_TURNING_RIGHT_LEFT ||
7692 move_pattern == MV_TURNING_RANDOM ||
7693 move_pattern == MV_ALL_DIRECTIONS)
7695 boolean can_turn_left =
7696 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7697 boolean can_turn_right =
7698 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7700 if (element_info[element].move_stepsize == 0) /* "not moving" */
7703 if (move_pattern == MV_TURNING_LEFT)
7704 MovDir[x][y] = left_dir;
7705 else if (move_pattern == MV_TURNING_RIGHT)
7706 MovDir[x][y] = right_dir;
7707 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7708 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7709 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7710 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7711 else if (move_pattern == MV_TURNING_RANDOM)
7712 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7713 can_turn_right && !can_turn_left ? right_dir :
7714 RND(2) ? left_dir : right_dir);
7715 else if (can_turn_left && can_turn_right)
7716 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7717 else if (can_turn_left)
7718 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7719 else if (can_turn_right)
7720 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7722 MovDir[x][y] = back_dir;
7724 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7726 else if (move_pattern == MV_HORIZONTAL ||
7727 move_pattern == MV_VERTICAL)
7729 if (move_pattern & old_move_dir)
7730 MovDir[x][y] = back_dir;
7731 else if (move_pattern == MV_HORIZONTAL)
7732 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7733 else if (move_pattern == MV_VERTICAL)
7734 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7736 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738 else if (move_pattern & MV_ANY_DIRECTION)
7740 MovDir[x][y] = move_pattern;
7741 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7743 else if (move_pattern & MV_WIND_DIRECTION)
7745 MovDir[x][y] = game.wind_direction;
7746 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7748 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7750 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7751 MovDir[x][y] = left_dir;
7752 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7753 MovDir[x][y] = right_dir;
7755 if (MovDir[x][y] != old_move_dir)
7756 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7758 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7760 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7761 MovDir[x][y] = right_dir;
7762 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763 MovDir[x][y] = left_dir;
7765 if (MovDir[x][y] != old_move_dir)
7766 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7768 else if (move_pattern == MV_TOWARDS_PLAYER ||
7769 move_pattern == MV_AWAY_FROM_PLAYER)
7771 int attr_x = -1, attr_y = -1;
7773 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7784 for (i = 0; i < MAX_PLAYERS; i++)
7786 struct PlayerInfo *player = &stored_player[i];
7787 int jx = player->jx, jy = player->jy;
7789 if (!player->active)
7793 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7801 MovDir[x][y] = MV_NONE;
7803 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7804 else if (attr_x > x)
7805 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7807 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7808 else if (attr_y > y)
7809 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7811 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7813 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7815 boolean first_horiz = RND(2);
7816 int new_move_dir = MovDir[x][y];
7818 if (element_info[element].move_stepsize == 0) /* "not moving" */
7820 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7821 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7827 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7828 Moving2Blocked(x, y, &newx, &newy);
7830 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7834 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7835 Moving2Blocked(x, y, &newx, &newy);
7837 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7840 MovDir[x][y] = old_move_dir;
7843 else if (move_pattern == MV_WHEN_PUSHED ||
7844 move_pattern == MV_WHEN_DROPPED)
7846 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7847 MovDir[x][y] = MV_NONE;
7851 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7853 static int test_xy[7][2] =
7863 static int test_dir[7] =
7873 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7874 int move_preference = -1000000; /* start with very low preference */
7875 int new_move_dir = MV_NONE;
7876 int start_test = RND(4);
7879 for (i = 0; i < NUM_DIRECTIONS; i++)
7881 int move_dir = test_dir[start_test + i];
7882 int move_dir_preference;
7884 xx = x + test_xy[start_test + i][0];
7885 yy = y + test_xy[start_test + i][1];
7887 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7888 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7890 new_move_dir = move_dir;
7895 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7898 move_dir_preference = -1 * RunnerVisit[xx][yy];
7899 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7900 move_dir_preference = PlayerVisit[xx][yy];
7902 if (move_dir_preference > move_preference)
7904 /* prefer field that has not been visited for the longest time */
7905 move_preference = move_dir_preference;
7906 new_move_dir = move_dir;
7908 else if (move_dir_preference == move_preference &&
7909 move_dir == old_move_dir)
7911 /* prefer last direction when all directions are preferred equally */
7912 move_preference = move_dir_preference;
7913 new_move_dir = move_dir;
7917 MovDir[x][y] = new_move_dir;
7918 if (old_move_dir != new_move_dir)
7919 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7923 static void TurnRound(int x, int y)
7925 int direction = MovDir[x][y];
7929 GfxDir[x][y] = MovDir[x][y];
7931 if (direction != MovDir[x][y])
7935 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7937 ResetGfxFrame(x, y, FALSE);
7940 static boolean JustBeingPushed(int x, int y)
7944 for (i = 0; i < MAX_PLAYERS; i++)
7946 struct PlayerInfo *player = &stored_player[i];
7948 if (player->active && player->is_pushing && player->MovPos)
7950 int next_jx = player->jx + (player->jx - player->last_jx);
7951 int next_jy = player->jy + (player->jy - player->last_jy);
7953 if (x == next_jx && y == next_jy)
7961 void StartMoving(int x, int y)
7963 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7964 int element = Feld[x][y];
7969 if (MovDelay[x][y] == 0)
7970 GfxAction[x][y] = ACTION_DEFAULT;
7972 if (CAN_FALL(element) && y < lev_fieldy - 1)
7974 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7975 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7976 if (JustBeingPushed(x, y))
7979 if (element == EL_QUICKSAND_FULL)
7981 if (IS_FREE(x, y + 1))
7983 InitMovingField(x, y, MV_DOWN);
7984 started_moving = TRUE;
7986 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7987 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7988 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7989 Store[x][y] = EL_ROCK;
7991 Store[x][y] = EL_ROCK;
7994 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7996 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7998 if (!MovDelay[x][y])
8000 MovDelay[x][y] = TILEY + 1;
8002 ResetGfxAnimation(x, y);
8003 ResetGfxAnimation(x, y + 1);
8008 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8009 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8016 Feld[x][y] = EL_QUICKSAND_EMPTY;
8017 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8018 Store[x][y + 1] = Store[x][y];
8021 PlayLevelSoundAction(x, y, ACTION_FILLING);
8023 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8025 if (!MovDelay[x][y])
8027 MovDelay[x][y] = TILEY + 1;
8029 ResetGfxAnimation(x, y);
8030 ResetGfxAnimation(x, y + 1);
8035 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8036 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8043 Feld[x][y] = EL_QUICKSAND_EMPTY;
8044 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8045 Store[x][y + 1] = Store[x][y];
8048 PlayLevelSoundAction(x, y, ACTION_FILLING);
8051 else if (element == EL_QUICKSAND_FAST_FULL)
8053 if (IS_FREE(x, y + 1))
8055 InitMovingField(x, y, MV_DOWN);
8056 started_moving = TRUE;
8058 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8059 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8060 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8061 Store[x][y] = EL_ROCK;
8063 Store[x][y] = EL_ROCK;
8066 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8068 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8070 if (!MovDelay[x][y])
8072 MovDelay[x][y] = TILEY + 1;
8074 ResetGfxAnimation(x, y);
8075 ResetGfxAnimation(x, y + 1);
8080 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8081 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8088 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8089 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8090 Store[x][y + 1] = Store[x][y];
8093 PlayLevelSoundAction(x, y, ACTION_FILLING);
8095 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8097 if (!MovDelay[x][y])
8099 MovDelay[x][y] = TILEY + 1;
8101 ResetGfxAnimation(x, y);
8102 ResetGfxAnimation(x, y + 1);
8107 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8108 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8115 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8116 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8117 Store[x][y + 1] = Store[x][y];
8120 PlayLevelSoundAction(x, y, ACTION_FILLING);
8123 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8124 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8126 InitMovingField(x, y, MV_DOWN);
8127 started_moving = TRUE;
8129 Feld[x][y] = EL_QUICKSAND_FILLING;
8130 Store[x][y] = element;
8132 PlayLevelSoundAction(x, y, ACTION_FILLING);
8134 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8135 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8137 InitMovingField(x, y, MV_DOWN);
8138 started_moving = TRUE;
8140 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8141 Store[x][y] = element;
8143 PlayLevelSoundAction(x, y, ACTION_FILLING);
8145 else if (element == EL_MAGIC_WALL_FULL)
8147 if (IS_FREE(x, y + 1))
8149 InitMovingField(x, y, MV_DOWN);
8150 started_moving = TRUE;
8152 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8153 Store[x][y] = EL_CHANGED(Store[x][y]);
8155 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8157 if (!MovDelay[x][y])
8158 MovDelay[x][y] = TILEY / 4 + 1;
8167 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8168 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8169 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8173 else if (element == EL_BD_MAGIC_WALL_FULL)
8175 if (IS_FREE(x, y + 1))
8177 InitMovingField(x, y, MV_DOWN);
8178 started_moving = TRUE;
8180 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8181 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8183 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8185 if (!MovDelay[x][y])
8186 MovDelay[x][y] = TILEY / 4 + 1;
8195 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8196 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8197 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8201 else if (element == EL_DC_MAGIC_WALL_FULL)
8203 if (IS_FREE(x, y + 1))
8205 InitMovingField(x, y, MV_DOWN);
8206 started_moving = TRUE;
8208 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8209 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8211 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8213 if (!MovDelay[x][y])
8214 MovDelay[x][y] = TILEY / 4 + 1;
8223 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8224 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8225 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8229 else if ((CAN_PASS_MAGIC_WALL(element) &&
8230 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8231 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8232 (CAN_PASS_DC_MAGIC_WALL(element) &&
8233 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8236 InitMovingField(x, y, MV_DOWN);
8237 started_moving = TRUE;
8240 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8241 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8242 EL_DC_MAGIC_WALL_FILLING);
8243 Store[x][y] = element;
8245 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8247 SplashAcid(x, y + 1);
8249 InitMovingField(x, y, MV_DOWN);
8250 started_moving = TRUE;
8252 Store[x][y] = EL_ACID;
8255 #if USE_FIX_IMPACT_COLLISION
8256 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8257 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8259 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8260 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8262 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8263 CAN_FALL(element) && WasJustFalling[x][y] &&
8264 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8266 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8267 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8268 (Feld[x][y + 1] == EL_BLOCKED)))
8270 /* this is needed for a special case not covered by calling "Impact()"
8271 from "ContinueMoving()": if an element moves to a tile directly below
8272 another element which was just falling on that tile (which was empty
8273 in the previous frame), the falling element above would just stop
8274 instead of smashing the element below (in previous version, the above
8275 element was just checked for "moving" instead of "falling", resulting
8276 in incorrect smashes caused by horizontal movement of the above
8277 element; also, the case of the player being the element to smash was
8278 simply not covered here... :-/ ) */
8280 CheckCollision[x][y] = 0;
8281 CheckImpact[x][y] = 0;
8285 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8287 if (MovDir[x][y] == MV_NONE)
8289 InitMovingField(x, y, MV_DOWN);
8290 started_moving = TRUE;
8293 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8295 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8296 MovDir[x][y] = MV_DOWN;
8298 InitMovingField(x, y, MV_DOWN);
8299 started_moving = TRUE;
8301 else if (element == EL_AMOEBA_DROP)
8303 Feld[x][y] = EL_AMOEBA_GROWING;
8304 Store[x][y] = EL_AMOEBA_WET;
8306 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8307 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8308 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8309 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8311 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8312 (IS_FREE(x - 1, y + 1) ||
8313 Feld[x - 1][y + 1] == EL_ACID));
8314 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8315 (IS_FREE(x + 1, y + 1) ||
8316 Feld[x + 1][y + 1] == EL_ACID));
8317 boolean can_fall_any = (can_fall_left || can_fall_right);
8318 boolean can_fall_both = (can_fall_left && can_fall_right);
8319 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8321 #if USE_NEW_ALL_SLIPPERY
8322 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8324 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8325 can_fall_right = FALSE;
8326 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8327 can_fall_left = FALSE;
8328 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8329 can_fall_right = FALSE;
8330 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8331 can_fall_left = FALSE;
8333 can_fall_any = (can_fall_left || can_fall_right);
8334 can_fall_both = FALSE;
8337 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8339 if (slippery_type == SLIPPERY_ONLY_LEFT)
8340 can_fall_right = FALSE;
8341 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8342 can_fall_left = FALSE;
8343 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8344 can_fall_right = FALSE;
8345 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8346 can_fall_left = FALSE;
8348 can_fall_any = (can_fall_left || can_fall_right);
8349 can_fall_both = (can_fall_left && can_fall_right);
8353 #if USE_NEW_ALL_SLIPPERY
8355 #if USE_NEW_SP_SLIPPERY
8356 /* !!! better use the same properties as for custom elements here !!! */
8357 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8358 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8360 can_fall_right = FALSE; /* slip down on left side */
8361 can_fall_both = FALSE;
8366 #if USE_NEW_ALL_SLIPPERY
8369 if (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;
8379 if (game.emulation == EMU_BOULDERDASH ||
8380 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8381 can_fall_right = FALSE; /* slip down on left side */
8383 can_fall_left = !(can_fall_right = RND(2));
8385 can_fall_both = FALSE;
8391 /* if not determined otherwise, prefer left side for slipping down */
8392 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8393 started_moving = TRUE;
8397 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8399 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8402 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8403 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8404 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8405 int belt_dir = game.belt_dir[belt_nr];
8407 if ((belt_dir == MV_LEFT && left_is_free) ||
8408 (belt_dir == MV_RIGHT && right_is_free))
8410 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8412 InitMovingField(x, y, belt_dir);
8413 started_moving = TRUE;
8415 Pushed[x][y] = TRUE;
8416 Pushed[nextx][y] = TRUE;
8418 GfxAction[x][y] = ACTION_DEFAULT;
8422 MovDir[x][y] = 0; /* if element was moving, stop it */
8427 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8429 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8431 if (CAN_MOVE(element) && !started_moving)
8434 int move_pattern = element_info[element].move_pattern;
8439 if (MovDir[x][y] == MV_NONE)
8441 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8442 x, y, element, element_info[element].token_name);
8443 printf("StartMoving(): This should never happen!\n");
8448 Moving2Blocked(x, y, &newx, &newy);
8450 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8453 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8454 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8456 WasJustMoving[x][y] = 0;
8457 CheckCollision[x][y] = 0;
8459 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8461 if (Feld[x][y] != element) /* element has changed */
8465 if (!MovDelay[x][y]) /* start new movement phase */
8467 /* all objects that can change their move direction after each step
8468 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8470 if (element != EL_YAMYAM &&
8471 element != EL_DARK_YAMYAM &&
8472 element != EL_PACMAN &&
8473 !(move_pattern & MV_ANY_DIRECTION) &&
8474 move_pattern != MV_TURNING_LEFT &&
8475 move_pattern != MV_TURNING_RIGHT &&
8476 move_pattern != MV_TURNING_LEFT_RIGHT &&
8477 move_pattern != MV_TURNING_RIGHT_LEFT &&
8478 move_pattern != MV_TURNING_RANDOM)
8482 if (MovDelay[x][y] && (element == EL_BUG ||
8483 element == EL_SPACESHIP ||
8484 element == EL_SP_SNIKSNAK ||
8485 element == EL_SP_ELECTRON ||
8486 element == EL_MOLE))
8487 TEST_DrawLevelField(x, y);
8491 if (MovDelay[x][y]) /* wait some time before next movement */
8495 if (element == EL_ROBOT ||
8496 element == EL_YAMYAM ||
8497 element == EL_DARK_YAMYAM)
8499 DrawLevelElementAnimationIfNeeded(x, y, element);
8500 PlayLevelSoundAction(x, y, ACTION_WAITING);
8502 else if (element == EL_SP_ELECTRON)
8503 DrawLevelElementAnimationIfNeeded(x, y, element);
8504 else if (element == EL_DRAGON)
8507 int dir = MovDir[x][y];
8508 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8509 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8510 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8511 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8512 dir == MV_UP ? IMG_FLAMES_1_UP :
8513 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8514 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8516 GfxAction[x][y] = ACTION_ATTACKING;
8518 if (IS_PLAYER(x, y))
8519 DrawPlayerField(x, y);
8521 TEST_DrawLevelField(x, y);
8523 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8525 for (i = 1; i <= 3; i++)
8527 int xx = x + i * dx;
8528 int yy = y + i * dy;
8529 int sx = SCREENX(xx);
8530 int sy = SCREENY(yy);
8531 int flame_graphic = graphic + (i - 1);
8533 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8538 int flamed = MovingOrBlocked2Element(xx, yy);
8542 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8544 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8545 RemoveMovingField(xx, yy);
8547 RemoveField(xx, yy);
8549 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8552 RemoveMovingField(xx, yy);
8555 ChangeDelay[xx][yy] = 0;
8557 Feld[xx][yy] = EL_FLAMES;
8559 if (IN_SCR_FIELD(sx, sy))
8561 TEST_DrawLevelFieldCrumbled(xx, yy);
8562 DrawGraphic(sx, sy, flame_graphic, frame);
8567 if (Feld[xx][yy] == EL_FLAMES)
8568 Feld[xx][yy] = EL_EMPTY;
8569 TEST_DrawLevelField(xx, yy);
8574 if (MovDelay[x][y]) /* element still has to wait some time */
8576 PlayLevelSoundAction(x, y, ACTION_WAITING);
8582 /* now make next step */
8584 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8586 if (DONT_COLLIDE_WITH(element) &&
8587 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8588 !PLAYER_ENEMY_PROTECTED(newx, newy))
8590 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8595 else if (CAN_MOVE_INTO_ACID(element) &&
8596 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8597 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8598 (MovDir[x][y] == MV_DOWN ||
8599 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8601 SplashAcid(newx, newy);
8602 Store[x][y] = EL_ACID;
8604 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8606 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8607 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8608 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8609 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8612 TEST_DrawLevelField(x, y);
8614 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8615 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8616 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8618 local_player->friends_still_needed--;
8619 if (!local_player->friends_still_needed &&
8620 !local_player->GameOver && AllPlayersGone)
8621 PlayerWins(local_player);
8625 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8627 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8628 TEST_DrawLevelField(newx, newy);
8630 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8632 else if (!IS_FREE(newx, newy))
8634 GfxAction[x][y] = ACTION_WAITING;
8636 if (IS_PLAYER(x, y))
8637 DrawPlayerField(x, y);
8639 TEST_DrawLevelField(x, y);
8644 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8646 if (IS_FOOD_PIG(Feld[newx][newy]))
8648 if (IS_MOVING(newx, newy))
8649 RemoveMovingField(newx, newy);
8652 Feld[newx][newy] = EL_EMPTY;
8653 TEST_DrawLevelField(newx, newy);
8656 PlayLevelSound(x, y, SND_PIG_DIGGING);
8658 else if (!IS_FREE(newx, newy))
8660 if (IS_PLAYER(x, y))
8661 DrawPlayerField(x, y);
8663 TEST_DrawLevelField(x, y);
8668 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8670 if (Store[x][y] != EL_EMPTY)
8672 boolean can_clone = FALSE;
8675 /* check if element to clone is still there */
8676 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8678 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8686 /* cannot clone or target field not free anymore -- do not clone */
8687 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8688 Store[x][y] = EL_EMPTY;
8691 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8693 if (IS_MV_DIAGONAL(MovDir[x][y]))
8695 int diagonal_move_dir = MovDir[x][y];
8696 int stored = Store[x][y];
8697 int change_delay = 8;
8700 /* android is moving diagonally */
8702 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8704 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8705 GfxElement[x][y] = EL_EMC_ANDROID;
8706 GfxAction[x][y] = ACTION_SHRINKING;
8707 GfxDir[x][y] = diagonal_move_dir;
8708 ChangeDelay[x][y] = change_delay;
8710 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8713 DrawLevelGraphicAnimation(x, y, graphic);
8714 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8716 if (Feld[newx][newy] == EL_ACID)
8718 SplashAcid(newx, newy);
8723 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8725 Store[newx][newy] = EL_EMC_ANDROID;
8726 GfxElement[newx][newy] = EL_EMC_ANDROID;
8727 GfxAction[newx][newy] = ACTION_GROWING;
8728 GfxDir[newx][newy] = diagonal_move_dir;
8729 ChangeDelay[newx][newy] = change_delay;
8731 graphic = el_act_dir2img(GfxElement[newx][newy],
8732 GfxAction[newx][newy], GfxDir[newx][newy]);
8734 DrawLevelGraphicAnimation(newx, newy, graphic);
8735 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8741 Feld[newx][newy] = EL_EMPTY;
8742 TEST_DrawLevelField(newx, newy);
8744 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8747 else if (!IS_FREE(newx, newy))
8750 if (IS_PLAYER(x, y))
8751 DrawPlayerField(x, y);
8753 TEST_DrawLevelField(x, y);
8759 else if (IS_CUSTOM_ELEMENT(element) &&
8760 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8763 if (!DigFieldByCE(newx, newy, element))
8766 int new_element = Feld[newx][newy];
8768 if (!IS_FREE(newx, newy))
8770 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8771 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8774 /* no element can dig solid indestructible elements */
8775 if (IS_INDESTRUCTIBLE(new_element) &&
8776 !IS_DIGGABLE(new_element) &&
8777 !IS_COLLECTIBLE(new_element))
8780 if (AmoebaNr[newx][newy] &&
8781 (new_element == EL_AMOEBA_FULL ||
8782 new_element == EL_BD_AMOEBA ||
8783 new_element == EL_AMOEBA_GROWING))
8785 AmoebaCnt[AmoebaNr[newx][newy]]--;
8786 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8789 if (IS_MOVING(newx, newy))
8790 RemoveMovingField(newx, newy);
8793 RemoveField(newx, newy);
8794 TEST_DrawLevelField(newx, newy);
8797 /* if digged element was about to explode, prevent the explosion */
8798 ExplodeField[newx][newy] = EX_TYPE_NONE;
8800 PlayLevelSoundAction(x, y, action);
8803 Store[newx][newy] = EL_EMPTY;
8806 /* this makes it possible to leave the removed element again */
8807 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8808 Store[newx][newy] = new_element;
8810 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8812 int move_leave_element = element_info[element].move_leave_element;
8814 /* this makes it possible to leave the removed element again */
8815 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8816 new_element : move_leave_element);
8822 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8824 RunnerVisit[x][y] = FrameCounter;
8825 PlayerVisit[x][y] /= 8; /* expire player visit path */
8828 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8830 if (!IS_FREE(newx, newy))
8832 if (IS_PLAYER(x, y))
8833 DrawPlayerField(x, y);
8835 TEST_DrawLevelField(x, y);
8841 boolean wanna_flame = !RND(10);
8842 int dx = newx - x, dy = newy - y;
8843 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8844 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8845 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8846 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8847 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8848 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8851 IS_CLASSIC_ENEMY(element1) ||
8852 IS_CLASSIC_ENEMY(element2)) &&
8853 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8854 element1 != EL_FLAMES && element2 != EL_FLAMES)
8856 ResetGfxAnimation(x, y);
8857 GfxAction[x][y] = ACTION_ATTACKING;
8859 if (IS_PLAYER(x, y))
8860 DrawPlayerField(x, y);
8862 TEST_DrawLevelField(x, y);
8864 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8866 MovDelay[x][y] = 50;
8870 RemoveField(newx, newy);
8872 Feld[newx][newy] = EL_FLAMES;
8873 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8876 RemoveField(newx1, newy1);
8878 Feld[newx1][newy1] = EL_FLAMES;
8880 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8883 RemoveField(newx2, newy2);
8885 Feld[newx2][newy2] = EL_FLAMES;
8892 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8893 Feld[newx][newy] == EL_DIAMOND)
8895 if (IS_MOVING(newx, newy))
8896 RemoveMovingField(newx, newy);
8899 Feld[newx][newy] = EL_EMPTY;
8900 TEST_DrawLevelField(newx, newy);
8903 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8905 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8906 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8908 if (AmoebaNr[newx][newy])
8910 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8911 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8912 Feld[newx][newy] == EL_BD_AMOEBA)
8913 AmoebaCnt[AmoebaNr[newx][newy]]--;
8918 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8920 RemoveMovingField(newx, newy);
8923 if (IS_MOVING(newx, newy))
8925 RemoveMovingField(newx, newy);
8930 Feld[newx][newy] = EL_EMPTY;
8931 TEST_DrawLevelField(newx, newy);
8934 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8936 else if ((element == EL_PACMAN || element == EL_MOLE)
8937 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8939 if (AmoebaNr[newx][newy])
8941 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8942 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8943 Feld[newx][newy] == EL_BD_AMOEBA)
8944 AmoebaCnt[AmoebaNr[newx][newy]]--;
8947 if (element == EL_MOLE)
8949 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8950 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8952 ResetGfxAnimation(x, y);
8953 GfxAction[x][y] = ACTION_DIGGING;
8954 TEST_DrawLevelField(x, y);
8956 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8958 return; /* wait for shrinking amoeba */
8960 else /* element == EL_PACMAN */
8962 Feld[newx][newy] = EL_EMPTY;
8963 TEST_DrawLevelField(newx, newy);
8964 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8967 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8968 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8969 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8971 /* wait for shrinking amoeba to completely disappear */
8974 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8976 /* object was running against a wall */
8981 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8982 if (move_pattern & MV_ANY_DIRECTION &&
8983 move_pattern == MovDir[x][y])
8985 int blocking_element =
8986 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8988 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8991 element = Feld[x][y]; /* element might have changed */
8995 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8996 DrawLevelElementAnimation(x, y, element);
8998 if (DONT_TOUCH(element))
8999 TestIfBadThingTouchesPlayer(x, y);
9004 InitMovingField(x, y, MovDir[x][y]);
9006 PlayLevelSoundAction(x, y, ACTION_MOVING);
9010 ContinueMoving(x, y);
9013 void ContinueMoving(int x, int y)
9015 int element = Feld[x][y];
9016 struct ElementInfo *ei = &element_info[element];
9017 int direction = MovDir[x][y];
9018 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9019 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9020 int newx = x + dx, newy = y + dy;
9021 int stored = Store[x][y];
9022 int stored_new = Store[newx][newy];
9023 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9024 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9025 boolean last_line = (newy == lev_fieldy - 1);
9027 MovPos[x][y] += getElementMoveStepsize(x, y);
9029 if (pushed_by_player) /* special case: moving object pushed by player */
9030 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9032 if (ABS(MovPos[x][y]) < TILEX)
9035 int ee = Feld[x][y];
9036 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9037 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9039 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9040 x, y, ABS(MovPos[x][y]),
9042 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9045 TEST_DrawLevelField(x, y);
9047 return; /* element is still moving */
9050 /* element reached destination field */
9052 Feld[x][y] = EL_EMPTY;
9053 Feld[newx][newy] = element;
9054 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9056 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9058 element = Feld[newx][newy] = EL_ACID;
9060 else if (element == EL_MOLE)
9062 Feld[x][y] = EL_SAND;
9064 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9066 else if (element == EL_QUICKSAND_FILLING)
9068 element = Feld[newx][newy] = get_next_element(element);
9069 Store[newx][newy] = Store[x][y];
9071 else if (element == EL_QUICKSAND_EMPTYING)
9073 Feld[x][y] = get_next_element(element);
9074 element = Feld[newx][newy] = Store[x][y];
9076 else if (element == EL_QUICKSAND_FAST_FILLING)
9078 element = Feld[newx][newy] = get_next_element(element);
9079 Store[newx][newy] = Store[x][y];
9081 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9083 Feld[x][y] = get_next_element(element);
9084 element = Feld[newx][newy] = Store[x][y];
9086 else if (element == EL_MAGIC_WALL_FILLING)
9088 element = Feld[newx][newy] = get_next_element(element);
9089 if (!game.magic_wall_active)
9090 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9091 Store[newx][newy] = Store[x][y];
9093 else if (element == EL_MAGIC_WALL_EMPTYING)
9095 Feld[x][y] = get_next_element(element);
9096 if (!game.magic_wall_active)
9097 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9098 element = Feld[newx][newy] = Store[x][y];
9100 #if USE_NEW_CUSTOM_VALUE
9101 InitField(newx, newy, FALSE);
9104 else if (element == EL_BD_MAGIC_WALL_FILLING)
9106 element = Feld[newx][newy] = get_next_element(element);
9107 if (!game.magic_wall_active)
9108 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9109 Store[newx][newy] = Store[x][y];
9111 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9113 Feld[x][y] = get_next_element(element);
9114 if (!game.magic_wall_active)
9115 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9116 element = Feld[newx][newy] = Store[x][y];
9118 #if USE_NEW_CUSTOM_VALUE
9119 InitField(newx, newy, FALSE);
9122 else if (element == EL_DC_MAGIC_WALL_FILLING)
9124 element = Feld[newx][newy] = get_next_element(element);
9125 if (!game.magic_wall_active)
9126 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9127 Store[newx][newy] = Store[x][y];
9129 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9131 Feld[x][y] = get_next_element(element);
9132 if (!game.magic_wall_active)
9133 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9134 element = Feld[newx][newy] = Store[x][y];
9136 #if USE_NEW_CUSTOM_VALUE
9137 InitField(newx, newy, FALSE);
9140 else if (element == EL_AMOEBA_DROPPING)
9142 Feld[x][y] = get_next_element(element);
9143 element = Feld[newx][newy] = Store[x][y];
9145 else if (element == EL_SOKOBAN_OBJECT)
9148 Feld[x][y] = Back[x][y];
9150 if (Back[newx][newy])
9151 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9153 Back[x][y] = Back[newx][newy] = 0;
9156 Store[x][y] = EL_EMPTY;
9161 MovDelay[newx][newy] = 0;
9163 if (CAN_CHANGE_OR_HAS_ACTION(element))
9165 /* copy element change control values to new field */
9166 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9167 ChangePage[newx][newy] = ChangePage[x][y];
9168 ChangeCount[newx][newy] = ChangeCount[x][y];
9169 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9172 #if USE_NEW_CUSTOM_VALUE
9173 CustomValue[newx][newy] = CustomValue[x][y];
9176 ChangeDelay[x][y] = 0;
9177 ChangePage[x][y] = -1;
9178 ChangeCount[x][y] = 0;
9179 ChangeEvent[x][y] = -1;
9181 #if USE_NEW_CUSTOM_VALUE
9182 CustomValue[x][y] = 0;
9185 /* copy animation control values to new field */
9186 GfxFrame[newx][newy] = GfxFrame[x][y];
9187 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9188 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9189 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9191 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9193 /* some elements can leave other elements behind after moving */
9195 if (ei->move_leave_element != EL_EMPTY &&
9196 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9197 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9199 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9200 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9201 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9204 int move_leave_element = ei->move_leave_element;
9208 /* this makes it possible to leave the removed element again */
9209 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9210 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9212 /* this makes it possible to leave the removed element again */
9213 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9214 move_leave_element = stored;
9217 /* this makes it possible to leave the removed element again */
9218 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9219 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9220 move_leave_element = stored;
9223 Feld[x][y] = move_leave_element;
9225 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9226 MovDir[x][y] = direction;
9228 InitField(x, y, FALSE);
9230 if (GFX_CRUMBLED(Feld[x][y]))
9231 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9233 if (ELEM_IS_PLAYER(move_leave_element))
9234 RelocatePlayer(x, y, move_leave_element);
9237 /* do this after checking for left-behind element */
9238 ResetGfxAnimation(x, y); /* reset animation values for old field */
9240 if (!CAN_MOVE(element) ||
9241 (CAN_FALL(element) && direction == MV_DOWN &&
9242 (element == EL_SPRING ||
9243 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9244 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9245 GfxDir[x][y] = MovDir[newx][newy] = 0;
9247 TEST_DrawLevelField(x, y);
9248 TEST_DrawLevelField(newx, newy);
9250 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9252 /* prevent pushed element from moving on in pushed direction */
9253 if (pushed_by_player && CAN_MOVE(element) &&
9254 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9255 !(element_info[element].move_pattern & direction))
9256 TurnRound(newx, newy);
9258 /* prevent elements on conveyor belt from moving on in last direction */
9259 if (pushed_by_conveyor && CAN_FALL(element) &&
9260 direction & MV_HORIZONTAL)
9261 MovDir[newx][newy] = 0;
9263 if (!pushed_by_player)
9265 int nextx = newx + dx, nexty = newy + dy;
9266 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9268 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9270 if (CAN_FALL(element) && direction == MV_DOWN)
9271 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9273 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9274 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9276 #if USE_FIX_IMPACT_COLLISION
9277 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9278 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9282 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9284 TestIfBadThingTouchesPlayer(newx, newy);
9285 TestIfBadThingTouchesFriend(newx, newy);
9287 if (!IS_CUSTOM_ELEMENT(element))
9288 TestIfBadThingTouchesOtherBadThing(newx, newy);
9290 else if (element == EL_PENGUIN)
9291 TestIfFriendTouchesBadThing(newx, newy);
9293 if (DONT_GET_HIT_BY(element))
9295 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9298 /* give the player one last chance (one more frame) to move away */
9299 if (CAN_FALL(element) && direction == MV_DOWN &&
9300 (last_line || (!IS_FREE(x, newy + 1) &&
9301 (!IS_PLAYER(x, newy + 1) ||
9302 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9305 if (pushed_by_player && !game.use_change_when_pushing_bug)
9307 int push_side = MV_DIR_OPPOSITE(direction);
9308 struct PlayerInfo *player = PLAYERINFO(x, y);
9310 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9311 player->index_bit, push_side);
9312 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9313 player->index_bit, push_side);
9316 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9317 MovDelay[newx][newy] = 1;
9319 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9321 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9324 if (ChangePage[newx][newy] != -1) /* delayed change */
9326 int page = ChangePage[newx][newy];
9327 struct ElementChangeInfo *change = &ei->change_page[page];
9329 ChangePage[newx][newy] = -1;
9331 if (change->can_change)
9333 if (ChangeElement(newx, newy, element, page))
9335 if (change->post_change_function)
9336 change->post_change_function(newx, newy);
9340 if (change->has_action)
9341 ExecuteCustomElementAction(newx, newy, element, page);
9345 TestIfElementHitsCustomElement(newx, newy, direction);
9346 TestIfPlayerTouchesCustomElement(newx, newy);
9347 TestIfElementTouchesCustomElement(newx, newy);
9349 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9350 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9351 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9352 MV_DIR_OPPOSITE(direction));
9355 int AmoebeNachbarNr(int ax, int ay)
9358 int element = Feld[ax][ay];
9360 static int xy[4][2] =
9368 for (i = 0; i < NUM_DIRECTIONS; i++)
9370 int x = ax + xy[i][0];
9371 int y = ay + xy[i][1];
9373 if (!IN_LEV_FIELD(x, y))
9376 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9377 group_nr = AmoebaNr[x][y];
9383 void AmoebenVereinigen(int ax, int ay)
9385 int i, x, y, xx, yy;
9386 int new_group_nr = AmoebaNr[ax][ay];
9387 static int xy[4][2] =
9395 if (new_group_nr == 0)
9398 for (i = 0; i < NUM_DIRECTIONS; i++)
9403 if (!IN_LEV_FIELD(x, y))
9406 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9407 Feld[x][y] == EL_BD_AMOEBA ||
9408 Feld[x][y] == EL_AMOEBA_DEAD) &&
9409 AmoebaNr[x][y] != new_group_nr)
9411 int old_group_nr = AmoebaNr[x][y];
9413 if (old_group_nr == 0)
9416 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9417 AmoebaCnt[old_group_nr] = 0;
9418 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9419 AmoebaCnt2[old_group_nr] = 0;
9421 SCAN_PLAYFIELD(xx, yy)
9423 if (AmoebaNr[xx][yy] == old_group_nr)
9424 AmoebaNr[xx][yy] = new_group_nr;
9430 void AmoebeUmwandeln(int ax, int ay)
9434 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9436 int group_nr = AmoebaNr[ax][ay];
9441 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9442 printf("AmoebeUmwandeln(): This should never happen!\n");
9447 SCAN_PLAYFIELD(x, y)
9449 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9452 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9456 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9457 SND_AMOEBA_TURNING_TO_GEM :
9458 SND_AMOEBA_TURNING_TO_ROCK));
9463 static int xy[4][2] =
9471 for (i = 0; i < NUM_DIRECTIONS; i++)
9476 if (!IN_LEV_FIELD(x, y))
9479 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9481 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9482 SND_AMOEBA_TURNING_TO_GEM :
9483 SND_AMOEBA_TURNING_TO_ROCK));
9490 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9493 int group_nr = AmoebaNr[ax][ay];
9494 boolean done = FALSE;
9499 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9500 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9505 SCAN_PLAYFIELD(x, y)
9507 if (AmoebaNr[x][y] == group_nr &&
9508 (Feld[x][y] == EL_AMOEBA_DEAD ||
9509 Feld[x][y] == EL_BD_AMOEBA ||
9510 Feld[x][y] == EL_AMOEBA_GROWING))
9513 Feld[x][y] = new_element;
9514 InitField(x, y, FALSE);
9515 TEST_DrawLevelField(x, y);
9521 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9522 SND_BD_AMOEBA_TURNING_TO_ROCK :
9523 SND_BD_AMOEBA_TURNING_TO_GEM));
9526 void AmoebeWaechst(int x, int y)
9528 static unsigned int sound_delay = 0;
9529 static unsigned int sound_delay_value = 0;
9531 if (!MovDelay[x][y]) /* start new growing cycle */
9535 if (DelayReached(&sound_delay, sound_delay_value))
9537 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9538 sound_delay_value = 30;
9542 if (MovDelay[x][y]) /* wait some time before growing bigger */
9545 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9547 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9548 6 - MovDelay[x][y]);
9550 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9553 if (!MovDelay[x][y])
9555 Feld[x][y] = Store[x][y];
9557 TEST_DrawLevelField(x, y);
9562 void AmoebaDisappearing(int x, int y)
9564 static unsigned int sound_delay = 0;
9565 static unsigned int sound_delay_value = 0;
9567 if (!MovDelay[x][y]) /* start new shrinking cycle */
9571 if (DelayReached(&sound_delay, sound_delay_value))
9572 sound_delay_value = 30;
9575 if (MovDelay[x][y]) /* wait some time before shrinking */
9578 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9580 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9581 6 - MovDelay[x][y]);
9583 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9586 if (!MovDelay[x][y])
9588 Feld[x][y] = EL_EMPTY;
9589 TEST_DrawLevelField(x, y);
9591 /* don't let mole enter this field in this cycle;
9592 (give priority to objects falling to this field from above) */
9598 void AmoebeAbleger(int ax, int ay)
9601 int element = Feld[ax][ay];
9602 int graphic = el2img(element);
9603 int newax = ax, neway = ay;
9604 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9605 static int xy[4][2] =
9613 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9615 Feld[ax][ay] = EL_AMOEBA_DEAD;
9616 TEST_DrawLevelField(ax, ay);
9620 if (IS_ANIMATED(graphic))
9621 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9623 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9624 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9626 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9629 if (MovDelay[ax][ay])
9633 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9636 int x = ax + xy[start][0];
9637 int y = ay + xy[start][1];
9639 if (!IN_LEV_FIELD(x, y))
9642 if (IS_FREE(x, y) ||
9643 CAN_GROW_INTO(Feld[x][y]) ||
9644 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9645 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9651 if (newax == ax && neway == ay)
9654 else /* normal or "filled" (BD style) amoeba */
9657 boolean waiting_for_player = FALSE;
9659 for (i = 0; i < NUM_DIRECTIONS; i++)
9661 int j = (start + i) % 4;
9662 int x = ax + xy[j][0];
9663 int y = ay + xy[j][1];
9665 if (!IN_LEV_FIELD(x, y))
9668 if (IS_FREE(x, y) ||
9669 CAN_GROW_INTO(Feld[x][y]) ||
9670 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9671 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9677 else if (IS_PLAYER(x, y))
9678 waiting_for_player = TRUE;
9681 if (newax == ax && neway == ay) /* amoeba cannot grow */
9683 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9685 Feld[ax][ay] = EL_AMOEBA_DEAD;
9686 TEST_DrawLevelField(ax, ay);
9687 AmoebaCnt[AmoebaNr[ax][ay]]--;
9689 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9691 if (element == EL_AMOEBA_FULL)
9692 AmoebeUmwandeln(ax, ay);
9693 else if (element == EL_BD_AMOEBA)
9694 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9699 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9701 /* amoeba gets larger by growing in some direction */
9703 int new_group_nr = AmoebaNr[ax][ay];
9706 if (new_group_nr == 0)
9708 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9709 printf("AmoebeAbleger(): This should never happen!\n");
9714 AmoebaNr[newax][neway] = new_group_nr;
9715 AmoebaCnt[new_group_nr]++;
9716 AmoebaCnt2[new_group_nr]++;
9718 /* if amoeba touches other amoeba(s) after growing, unify them */
9719 AmoebenVereinigen(newax, neway);
9721 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9723 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9729 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9730 (neway == lev_fieldy - 1 && newax != ax))
9732 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9733 Store[newax][neway] = element;
9735 else if (neway == ay || element == EL_EMC_DRIPPER)
9737 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9739 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9743 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9744 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9745 Store[ax][ay] = EL_AMOEBA_DROP;
9746 ContinueMoving(ax, ay);
9750 TEST_DrawLevelField(newax, neway);
9753 void Life(int ax, int ay)
9757 int element = Feld[ax][ay];
9758 int graphic = el2img(element);
9759 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9761 boolean changed = FALSE;
9763 if (IS_ANIMATED(graphic))
9764 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9769 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9770 MovDelay[ax][ay] = life_time;
9772 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9775 if (MovDelay[ax][ay])
9779 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9781 int xx = ax+x1, yy = ay+y1;
9784 if (!IN_LEV_FIELD(xx, yy))
9787 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9789 int x = xx+x2, y = yy+y2;
9791 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9794 if (((Feld[x][y] == element ||
9795 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9797 (IS_FREE(x, y) && Stop[x][y]))
9801 if (xx == ax && yy == ay) /* field in the middle */
9803 if (nachbarn < life_parameter[0] ||
9804 nachbarn > life_parameter[1])
9806 Feld[xx][yy] = EL_EMPTY;
9808 TEST_DrawLevelField(xx, yy);
9809 Stop[xx][yy] = TRUE;
9813 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9814 { /* free border field */
9815 if (nachbarn >= life_parameter[2] &&
9816 nachbarn <= life_parameter[3])
9818 Feld[xx][yy] = element;
9819 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9821 TEST_DrawLevelField(xx, yy);
9822 Stop[xx][yy] = TRUE;
9829 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9830 SND_GAME_OF_LIFE_GROWING);
9833 static void InitRobotWheel(int x, int y)
9835 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9838 static void RunRobotWheel(int x, int y)
9840 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9843 static void StopRobotWheel(int x, int y)
9845 if (ZX == x && ZY == y)
9849 game.robot_wheel_active = FALSE;
9853 static void InitTimegateWheel(int x, int y)
9855 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9858 static void RunTimegateWheel(int x, int y)
9860 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9863 static void InitMagicBallDelay(int x, int y)
9866 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9868 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9872 static void ActivateMagicBall(int bx, int by)
9876 if (level.ball_random)
9878 int pos_border = RND(8); /* select one of the eight border elements */
9879 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9880 int xx = pos_content % 3;
9881 int yy = pos_content / 3;
9886 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9887 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9891 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9893 int xx = x - bx + 1;
9894 int yy = y - by + 1;
9896 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9897 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9901 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9904 void CheckExit(int x, int y)
9906 if (local_player->gems_still_needed > 0 ||
9907 local_player->sokobanfields_still_needed > 0 ||
9908 local_player->lights_still_needed > 0)
9910 int element = Feld[x][y];
9911 int graphic = el2img(element);
9913 if (IS_ANIMATED(graphic))
9914 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9919 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9922 Feld[x][y] = EL_EXIT_OPENING;
9924 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9927 void CheckExitEM(int x, int y)
9929 if (local_player->gems_still_needed > 0 ||
9930 local_player->sokobanfields_still_needed > 0 ||
9931 local_player->lights_still_needed > 0)
9933 int element = Feld[x][y];
9934 int graphic = el2img(element);
9936 if (IS_ANIMATED(graphic))
9937 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9942 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9945 Feld[x][y] = EL_EM_EXIT_OPENING;
9947 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9950 void CheckExitSteel(int x, int y)
9952 if (local_player->gems_still_needed > 0 ||
9953 local_player->sokobanfields_still_needed > 0 ||
9954 local_player->lights_still_needed > 0)
9956 int element = Feld[x][y];
9957 int graphic = el2img(element);
9959 if (IS_ANIMATED(graphic))
9960 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9965 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9968 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9970 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9973 void CheckExitSteelEM(int x, int y)
9975 if (local_player->gems_still_needed > 0 ||
9976 local_player->sokobanfields_still_needed > 0 ||
9977 local_player->lights_still_needed > 0)
9979 int element = Feld[x][y];
9980 int graphic = el2img(element);
9982 if (IS_ANIMATED(graphic))
9983 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9988 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9991 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9993 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9996 void CheckExitSP(int x, int y)
9998 if (local_player->gems_still_needed > 0)
10000 int element = Feld[x][y];
10001 int graphic = el2img(element);
10003 if (IS_ANIMATED(graphic))
10004 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10009 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10012 Feld[x][y] = EL_SP_EXIT_OPENING;
10014 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10017 static void CloseAllOpenTimegates()
10021 SCAN_PLAYFIELD(x, y)
10023 int element = Feld[x][y];
10025 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10027 Feld[x][y] = EL_TIMEGATE_CLOSING;
10029 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10034 void DrawTwinkleOnField(int x, int y)
10036 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10039 if (Feld[x][y] == EL_BD_DIAMOND)
10042 if (MovDelay[x][y] == 0) /* next animation frame */
10043 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10045 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10049 DrawLevelElementAnimation(x, y, Feld[x][y]);
10051 if (MovDelay[x][y] != 0)
10053 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10054 10 - MovDelay[x][y]);
10056 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10061 void MauerWaechst(int x, int y)
10065 if (!MovDelay[x][y]) /* next animation frame */
10066 MovDelay[x][y] = 3 * delay;
10068 if (MovDelay[x][y]) /* wait some time before next frame */
10072 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10074 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10075 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10077 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10080 if (!MovDelay[x][y])
10082 if (MovDir[x][y] == MV_LEFT)
10084 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10085 TEST_DrawLevelField(x - 1, y);
10087 else if (MovDir[x][y] == MV_RIGHT)
10089 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10090 TEST_DrawLevelField(x + 1, y);
10092 else if (MovDir[x][y] == MV_UP)
10094 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10095 TEST_DrawLevelField(x, y - 1);
10099 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10100 TEST_DrawLevelField(x, y + 1);
10103 Feld[x][y] = Store[x][y];
10105 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10106 TEST_DrawLevelField(x, y);
10111 void MauerAbleger(int ax, int ay)
10113 int element = Feld[ax][ay];
10114 int graphic = el2img(element);
10115 boolean oben_frei = FALSE, unten_frei = FALSE;
10116 boolean links_frei = FALSE, rechts_frei = FALSE;
10117 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10118 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10119 boolean new_wall = FALSE;
10121 if (IS_ANIMATED(graphic))
10122 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10124 if (!MovDelay[ax][ay]) /* start building new wall */
10125 MovDelay[ax][ay] = 6;
10127 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10129 MovDelay[ax][ay]--;
10130 if (MovDelay[ax][ay])
10134 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10136 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10138 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10140 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10141 rechts_frei = TRUE;
10143 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10144 element == EL_EXPANDABLE_WALL_ANY)
10148 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10149 Store[ax][ay-1] = element;
10150 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10151 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10152 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10153 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10158 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10159 Store[ax][ay+1] = element;
10160 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10161 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10162 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10163 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10168 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10169 element == EL_EXPANDABLE_WALL_ANY ||
10170 element == EL_EXPANDABLE_WALL ||
10171 element == EL_BD_EXPANDABLE_WALL)
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_LEFT;
10178 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10179 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10180 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10186 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10187 Store[ax+1][ay] = element;
10188 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10189 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10190 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10191 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10196 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10197 TEST_DrawLevelField(ax, ay);
10199 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10200 oben_massiv = TRUE;
10201 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10202 unten_massiv = TRUE;
10203 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10204 links_massiv = TRUE;
10205 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10206 rechts_massiv = TRUE;
10208 if (((oben_massiv && unten_massiv) ||
10209 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10210 element == EL_EXPANDABLE_WALL) &&
10211 ((links_massiv && rechts_massiv) ||
10212 element == EL_EXPANDABLE_WALL_VERTICAL))
10213 Feld[ax][ay] = EL_WALL;
10216 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10219 void MauerAblegerStahl(int ax, int ay)
10221 int element = Feld[ax][ay];
10222 int graphic = el2img(element);
10223 boolean oben_frei = FALSE, unten_frei = FALSE;
10224 boolean links_frei = FALSE, rechts_frei = FALSE;
10225 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10226 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10227 boolean new_wall = FALSE;
10229 if (IS_ANIMATED(graphic))
10230 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10232 if (!MovDelay[ax][ay]) /* start building new wall */
10233 MovDelay[ax][ay] = 6;
10235 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10237 MovDelay[ax][ay]--;
10238 if (MovDelay[ax][ay])
10242 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10244 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10246 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10248 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10249 rechts_frei = TRUE;
10251 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10252 element == EL_EXPANDABLE_STEELWALL_ANY)
10256 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10257 Store[ax][ay-1] = element;
10258 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10259 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10260 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10261 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10266 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10267 Store[ax][ay+1] = element;
10268 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10269 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10270 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10271 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10276 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10277 element == EL_EXPANDABLE_STEELWALL_ANY)
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_LEFT;
10284 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10285 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10286 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10292 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10293 Store[ax+1][ay] = element;
10294 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10295 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10296 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10297 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10302 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10303 oben_massiv = TRUE;
10304 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10305 unten_massiv = TRUE;
10306 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10307 links_massiv = TRUE;
10308 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10309 rechts_massiv = TRUE;
10311 if (((oben_massiv && unten_massiv) ||
10312 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10313 ((links_massiv && rechts_massiv) ||
10314 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10315 Feld[ax][ay] = EL_STEELWALL;
10318 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10321 void CheckForDragon(int x, int y)
10324 boolean dragon_found = FALSE;
10325 static int xy[4][2] =
10333 for (i = 0; i < NUM_DIRECTIONS; i++)
10335 for (j = 0; j < 4; j++)
10337 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10339 if (IN_LEV_FIELD(xx, yy) &&
10340 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10342 if (Feld[xx][yy] == EL_DRAGON)
10343 dragon_found = TRUE;
10352 for (i = 0; i < NUM_DIRECTIONS; i++)
10354 for (j = 0; j < 3; j++)
10356 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10358 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10360 Feld[xx][yy] = EL_EMPTY;
10361 TEST_DrawLevelField(xx, yy);
10370 static void InitBuggyBase(int x, int y)
10372 int element = Feld[x][y];
10373 int activating_delay = FRAMES_PER_SECOND / 4;
10375 ChangeDelay[x][y] =
10376 (element == EL_SP_BUGGY_BASE ?
10377 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10378 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10380 element == EL_SP_BUGGY_BASE_ACTIVE ?
10381 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10384 static void WarnBuggyBase(int x, int y)
10387 static int xy[4][2] =
10395 for (i = 0; i < NUM_DIRECTIONS; i++)
10397 int xx = x + xy[i][0];
10398 int yy = y + xy[i][1];
10400 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10402 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10409 static void InitTrap(int x, int y)
10411 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10414 static void ActivateTrap(int x, int y)
10416 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10419 static void ChangeActiveTrap(int x, int y)
10421 int graphic = IMG_TRAP_ACTIVE;
10423 /* if new animation frame was drawn, correct crumbled sand border */
10424 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10425 TEST_DrawLevelFieldCrumbled(x, y);
10428 static int getSpecialActionElement(int element, int number, int base_element)
10430 return (element != EL_EMPTY ? element :
10431 number != -1 ? base_element + number - 1 :
10435 static int getModifiedActionNumber(int value_old, int operator, int operand,
10436 int value_min, int value_max)
10438 int value_new = (operator == CA_MODE_SET ? operand :
10439 operator == CA_MODE_ADD ? value_old + operand :
10440 operator == CA_MODE_SUBTRACT ? value_old - operand :
10441 operator == CA_MODE_MULTIPLY ? value_old * operand :
10442 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10443 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10446 return (value_new < value_min ? value_min :
10447 value_new > value_max ? value_max :
10451 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10453 struct ElementInfo *ei = &element_info[element];
10454 struct ElementChangeInfo *change = &ei->change_page[page];
10455 int target_element = change->target_element;
10456 int action_type = change->action_type;
10457 int action_mode = change->action_mode;
10458 int action_arg = change->action_arg;
10459 int action_element = change->action_element;
10462 if (!change->has_action)
10465 /* ---------- determine action paramater values -------------------------- */
10467 int level_time_value =
10468 (level.time > 0 ? TimeLeft :
10471 int action_arg_element_raw =
10472 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10473 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10474 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10475 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10476 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10477 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10478 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10480 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10483 if (action_arg_element_raw == EL_GROUP_START)
10484 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10487 int action_arg_direction =
10488 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10489 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10490 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10491 change->actual_trigger_side :
10492 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10493 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10496 int action_arg_number_min =
10497 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10500 int action_arg_number_max =
10501 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10502 action_type == CA_SET_LEVEL_GEMS ? 999 :
10503 action_type == CA_SET_LEVEL_TIME ? 9999 :
10504 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10505 action_type == CA_SET_CE_VALUE ? 9999 :
10506 action_type == CA_SET_CE_SCORE ? 9999 :
10509 int action_arg_number_reset =
10510 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10511 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10512 action_type == CA_SET_LEVEL_TIME ? level.time :
10513 action_type == CA_SET_LEVEL_SCORE ? 0 :
10514 #if USE_NEW_CUSTOM_VALUE
10515 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10517 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10519 action_type == CA_SET_CE_SCORE ? 0 :
10522 int action_arg_number =
10523 (action_arg <= CA_ARG_MAX ? action_arg :
10524 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10525 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10526 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10527 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10528 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10529 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10530 #if USE_NEW_CUSTOM_VALUE
10531 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10533 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10535 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10536 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10537 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10538 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10539 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10540 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10541 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10542 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10543 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10544 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10545 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10546 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10547 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10548 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10551 int action_arg_number_old =
10552 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10553 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10554 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10555 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10556 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10559 int action_arg_number_new =
10560 getModifiedActionNumber(action_arg_number_old,
10561 action_mode, action_arg_number,
10562 action_arg_number_min, action_arg_number_max);
10565 int trigger_player_bits =
10566 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10567 change->actual_trigger_player_bits : change->trigger_player);
10569 int trigger_player_bits =
10570 (change->actual_trigger_player >= EL_PLAYER_1 &&
10571 change->actual_trigger_player <= EL_PLAYER_4 ?
10572 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10576 int action_arg_player_bits =
10577 (action_arg >= CA_ARG_PLAYER_1 &&
10578 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10579 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10580 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10583 /* ---------- execute action -------------------------------------------- */
10585 switch (action_type)
10592 /* ---------- level actions ------------------------------------------- */
10594 case CA_RESTART_LEVEL:
10596 game.restart_level = TRUE;
10601 case CA_SHOW_ENVELOPE:
10603 int element = getSpecialActionElement(action_arg_element,
10604 action_arg_number, EL_ENVELOPE_1);
10606 if (IS_ENVELOPE(element))
10607 local_player->show_envelope = element;
10612 case CA_SET_LEVEL_TIME:
10614 if (level.time > 0) /* only modify limited time value */
10616 TimeLeft = action_arg_number_new;
10619 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10621 DisplayGameControlValues();
10623 DrawGameValue_Time(TimeLeft);
10626 if (!TimeLeft && setup.time_limit)
10627 for (i = 0; i < MAX_PLAYERS; i++)
10628 KillPlayer(&stored_player[i]);
10634 case CA_SET_LEVEL_SCORE:
10636 local_player->score = action_arg_number_new;
10639 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10641 DisplayGameControlValues();
10643 DrawGameValue_Score(local_player->score);
10649 case CA_SET_LEVEL_GEMS:
10651 local_player->gems_still_needed = action_arg_number_new;
10654 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10656 DisplayGameControlValues();
10658 DrawGameValue_Emeralds(local_player->gems_still_needed);
10664 #if !USE_PLAYER_GRAVITY
10665 case CA_SET_LEVEL_GRAVITY:
10667 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10668 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10669 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10675 case CA_SET_LEVEL_WIND:
10677 game.wind_direction = action_arg_direction;
10682 case CA_SET_LEVEL_RANDOM_SEED:
10685 /* ensure that setting a new random seed while playing is predictable */
10686 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10688 InitRND(action_arg_number_new);
10692 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10700 for (i = 0; i < 9; i++)
10701 printf("%d, ", RND(2));
10709 /* ---------- player actions ------------------------------------------ */
10711 case CA_MOVE_PLAYER:
10713 /* automatically move to the next field in specified direction */
10714 for (i = 0; i < MAX_PLAYERS; i++)
10715 if (trigger_player_bits & (1 << i))
10716 stored_player[i].programmed_action = action_arg_direction;
10721 case CA_EXIT_PLAYER:
10723 for (i = 0; i < MAX_PLAYERS; i++)
10724 if (action_arg_player_bits & (1 << i))
10725 PlayerWins(&stored_player[i]);
10730 case CA_KILL_PLAYER:
10732 for (i = 0; i < MAX_PLAYERS; i++)
10733 if (action_arg_player_bits & (1 << i))
10734 KillPlayer(&stored_player[i]);
10739 case CA_SET_PLAYER_KEYS:
10741 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10742 int element = getSpecialActionElement(action_arg_element,
10743 action_arg_number, EL_KEY_1);
10745 if (IS_KEY(element))
10747 for (i = 0; i < MAX_PLAYERS; i++)
10749 if (trigger_player_bits & (1 << i))
10751 stored_player[i].key[KEY_NR(element)] = key_state;
10753 DrawGameDoorValues();
10761 case CA_SET_PLAYER_SPEED:
10764 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10767 for (i = 0; i < MAX_PLAYERS; i++)
10769 if (trigger_player_bits & (1 << i))
10771 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10773 if (action_arg == CA_ARG_SPEED_FASTER &&
10774 stored_player[i].cannot_move)
10776 action_arg_number = STEPSIZE_VERY_SLOW;
10778 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10779 action_arg == CA_ARG_SPEED_FASTER)
10781 action_arg_number = 2;
10782 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10785 else if (action_arg == CA_ARG_NUMBER_RESET)
10787 action_arg_number = level.initial_player_stepsize[i];
10791 getModifiedActionNumber(move_stepsize,
10794 action_arg_number_min,
10795 action_arg_number_max);
10797 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10804 case CA_SET_PLAYER_SHIELD:
10806 for (i = 0; i < MAX_PLAYERS; i++)
10808 if (trigger_player_bits & (1 << i))
10810 if (action_arg == CA_ARG_SHIELD_OFF)
10812 stored_player[i].shield_normal_time_left = 0;
10813 stored_player[i].shield_deadly_time_left = 0;
10815 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10817 stored_player[i].shield_normal_time_left = 999999;
10819 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10821 stored_player[i].shield_normal_time_left = 999999;
10822 stored_player[i].shield_deadly_time_left = 999999;
10830 #if USE_PLAYER_GRAVITY
10831 case CA_SET_PLAYER_GRAVITY:
10833 for (i = 0; i < MAX_PLAYERS; i++)
10835 if (trigger_player_bits & (1 << i))
10837 stored_player[i].gravity =
10838 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10839 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10840 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10841 stored_player[i].gravity);
10849 case CA_SET_PLAYER_ARTWORK:
10851 for (i = 0; i < MAX_PLAYERS; i++)
10853 if (trigger_player_bits & (1 << i))
10855 int artwork_element = action_arg_element;
10857 if (action_arg == CA_ARG_ELEMENT_RESET)
10859 (level.use_artwork_element[i] ? level.artwork_element[i] :
10860 stored_player[i].element_nr);
10862 #if USE_GFX_RESET_PLAYER_ARTWORK
10863 if (stored_player[i].artwork_element != artwork_element)
10864 stored_player[i].Frame = 0;
10867 stored_player[i].artwork_element = artwork_element;
10869 SetPlayerWaiting(&stored_player[i], FALSE);
10871 /* set number of special actions for bored and sleeping animation */
10872 stored_player[i].num_special_action_bored =
10873 get_num_special_action(artwork_element,
10874 ACTION_BORING_1, ACTION_BORING_LAST);
10875 stored_player[i].num_special_action_sleeping =
10876 get_num_special_action(artwork_element,
10877 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10884 case CA_SET_PLAYER_INVENTORY:
10886 for (i = 0; i < MAX_PLAYERS; i++)
10888 struct PlayerInfo *player = &stored_player[i];
10891 if (trigger_player_bits & (1 << i))
10893 int inventory_element = action_arg_element;
10895 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10896 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10897 action_arg == CA_ARG_ELEMENT_ACTION)
10899 int element = inventory_element;
10900 int collect_count = element_info[element].collect_count_initial;
10902 if (!IS_CUSTOM_ELEMENT(element))
10905 if (collect_count == 0)
10906 player->inventory_infinite_element = element;
10908 for (k = 0; k < collect_count; k++)
10909 if (player->inventory_size < MAX_INVENTORY_SIZE)
10910 player->inventory_element[player->inventory_size++] =
10913 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10914 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10915 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10917 if (player->inventory_infinite_element != EL_UNDEFINED &&
10918 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10919 action_arg_element_raw))
10920 player->inventory_infinite_element = EL_UNDEFINED;
10922 for (k = 0, j = 0; j < player->inventory_size; j++)
10924 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10925 action_arg_element_raw))
10926 player->inventory_element[k++] = player->inventory_element[j];
10929 player->inventory_size = k;
10931 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10933 if (player->inventory_size > 0)
10935 for (j = 0; j < player->inventory_size - 1; j++)
10936 player->inventory_element[j] = player->inventory_element[j + 1];
10938 player->inventory_size--;
10941 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10943 if (player->inventory_size > 0)
10944 player->inventory_size--;
10946 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10948 player->inventory_infinite_element = EL_UNDEFINED;
10949 player->inventory_size = 0;
10951 else if (action_arg == CA_ARG_INVENTORY_RESET)
10953 player->inventory_infinite_element = EL_UNDEFINED;
10954 player->inventory_size = 0;
10956 if (level.use_initial_inventory[i])
10958 for (j = 0; j < level.initial_inventory_size[i]; j++)
10960 int element = level.initial_inventory_content[i][j];
10961 int collect_count = element_info[element].collect_count_initial;
10963 if (!IS_CUSTOM_ELEMENT(element))
10966 if (collect_count == 0)
10967 player->inventory_infinite_element = element;
10969 for (k = 0; k < collect_count; k++)
10970 if (player->inventory_size < MAX_INVENTORY_SIZE)
10971 player->inventory_element[player->inventory_size++] =
10982 /* ---------- CE actions ---------------------------------------------- */
10984 case CA_SET_CE_VALUE:
10986 #if USE_NEW_CUSTOM_VALUE
10987 int last_ce_value = CustomValue[x][y];
10989 CustomValue[x][y] = action_arg_number_new;
10991 if (CustomValue[x][y] != last_ce_value)
10993 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10994 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10996 if (CustomValue[x][y] == 0)
10998 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10999 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11007 case CA_SET_CE_SCORE:
11009 #if USE_NEW_CUSTOM_VALUE
11010 int last_ce_score = ei->collect_score;
11012 ei->collect_score = action_arg_number_new;
11014 if (ei->collect_score != last_ce_score)
11016 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11017 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11019 if (ei->collect_score == 0)
11023 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11024 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11027 This is a very special case that seems to be a mixture between
11028 CheckElementChange() and CheckTriggeredElementChange(): while
11029 the first one only affects single elements that are triggered
11030 directly, the second one affects multiple elements in the playfield
11031 that are triggered indirectly by another element. This is a third
11032 case: Changing the CE score always affects multiple identical CEs,
11033 so every affected CE must be checked, not only the single CE for
11034 which the CE score was changed in the first place (as every instance
11035 of that CE shares the same CE score, and therefore also can change)!
11037 SCAN_PLAYFIELD(xx, yy)
11039 if (Feld[xx][yy] == element)
11040 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11041 CE_SCORE_GETS_ZERO);
11050 case CA_SET_CE_ARTWORK:
11052 int artwork_element = action_arg_element;
11053 boolean reset_frame = FALSE;
11056 if (action_arg == CA_ARG_ELEMENT_RESET)
11057 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11060 if (ei->gfx_element != artwork_element)
11061 reset_frame = TRUE;
11063 ei->gfx_element = artwork_element;
11065 SCAN_PLAYFIELD(xx, yy)
11067 if (Feld[xx][yy] == element)
11071 ResetGfxAnimation(xx, yy);
11072 ResetRandomAnimationValue(xx, yy);
11075 TEST_DrawLevelField(xx, yy);
11082 /* ---------- engine actions ------------------------------------------ */
11084 case CA_SET_ENGINE_SCAN_MODE:
11086 InitPlayfieldScanMode(action_arg);
11096 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11098 int old_element = Feld[x][y];
11099 int new_element = GetElementFromGroupElement(element);
11100 int previous_move_direction = MovDir[x][y];
11101 #if USE_NEW_CUSTOM_VALUE
11102 int last_ce_value = CustomValue[x][y];
11104 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11105 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11106 boolean add_player_onto_element = (new_element_is_player &&
11107 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11108 /* this breaks SnakeBite when a snake is
11109 halfway through a door that closes */
11110 /* NOW FIXED AT LEVEL INIT IN files.c */
11111 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11113 IS_WALKABLE(old_element));
11116 /* check if element under the player changes from accessible to unaccessible
11117 (needed for special case of dropping element which then changes) */
11118 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11119 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11127 if (!add_player_onto_element)
11129 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11130 RemoveMovingField(x, y);
11134 Feld[x][y] = new_element;
11136 #if !USE_GFX_RESET_GFX_ANIMATION
11137 ResetGfxAnimation(x, y);
11138 ResetRandomAnimationValue(x, y);
11141 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11142 MovDir[x][y] = previous_move_direction;
11144 #if USE_NEW_CUSTOM_VALUE
11145 if (element_info[new_element].use_last_ce_value)
11146 CustomValue[x][y] = last_ce_value;
11149 InitField_WithBug1(x, y, FALSE);
11151 new_element = Feld[x][y]; /* element may have changed */
11153 #if USE_GFX_RESET_GFX_ANIMATION
11154 ResetGfxAnimation(x, y);
11155 ResetRandomAnimationValue(x, y);
11158 TEST_DrawLevelField(x, y);
11160 if (GFX_CRUMBLED(new_element))
11161 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11165 /* check if element under the player changes from accessible to unaccessible
11166 (needed for special case of dropping element which then changes) */
11167 /* (must be checked after creating new element for walkable group elements) */
11168 #if USE_FIX_KILLED_BY_NON_WALKABLE
11169 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11170 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11177 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11178 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11187 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11188 if (new_element_is_player)
11189 RelocatePlayer(x, y, new_element);
11192 ChangeCount[x][y]++; /* count number of changes in the same frame */
11194 TestIfBadThingTouchesPlayer(x, y);
11195 TestIfPlayerTouchesCustomElement(x, y);
11196 TestIfElementTouchesCustomElement(x, y);
11199 static void CreateField(int x, int y, int element)
11201 CreateFieldExt(x, y, element, FALSE);
11204 static void CreateElementFromChange(int x, int y, int element)
11206 element = GET_VALID_RUNTIME_ELEMENT(element);
11208 #if USE_STOP_CHANGED_ELEMENTS
11209 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11211 int old_element = Feld[x][y];
11213 /* prevent changed element from moving in same engine frame
11214 unless both old and new element can either fall or move */
11215 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11216 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11221 CreateFieldExt(x, y, element, TRUE);
11224 static boolean ChangeElement(int x, int y, int element, int page)
11226 struct ElementInfo *ei = &element_info[element];
11227 struct ElementChangeInfo *change = &ei->change_page[page];
11228 int ce_value = CustomValue[x][y];
11229 int ce_score = ei->collect_score;
11230 int target_element;
11231 int old_element = Feld[x][y];
11233 /* always use default change event to prevent running into a loop */
11234 if (ChangeEvent[x][y] == -1)
11235 ChangeEvent[x][y] = CE_DELAY;
11237 if (ChangeEvent[x][y] == CE_DELAY)
11239 /* reset actual trigger element, trigger player and action element */
11240 change->actual_trigger_element = EL_EMPTY;
11241 change->actual_trigger_player = EL_EMPTY;
11242 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11243 change->actual_trigger_side = CH_SIDE_NONE;
11244 change->actual_trigger_ce_value = 0;
11245 change->actual_trigger_ce_score = 0;
11248 /* do not change elements more than a specified maximum number of changes */
11249 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11252 ChangeCount[x][y]++; /* count number of changes in the same frame */
11254 if (change->explode)
11261 if (change->use_target_content)
11263 boolean complete_replace = TRUE;
11264 boolean can_replace[3][3];
11267 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11270 boolean is_walkable;
11271 boolean is_diggable;
11272 boolean is_collectible;
11273 boolean is_removable;
11274 boolean is_destructible;
11275 int ex = x + xx - 1;
11276 int ey = y + yy - 1;
11277 int content_element = change->target_content.e[xx][yy];
11280 can_replace[xx][yy] = TRUE;
11282 if (ex == x && ey == y) /* do not check changing element itself */
11285 if (content_element == EL_EMPTY_SPACE)
11287 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11292 if (!IN_LEV_FIELD(ex, ey))
11294 can_replace[xx][yy] = FALSE;
11295 complete_replace = FALSE;
11302 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11303 e = MovingOrBlocked2Element(ex, ey);
11305 is_empty = (IS_FREE(ex, ey) ||
11306 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11308 is_walkable = (is_empty || IS_WALKABLE(e));
11309 is_diggable = (is_empty || IS_DIGGABLE(e));
11310 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11311 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11312 is_removable = (is_diggable || is_collectible);
11314 can_replace[xx][yy] =
11315 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11316 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11317 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11318 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11319 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11320 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11321 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11323 if (!can_replace[xx][yy])
11324 complete_replace = FALSE;
11327 if (!change->only_if_complete || complete_replace)
11329 boolean something_has_changed = FALSE;
11331 if (change->only_if_complete && change->use_random_replace &&
11332 RND(100) < change->random_percentage)
11335 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11337 int ex = x + xx - 1;
11338 int ey = y + yy - 1;
11339 int content_element;
11341 if (can_replace[xx][yy] && (!change->use_random_replace ||
11342 RND(100) < change->random_percentage))
11344 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11345 RemoveMovingField(ex, ey);
11347 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11349 content_element = change->target_content.e[xx][yy];
11350 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11351 ce_value, ce_score);
11353 CreateElementFromChange(ex, ey, target_element);
11355 something_has_changed = TRUE;
11357 /* for symmetry reasons, freeze newly created border elements */
11358 if (ex != x || ey != y)
11359 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11363 if (something_has_changed)
11365 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11366 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11372 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11373 ce_value, ce_score);
11375 if (element == EL_DIAGONAL_GROWING ||
11376 element == EL_DIAGONAL_SHRINKING)
11378 target_element = Store[x][y];
11380 Store[x][y] = EL_EMPTY;
11383 CreateElementFromChange(x, y, target_element);
11385 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11386 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11389 /* this uses direct change before indirect change */
11390 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11395 #if USE_NEW_DELAYED_ACTION
11397 static void HandleElementChange(int x, int y, int page)
11399 int element = MovingOrBlocked2Element(x, y);
11400 struct ElementInfo *ei = &element_info[element];
11401 struct ElementChangeInfo *change = &ei->change_page[page];
11402 boolean handle_action_before_change = FALSE;
11405 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11406 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11409 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11410 x, y, element, element_info[element].token_name);
11411 printf("HandleElementChange(): This should never happen!\n");
11416 /* this can happen with classic bombs on walkable, changing elements */
11417 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11420 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11421 ChangeDelay[x][y] = 0;
11427 if (ChangeDelay[x][y] == 0) /* initialize element change */
11429 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11431 if (change->can_change)
11434 /* !!! not clear why graphic animation should be reset at all here !!! */
11435 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11436 #if USE_GFX_RESET_WHEN_NOT_MOVING
11437 /* when a custom element is about to change (for example by change delay),
11438 do not reset graphic animation when the custom element is moving */
11439 if (!IS_MOVING(x, y))
11442 ResetGfxAnimation(x, y);
11443 ResetRandomAnimationValue(x, y);
11447 if (change->pre_change_function)
11448 change->pre_change_function(x, y);
11452 ChangeDelay[x][y]--;
11454 if (ChangeDelay[x][y] != 0) /* continue element change */
11456 if (change->can_change)
11458 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11460 if (IS_ANIMATED(graphic))
11461 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11463 if (change->change_function)
11464 change->change_function(x, y);
11467 else /* finish element change */
11469 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11471 page = ChangePage[x][y];
11472 ChangePage[x][y] = -1;
11474 change = &ei->change_page[page];
11477 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11479 ChangeDelay[x][y] = 1; /* try change after next move step */
11480 ChangePage[x][y] = page; /* remember page to use for change */
11486 /* special case: set new level random seed before changing element */
11487 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11488 handle_action_before_change = TRUE;
11490 if (change->has_action && handle_action_before_change)
11491 ExecuteCustomElementAction(x, y, element, page);
11494 if (change->can_change)
11496 if (ChangeElement(x, y, element, page))
11498 if (change->post_change_function)
11499 change->post_change_function(x, y);
11503 if (change->has_action && !handle_action_before_change)
11504 ExecuteCustomElementAction(x, y, element, page);
11510 static void HandleElementChange(int x, int y, int page)
11512 int element = MovingOrBlocked2Element(x, y);
11513 struct ElementInfo *ei = &element_info[element];
11514 struct ElementChangeInfo *change = &ei->change_page[page];
11517 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11520 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11521 x, y, element, element_info[element].token_name);
11522 printf("HandleElementChange(): This should never happen!\n");
11527 /* this can happen with classic bombs on walkable, changing elements */
11528 if (!CAN_CHANGE(element))
11531 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11532 ChangeDelay[x][y] = 0;
11538 if (ChangeDelay[x][y] == 0) /* initialize element change */
11540 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11542 ResetGfxAnimation(x, y);
11543 ResetRandomAnimationValue(x, y);
11545 if (change->pre_change_function)
11546 change->pre_change_function(x, y);
11549 ChangeDelay[x][y]--;
11551 if (ChangeDelay[x][y] != 0) /* continue element change */
11553 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11555 if (IS_ANIMATED(graphic))
11556 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11558 if (change->change_function)
11559 change->change_function(x, y);
11561 else /* finish element change */
11563 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11565 page = ChangePage[x][y];
11566 ChangePage[x][y] = -1;
11568 change = &ei->change_page[page];
11571 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11573 ChangeDelay[x][y] = 1; /* try change after next move step */
11574 ChangePage[x][y] = page; /* remember page to use for change */
11579 if (ChangeElement(x, y, element, page))
11581 if (change->post_change_function)
11582 change->post_change_function(x, y);
11589 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11590 int trigger_element,
11592 int trigger_player,
11596 boolean change_done_any = FALSE;
11597 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11600 if (!(trigger_events[trigger_element][trigger_event]))
11604 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11605 trigger_event, recursion_loop_depth, recursion_loop_detected,
11606 recursion_loop_element, EL_NAME(recursion_loop_element));
11609 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11611 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11613 int element = EL_CUSTOM_START + i;
11614 boolean change_done = FALSE;
11617 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11618 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11621 for (p = 0; p < element_info[element].num_change_pages; p++)
11623 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11625 if (change->can_change_or_has_action &&
11626 change->has_event[trigger_event] &&
11627 change->trigger_side & trigger_side &&
11628 change->trigger_player & trigger_player &&
11629 change->trigger_page & trigger_page_bits &&
11630 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11632 change->actual_trigger_element = trigger_element;
11633 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11634 change->actual_trigger_player_bits = trigger_player;
11635 change->actual_trigger_side = trigger_side;
11636 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11637 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11640 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11641 element, EL_NAME(element), p);
11644 if ((change->can_change && !change_done) || change->has_action)
11648 SCAN_PLAYFIELD(x, y)
11650 if (Feld[x][y] == element)
11652 if (change->can_change && !change_done)
11654 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11655 /* if element already changed in this frame, not only prevent
11656 another element change (checked in ChangeElement()), but
11657 also prevent additional element actions for this element */
11659 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11660 !level.use_action_after_change_bug)
11665 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11666 element, EL_NAME(element), p);
11669 ChangeDelay[x][y] = 1;
11670 ChangeEvent[x][y] = trigger_event;
11672 HandleElementChange(x, y, p);
11674 #if USE_NEW_DELAYED_ACTION
11675 else if (change->has_action)
11677 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11678 /* if element already changed in this frame, not only prevent
11679 another element change (checked in ChangeElement()), but
11680 also prevent additional element actions for this element */
11682 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11683 !level.use_action_after_change_bug)
11689 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11690 element, EL_NAME(element), p);
11693 ExecuteCustomElementAction(x, y, element, p);
11694 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11697 if (change->has_action)
11699 ExecuteCustomElementAction(x, y, element, p);
11700 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11706 if (change->can_change)
11708 change_done = TRUE;
11709 change_done_any = TRUE;
11712 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11713 element, EL_NAME(element), p);
11722 RECURSION_LOOP_DETECTION_END();
11724 return change_done_any;
11727 static boolean CheckElementChangeExt(int x, int y,
11729 int trigger_element,
11731 int trigger_player,
11734 boolean change_done = FALSE;
11737 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11738 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11741 if (Feld[x][y] == EL_BLOCKED)
11743 Blocked2Moving(x, y, &x, &y);
11744 element = Feld[x][y];
11748 /* check if element has already changed */
11749 if (Feld[x][y] != element)
11752 /* check if element has already changed or is about to change after moving */
11753 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11754 Feld[x][y] != element) ||
11756 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11757 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11758 ChangePage[x][y] != -1)))
11763 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11764 trigger_event, recursion_loop_depth, recursion_loop_detected,
11765 recursion_loop_element, EL_NAME(recursion_loop_element));
11768 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11771 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11774 for (p = 0; p < element_info[element].num_change_pages; p++)
11776 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11778 /* check trigger element for all events where the element that is checked
11779 for changing interacts with a directly adjacent element -- this is
11780 different to element changes that affect other elements to change on the
11781 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11782 boolean check_trigger_element =
11783 (trigger_event == CE_TOUCHING_X ||
11784 trigger_event == CE_HITTING_X ||
11785 trigger_event == CE_HIT_BY_X ||
11787 /* this one was forgotten until 3.2.3 */
11788 trigger_event == CE_DIGGING_X);
11791 if (change->can_change_or_has_action &&
11792 change->has_event[trigger_event] &&
11793 change->trigger_side & trigger_side &&
11794 change->trigger_player & trigger_player &&
11795 (!check_trigger_element ||
11796 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11798 change->actual_trigger_element = trigger_element;
11799 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11800 change->actual_trigger_player_bits = trigger_player;
11801 change->actual_trigger_side = trigger_side;
11802 change->actual_trigger_ce_value = CustomValue[x][y];
11803 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11805 /* special case: trigger element not at (x,y) position for some events */
11806 if (check_trigger_element)
11818 { 0, 0 }, { 0, 0 }, { 0, 0 },
11822 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11823 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11825 change->actual_trigger_ce_value = CustomValue[xx][yy];
11826 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11829 if (change->can_change && !change_done)
11831 ChangeDelay[x][y] = 1;
11832 ChangeEvent[x][y] = trigger_event;
11834 HandleElementChange(x, y, p);
11836 change_done = TRUE;
11838 #if USE_NEW_DELAYED_ACTION
11839 else if (change->has_action)
11841 ExecuteCustomElementAction(x, y, element, p);
11842 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11845 if (change->has_action)
11847 ExecuteCustomElementAction(x, y, element, p);
11848 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11854 RECURSION_LOOP_DETECTION_END();
11856 return change_done;
11859 static void PlayPlayerSound(struct PlayerInfo *player)
11861 int jx = player->jx, jy = player->jy;
11862 int sound_element = player->artwork_element;
11863 int last_action = player->last_action_waiting;
11864 int action = player->action_waiting;
11866 if (player->is_waiting)
11868 if (action != last_action)
11869 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11871 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11875 if (action != last_action)
11876 StopSound(element_info[sound_element].sound[last_action]);
11878 if (last_action == ACTION_SLEEPING)
11879 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11883 static void PlayAllPlayersSound()
11887 for (i = 0; i < MAX_PLAYERS; i++)
11888 if (stored_player[i].active)
11889 PlayPlayerSound(&stored_player[i]);
11892 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11894 boolean last_waiting = player->is_waiting;
11895 int move_dir = player->MovDir;
11897 player->dir_waiting = move_dir;
11898 player->last_action_waiting = player->action_waiting;
11902 if (!last_waiting) /* not waiting -> waiting */
11904 player->is_waiting = TRUE;
11906 player->frame_counter_bored =
11908 game.player_boring_delay_fixed +
11909 GetSimpleRandom(game.player_boring_delay_random);
11910 player->frame_counter_sleeping =
11912 game.player_sleeping_delay_fixed +
11913 GetSimpleRandom(game.player_sleeping_delay_random);
11915 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11918 if (game.player_sleeping_delay_fixed +
11919 game.player_sleeping_delay_random > 0 &&
11920 player->anim_delay_counter == 0 &&
11921 player->post_delay_counter == 0 &&
11922 FrameCounter >= player->frame_counter_sleeping)
11923 player->is_sleeping = TRUE;
11924 else if (game.player_boring_delay_fixed +
11925 game.player_boring_delay_random > 0 &&
11926 FrameCounter >= player->frame_counter_bored)
11927 player->is_bored = TRUE;
11929 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11930 player->is_bored ? ACTION_BORING :
11933 if (player->is_sleeping && player->use_murphy)
11935 /* special case for sleeping Murphy when leaning against non-free tile */
11937 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11938 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11939 !IS_MOVING(player->jx - 1, player->jy)))
11940 move_dir = MV_LEFT;
11941 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11942 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11943 !IS_MOVING(player->jx + 1, player->jy)))
11944 move_dir = MV_RIGHT;
11946 player->is_sleeping = FALSE;
11948 player->dir_waiting = move_dir;
11951 if (player->is_sleeping)
11953 if (player->num_special_action_sleeping > 0)
11955 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11957 int last_special_action = player->special_action_sleeping;
11958 int num_special_action = player->num_special_action_sleeping;
11959 int special_action =
11960 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11961 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11962 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11963 last_special_action + 1 : ACTION_SLEEPING);
11964 int special_graphic =
11965 el_act_dir2img(player->artwork_element, special_action, move_dir);
11967 player->anim_delay_counter =
11968 graphic_info[special_graphic].anim_delay_fixed +
11969 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11970 player->post_delay_counter =
11971 graphic_info[special_graphic].post_delay_fixed +
11972 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11974 player->special_action_sleeping = special_action;
11977 if (player->anim_delay_counter > 0)
11979 player->action_waiting = player->special_action_sleeping;
11980 player->anim_delay_counter--;
11982 else if (player->post_delay_counter > 0)
11984 player->post_delay_counter--;
11988 else if (player->is_bored)
11990 if (player->num_special_action_bored > 0)
11992 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11994 int special_action =
11995 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11996 int special_graphic =
11997 el_act_dir2img(player->artwork_element, special_action, move_dir);
11999 player->anim_delay_counter =
12000 graphic_info[special_graphic].anim_delay_fixed +
12001 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12002 player->post_delay_counter =
12003 graphic_info[special_graphic].post_delay_fixed +
12004 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12006 player->special_action_bored = special_action;
12009 if (player->anim_delay_counter > 0)
12011 player->action_waiting = player->special_action_bored;
12012 player->anim_delay_counter--;
12014 else if (player->post_delay_counter > 0)
12016 player->post_delay_counter--;
12021 else if (last_waiting) /* waiting -> not waiting */
12023 player->is_waiting = FALSE;
12024 player->is_bored = FALSE;
12025 player->is_sleeping = FALSE;
12027 player->frame_counter_bored = -1;
12028 player->frame_counter_sleeping = -1;
12030 player->anim_delay_counter = 0;
12031 player->post_delay_counter = 0;
12033 player->dir_waiting = player->MovDir;
12034 player->action_waiting = ACTION_DEFAULT;
12036 player->special_action_bored = ACTION_DEFAULT;
12037 player->special_action_sleeping = ACTION_DEFAULT;
12041 static void CheckSingleStepMode(struct PlayerInfo *player)
12043 if (tape.single_step && tape.recording && !tape.pausing)
12045 /* as it is called "single step mode", just return to pause mode when the
12046 player stopped moving after one tile (or never starts moving at all) */
12047 if (!player->is_moving && !player->is_pushing)
12049 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12050 SnapField(player, 0, 0); /* stop snapping */
12055 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12057 int left = player_action & JOY_LEFT;
12058 int right = player_action & JOY_RIGHT;
12059 int up = player_action & JOY_UP;
12060 int down = player_action & JOY_DOWN;
12061 int button1 = player_action & JOY_BUTTON_1;
12062 int button2 = player_action & JOY_BUTTON_2;
12063 int dx = (left ? -1 : right ? 1 : 0);
12064 int dy = (up ? -1 : down ? 1 : 0);
12066 if (!player->active || tape.pausing)
12072 SnapField(player, dx, dy);
12076 DropElement(player);
12078 MovePlayer(player, dx, dy);
12081 CheckSingleStepMode(player);
12083 SetPlayerWaiting(player, FALSE);
12085 return player_action;
12089 /* no actions for this player (no input at player's configured device) */
12091 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12092 SnapField(player, 0, 0);
12093 CheckGravityMovementWhenNotMoving(player);
12095 if (player->MovPos == 0)
12096 SetPlayerWaiting(player, TRUE);
12098 if (player->MovPos == 0) /* needed for tape.playing */
12099 player->is_moving = FALSE;
12101 player->is_dropping = FALSE;
12102 player->is_dropping_pressed = FALSE;
12103 player->drop_pressed_delay = 0;
12105 CheckSingleStepMode(player);
12111 static void CheckLevelTime()
12115 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12116 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12118 if (level.native_em_level->lev->home == 0) /* all players at home */
12120 PlayerWins(local_player);
12122 AllPlayersGone = TRUE;
12124 level.native_em_level->lev->home = -1;
12127 if (level.native_em_level->ply[0]->alive == 0 &&
12128 level.native_em_level->ply[1]->alive == 0 &&
12129 level.native_em_level->ply[2]->alive == 0 &&
12130 level.native_em_level->ply[3]->alive == 0) /* all dead */
12131 AllPlayersGone = TRUE;
12133 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12135 if (game_sp.LevelSolved &&
12136 !game_sp.GameOver) /* game won */
12138 PlayerWins(local_player);
12140 game_sp.GameOver = TRUE;
12142 AllPlayersGone = TRUE;
12145 if (game_sp.GameOver) /* game lost */
12146 AllPlayersGone = TRUE;
12149 if (TimeFrames >= FRAMES_PER_SECOND)
12154 for (i = 0; i < MAX_PLAYERS; i++)
12156 struct PlayerInfo *player = &stored_player[i];
12158 if (SHIELD_ON(player))
12160 player->shield_normal_time_left--;
12162 if (player->shield_deadly_time_left > 0)
12163 player->shield_deadly_time_left--;
12167 if (!local_player->LevelSolved && !level.use_step_counter)
12175 if (TimeLeft <= 10 && setup.time_limit)
12176 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12179 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12180 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12182 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12184 /* (already called by UpdateAndDisplayGameControlValues() below) */
12185 // DisplayGameControlValues();
12187 DrawGameValue_Time(TimeLeft);
12190 if (!TimeLeft && setup.time_limit)
12192 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12193 level.native_em_level->lev->killed_out_of_time = TRUE;
12195 for (i = 0; i < MAX_PLAYERS; i++)
12196 KillPlayer(&stored_player[i]);
12200 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12202 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12204 /* (already called by UpdateAndDisplayGameControlValues() below) */
12205 // DisplayGameControlValues();
12208 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12209 DrawGameValue_Time(TimePlayed);
12212 level.native_em_level->lev->time =
12213 (game.no_time_limit ? TimePlayed : TimeLeft);
12216 if (tape.recording || tape.playing)
12217 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12221 UpdateAndDisplayGameControlValues();
12223 UpdateGameDoorValues();
12224 DrawGameDoorValues();
12228 void AdvanceFrameAndPlayerCounters(int player_nr)
12232 /* advance frame counters (global frame counter and time frame counter) */
12236 /* advance player counters (counters for move delay, move animation etc.) */
12237 for (i = 0; i < MAX_PLAYERS; i++)
12239 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12240 int move_delay_value = stored_player[i].move_delay_value;
12241 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12243 if (!advance_player_counters) /* not all players may be affected */
12246 #if USE_NEW_PLAYER_ANIM
12247 if (move_frames == 0) /* less than one move per game frame */
12249 int stepsize = TILEX / move_delay_value;
12250 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12251 int count = (stored_player[i].is_moving ?
12252 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12254 if (count % delay == 0)
12259 stored_player[i].Frame += move_frames;
12261 if (stored_player[i].MovPos != 0)
12262 stored_player[i].StepFrame += move_frames;
12264 if (stored_player[i].move_delay > 0)
12265 stored_player[i].move_delay--;
12267 /* due to bugs in previous versions, counter must count up, not down */
12268 if (stored_player[i].push_delay != -1)
12269 stored_player[i].push_delay++;
12271 if (stored_player[i].drop_delay > 0)
12272 stored_player[i].drop_delay--;
12274 if (stored_player[i].is_dropping_pressed)
12275 stored_player[i].drop_pressed_delay++;
12279 void StartGameActions(boolean init_network_game, boolean record_tape,
12282 unsigned int new_random_seed = InitRND(random_seed);
12285 TapeStartRecording(new_random_seed);
12287 #if defined(NETWORK_AVALIABLE)
12288 if (init_network_game)
12290 SendToServer_StartPlaying();
12301 static unsigned int game_frame_delay = 0;
12302 unsigned int game_frame_delay_value;
12303 byte *recorded_player_action;
12304 byte summarized_player_action = 0;
12305 byte tape_action[MAX_PLAYERS];
12308 /* detect endless loops, caused by custom element programming */
12309 if (recursion_loop_detected && recursion_loop_depth == 0)
12311 char *message = getStringCat3("Internal Error! Element ",
12312 EL_NAME(recursion_loop_element),
12313 " caused endless loop! Quit the game?");
12315 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12316 EL_NAME(recursion_loop_element));
12318 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12320 recursion_loop_detected = FALSE; /* if game should be continued */
12327 if (game.restart_level)
12328 StartGameActions(options.network, setup.autorecord, level.random_seed);
12330 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12331 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12333 if (level.native_em_level->lev->home == 0) /* all players at home */
12335 PlayerWins(local_player);
12337 AllPlayersGone = TRUE;
12339 level.native_em_level->lev->home = -1;
12342 if (level.native_em_level->ply[0]->alive == 0 &&
12343 level.native_em_level->ply[1]->alive == 0 &&
12344 level.native_em_level->ply[2]->alive == 0 &&
12345 level.native_em_level->ply[3]->alive == 0) /* all dead */
12346 AllPlayersGone = TRUE;
12348 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12350 if (game_sp.LevelSolved &&
12351 !game_sp.GameOver) /* game won */
12353 PlayerWins(local_player);
12355 game_sp.GameOver = TRUE;
12357 AllPlayersGone = TRUE;
12360 if (game_sp.GameOver) /* game lost */
12361 AllPlayersGone = TRUE;
12364 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12367 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12370 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12373 game_frame_delay_value =
12374 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12376 if (tape.playing && tape.warp_forward && !tape.pausing)
12377 game_frame_delay_value = 0;
12379 /* ---------- main game synchronization point ---------- */
12381 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12383 if (network_playing && !network_player_action_received)
12385 /* try to get network player actions in time */
12387 #if defined(NETWORK_AVALIABLE)
12388 /* last chance to get network player actions without main loop delay */
12389 HandleNetworking();
12392 /* game was quit by network peer */
12393 if (game_status != GAME_MODE_PLAYING)
12396 if (!network_player_action_received)
12397 return; /* failed to get network player actions in time */
12399 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12405 /* at this point we know that we really continue executing the game */
12407 network_player_action_received = FALSE;
12409 /* when playing tape, read previously recorded player input from tape data */
12410 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12413 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12418 if (tape.set_centered_player)
12420 game.centered_player_nr_next = tape.centered_player_nr_next;
12421 game.set_centered_player = TRUE;
12424 for (i = 0; i < MAX_PLAYERS; i++)
12426 summarized_player_action |= stored_player[i].action;
12429 if (!network_playing && (game.team_mode || tape.playing))
12430 stored_player[i].effective_action = stored_player[i].action;
12432 if (!network_playing)
12433 stored_player[i].effective_action = stored_player[i].action;
12437 #if defined(NETWORK_AVALIABLE)
12438 if (network_playing)
12439 SendToServer_MovePlayer(summarized_player_action);
12442 if (!options.network && !game.team_mode)
12443 local_player->effective_action = summarized_player_action;
12445 if (tape.recording &&
12447 setup.input_on_focus &&
12448 game.centered_player_nr != -1)
12450 for (i = 0; i < MAX_PLAYERS; i++)
12451 stored_player[i].effective_action =
12452 (i == game.centered_player_nr ? summarized_player_action : 0);
12455 if (recorded_player_action != NULL)
12456 for (i = 0; i < MAX_PLAYERS; i++)
12457 stored_player[i].effective_action = recorded_player_action[i];
12459 for (i = 0; i < MAX_PLAYERS; i++)
12461 tape_action[i] = stored_player[i].effective_action;
12464 /* (this may happen in the RND game engine if a player was not present on
12465 the playfield on level start, but appeared later from a custom element */
12466 if (tape.recording &&
12469 !tape.player_participates[i])
12470 tape.player_participates[i] = TRUE;
12472 /* (this can only happen in the R'n'D game engine) */
12473 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12474 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12478 /* only record actions from input devices, but not programmed actions */
12479 if (tape.recording)
12480 TapeRecordAction(tape_action);
12482 #if USE_NEW_PLAYER_ASSIGNMENTS
12484 if (game.team_mode)
12487 byte mapped_action[MAX_PLAYERS];
12489 #if DEBUG_PLAYER_ACTIONS
12491 for (i = 0; i < MAX_PLAYERS; i++)
12492 printf(" %d, ", stored_player[i].effective_action);
12495 for (i = 0; i < MAX_PLAYERS; i++)
12496 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12498 for (i = 0; i < MAX_PLAYERS; i++)
12499 stored_player[i].effective_action = mapped_action[i];
12501 #if DEBUG_PLAYER_ACTIONS
12503 for (i = 0; i < MAX_PLAYERS; i++)
12504 printf(" %d, ", stored_player[i].effective_action);
12508 #if DEBUG_PLAYER_ACTIONS
12512 for (i = 0; i < MAX_PLAYERS; i++)
12513 printf(" %d, ", stored_player[i].effective_action);
12520 printf("::: summarized_player_action == %d\n",
12521 local_player->effective_action);
12528 #if DEBUG_INIT_PLAYER
12531 printf("Player status (final):\n");
12533 for (i = 0; i < MAX_PLAYERS; i++)
12535 struct PlayerInfo *player = &stored_player[i];
12537 printf("- player %d: present == %d, connected == %d, active == %d",
12543 if (local_player == player)
12544 printf(" (local player)");
12554 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12556 GameActions_EM_Main();
12558 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12560 GameActions_SP_Main();
12568 void GameActions_EM_Main()
12570 byte effective_action[MAX_PLAYERS];
12571 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12574 for (i = 0; i < MAX_PLAYERS; i++)
12575 effective_action[i] = stored_player[i].effective_action;
12577 GameActions_EM(effective_action, warp_mode);
12581 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12584 void GameActions_SP_Main()
12586 byte effective_action[MAX_PLAYERS];
12587 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12590 for (i = 0; i < MAX_PLAYERS; i++)
12591 effective_action[i] = stored_player[i].effective_action;
12593 GameActions_SP(effective_action, warp_mode);
12597 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12600 void GameActions_RND()
12602 int magic_wall_x = 0, magic_wall_y = 0;
12603 int i, x, y, element, graphic;
12605 InitPlayfieldScanModeVars();
12607 #if USE_ONE_MORE_CHANGE_PER_FRAME
12608 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12610 SCAN_PLAYFIELD(x, y)
12612 ChangeCount[x][y] = 0;
12613 ChangeEvent[x][y] = -1;
12618 if (game.set_centered_player)
12620 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12622 /* switching to "all players" only possible if all players fit to screen */
12623 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12625 game.centered_player_nr_next = game.centered_player_nr;
12626 game.set_centered_player = FALSE;
12629 /* do not switch focus to non-existing (or non-active) player */
12630 if (game.centered_player_nr_next >= 0 &&
12631 !stored_player[game.centered_player_nr_next].active)
12633 game.centered_player_nr_next = game.centered_player_nr;
12634 game.set_centered_player = FALSE;
12638 if (game.set_centered_player &&
12639 ScreenMovPos == 0) /* screen currently aligned at tile position */
12643 if (game.centered_player_nr_next == -1)
12645 setScreenCenteredToAllPlayers(&sx, &sy);
12649 sx = stored_player[game.centered_player_nr_next].jx;
12650 sy = stored_player[game.centered_player_nr_next].jy;
12653 game.centered_player_nr = game.centered_player_nr_next;
12654 game.set_centered_player = FALSE;
12656 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12657 DrawGameDoorValues();
12660 for (i = 0; i < MAX_PLAYERS; i++)
12662 int actual_player_action = stored_player[i].effective_action;
12665 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12666 - rnd_equinox_tetrachloride 048
12667 - rnd_equinox_tetrachloride_ii 096
12668 - rnd_emanuel_schmieg 002
12669 - doctor_sloan_ww 001, 020
12671 if (stored_player[i].MovPos == 0)
12672 CheckGravityMovement(&stored_player[i]);
12675 /* overwrite programmed action with tape action */
12676 if (stored_player[i].programmed_action)
12677 actual_player_action = stored_player[i].programmed_action;
12679 PlayerActions(&stored_player[i], actual_player_action);
12681 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12684 ScrollScreen(NULL, SCROLL_GO_ON);
12686 /* for backwards compatibility, the following code emulates a fixed bug that
12687 occured when pushing elements (causing elements that just made their last
12688 pushing step to already (if possible) make their first falling step in the
12689 same game frame, which is bad); this code is also needed to use the famous
12690 "spring push bug" which is used in older levels and might be wanted to be
12691 used also in newer levels, but in this case the buggy pushing code is only
12692 affecting the "spring" element and no other elements */
12694 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12696 for (i = 0; i < MAX_PLAYERS; i++)
12698 struct PlayerInfo *player = &stored_player[i];
12699 int x = player->jx;
12700 int y = player->jy;
12702 if (player->active && player->is_pushing && player->is_moving &&
12704 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12705 Feld[x][y] == EL_SPRING))
12707 ContinueMoving(x, y);
12709 /* continue moving after pushing (this is actually a bug) */
12710 if (!IS_MOVING(x, y))
12711 Stop[x][y] = FALSE;
12717 debug_print_timestamp(0, "start main loop profiling");
12720 SCAN_PLAYFIELD(x, y)
12722 ChangeCount[x][y] = 0;
12723 ChangeEvent[x][y] = -1;
12725 /* this must be handled before main playfield loop */
12726 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12729 if (MovDelay[x][y] <= 0)
12733 #if USE_NEW_SNAP_DELAY
12734 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12737 if (MovDelay[x][y] <= 0)
12740 TEST_DrawLevelField(x, y);
12742 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12748 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12750 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12751 printf("GameActions(): This should never happen!\n");
12753 ChangePage[x][y] = -1;
12757 Stop[x][y] = FALSE;
12758 if (WasJustMoving[x][y] > 0)
12759 WasJustMoving[x][y]--;
12760 if (WasJustFalling[x][y] > 0)
12761 WasJustFalling[x][y]--;
12762 if (CheckCollision[x][y] > 0)
12763 CheckCollision[x][y]--;
12764 if (CheckImpact[x][y] > 0)
12765 CheckImpact[x][y]--;
12769 /* reset finished pushing action (not done in ContinueMoving() to allow
12770 continuous pushing animation for elements with zero push delay) */
12771 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12773 ResetGfxAnimation(x, y);
12774 TEST_DrawLevelField(x, y);
12778 if (IS_BLOCKED(x, y))
12782 Blocked2Moving(x, y, &oldx, &oldy);
12783 if (!IS_MOVING(oldx, oldy))
12785 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12786 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12787 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12788 printf("GameActions(): This should never happen!\n");
12795 debug_print_timestamp(0, "- time for pre-main loop:");
12798 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12799 SCAN_PLAYFIELD(x, y)
12801 element = Feld[x][y];
12802 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12807 int element2 = element;
12808 int graphic2 = graphic;
12810 int element2 = Feld[x][y];
12811 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12813 int last_gfx_frame = GfxFrame[x][y];
12815 if (graphic_info[graphic2].anim_global_sync)
12816 GfxFrame[x][y] = FrameCounter;
12817 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12818 GfxFrame[x][y] = CustomValue[x][y];
12819 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12820 GfxFrame[x][y] = element_info[element2].collect_score;
12821 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12822 GfxFrame[x][y] = ChangeDelay[x][y];
12824 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12825 DrawLevelGraphicAnimation(x, y, graphic2);
12828 ResetGfxFrame(x, y, TRUE);
12832 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12833 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12834 ResetRandomAnimationValue(x, y);
12838 SetRandomAnimationValue(x, y);
12842 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12845 #endif // -------------------- !!! TEST ONLY !!! --------------------
12848 debug_print_timestamp(0, "- time for TEST loop: -->");
12851 SCAN_PLAYFIELD(x, y)
12853 element = Feld[x][y];
12854 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12856 ResetGfxFrame(x, y, TRUE);
12858 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12859 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12860 ResetRandomAnimationValue(x, y);
12862 SetRandomAnimationValue(x, y);
12864 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12866 if (IS_INACTIVE(element))
12868 if (IS_ANIMATED(graphic))
12869 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12874 /* this may take place after moving, so 'element' may have changed */
12875 if (IS_CHANGING(x, y) &&
12876 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12878 int page = element_info[element].event_page_nr[CE_DELAY];
12881 HandleElementChange(x, y, page);
12883 if (CAN_CHANGE(element))
12884 HandleElementChange(x, y, page);
12886 if (HAS_ACTION(element))
12887 ExecuteCustomElementAction(x, y, element, page);
12890 element = Feld[x][y];
12891 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12894 #if 0 // ---------------------------------------------------------------------
12896 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12900 element = Feld[x][y];
12901 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12903 if (IS_ANIMATED(graphic) &&
12904 !IS_MOVING(x, y) &&
12906 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12908 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12909 TEST_DrawTwinkleOnField(x, y);
12911 else if (IS_MOVING(x, y))
12912 ContinueMoving(x, y);
12919 case EL_EM_EXIT_OPEN:
12920 case EL_SP_EXIT_OPEN:
12921 case EL_STEEL_EXIT_OPEN:
12922 case EL_EM_STEEL_EXIT_OPEN:
12923 case EL_SP_TERMINAL:
12924 case EL_SP_TERMINAL_ACTIVE:
12925 case EL_EXTRA_TIME:
12926 case EL_SHIELD_NORMAL:
12927 case EL_SHIELD_DEADLY:
12928 if (IS_ANIMATED(graphic))
12929 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12932 case EL_DYNAMITE_ACTIVE:
12933 case EL_EM_DYNAMITE_ACTIVE:
12934 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12935 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12936 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12937 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12938 case EL_SP_DISK_RED_ACTIVE:
12939 CheckDynamite(x, y);
12942 case EL_AMOEBA_GROWING:
12943 AmoebeWaechst(x, y);
12946 case EL_AMOEBA_SHRINKING:
12947 AmoebaDisappearing(x, y);
12950 #if !USE_NEW_AMOEBA_CODE
12951 case EL_AMOEBA_WET:
12952 case EL_AMOEBA_DRY:
12953 case EL_AMOEBA_FULL:
12955 case EL_EMC_DRIPPER:
12956 AmoebeAbleger(x, y);
12960 case EL_GAME_OF_LIFE:
12965 case EL_EXIT_CLOSED:
12969 case EL_EM_EXIT_CLOSED:
12973 case EL_STEEL_EXIT_CLOSED:
12974 CheckExitSteel(x, y);
12977 case EL_EM_STEEL_EXIT_CLOSED:
12978 CheckExitSteelEM(x, y);
12981 case EL_SP_EXIT_CLOSED:
12985 case EL_EXPANDABLE_WALL_GROWING:
12986 case EL_EXPANDABLE_STEELWALL_GROWING:
12987 MauerWaechst(x, y);
12990 case EL_EXPANDABLE_WALL:
12991 case EL_EXPANDABLE_WALL_HORIZONTAL:
12992 case EL_EXPANDABLE_WALL_VERTICAL:
12993 case EL_EXPANDABLE_WALL_ANY:
12994 case EL_BD_EXPANDABLE_WALL:
12995 MauerAbleger(x, y);
12998 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12999 case EL_EXPANDABLE_STEELWALL_VERTICAL:
13000 case EL_EXPANDABLE_STEELWALL_ANY:
13001 MauerAblegerStahl(x, y);
13005 CheckForDragon(x, y);
13011 case EL_ELEMENT_SNAPPING:
13012 case EL_DIAGONAL_SHRINKING:
13013 case EL_DIAGONAL_GROWING:
13016 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13018 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13023 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13024 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13029 #else // ---------------------------------------------------------------------
13031 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13035 element = Feld[x][y];
13036 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13038 if (IS_ANIMATED(graphic) &&
13039 !IS_MOVING(x, y) &&
13041 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13043 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13044 TEST_DrawTwinkleOnField(x, y);
13046 else if ((element == EL_ACID ||
13047 element == EL_EXIT_OPEN ||
13048 element == EL_EM_EXIT_OPEN ||
13049 element == EL_SP_EXIT_OPEN ||
13050 element == EL_STEEL_EXIT_OPEN ||
13051 element == EL_EM_STEEL_EXIT_OPEN ||
13052 element == EL_SP_TERMINAL ||
13053 element == EL_SP_TERMINAL_ACTIVE ||
13054 element == EL_EXTRA_TIME ||
13055 element == EL_SHIELD_NORMAL ||
13056 element == EL_SHIELD_DEADLY) &&
13057 IS_ANIMATED(graphic))
13058 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13059 else if (IS_MOVING(x, y))
13060 ContinueMoving(x, y);
13061 else if (IS_ACTIVE_BOMB(element))
13062 CheckDynamite(x, y);
13063 else if (element == EL_AMOEBA_GROWING)
13064 AmoebeWaechst(x, y);
13065 else if (element == EL_AMOEBA_SHRINKING)
13066 AmoebaDisappearing(x, y);
13068 #if !USE_NEW_AMOEBA_CODE
13069 else if (IS_AMOEBALIVE(element))
13070 AmoebeAbleger(x, y);
13073 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13075 else if (element == EL_EXIT_CLOSED)
13077 else if (element == EL_EM_EXIT_CLOSED)
13079 else if (element == EL_STEEL_EXIT_CLOSED)
13080 CheckExitSteel(x, y);
13081 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13082 CheckExitSteelEM(x, y);
13083 else if (element == EL_SP_EXIT_CLOSED)
13085 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13086 element == EL_EXPANDABLE_STEELWALL_GROWING)
13087 MauerWaechst(x, y);
13088 else if (element == EL_EXPANDABLE_WALL ||
13089 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13090 element == EL_EXPANDABLE_WALL_VERTICAL ||
13091 element == EL_EXPANDABLE_WALL_ANY ||
13092 element == EL_BD_EXPANDABLE_WALL)
13093 MauerAbleger(x, y);
13094 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13095 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13096 element == EL_EXPANDABLE_STEELWALL_ANY)
13097 MauerAblegerStahl(x, y);
13098 else if (element == EL_FLAMES)
13099 CheckForDragon(x, y);
13100 else if (element == EL_EXPLOSION)
13101 ; /* drawing of correct explosion animation is handled separately */
13102 else if (element == EL_ELEMENT_SNAPPING ||
13103 element == EL_DIAGONAL_SHRINKING ||
13104 element == EL_DIAGONAL_GROWING)
13106 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13108 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13110 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13111 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13113 #endif // ---------------------------------------------------------------------
13115 if (IS_BELT_ACTIVE(element))
13116 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13118 if (game.magic_wall_active)
13120 int jx = local_player->jx, jy = local_player->jy;
13122 /* play the element sound at the position nearest to the player */
13123 if ((element == EL_MAGIC_WALL_FULL ||
13124 element == EL_MAGIC_WALL_ACTIVE ||
13125 element == EL_MAGIC_WALL_EMPTYING ||
13126 element == EL_BD_MAGIC_WALL_FULL ||
13127 element == EL_BD_MAGIC_WALL_ACTIVE ||
13128 element == EL_BD_MAGIC_WALL_EMPTYING ||
13129 element == EL_DC_MAGIC_WALL_FULL ||
13130 element == EL_DC_MAGIC_WALL_ACTIVE ||
13131 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13132 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13141 debug_print_timestamp(0, "- time for MAIN loop: -->");
13144 #if USE_NEW_AMOEBA_CODE
13145 /* new experimental amoeba growth stuff */
13146 if (!(FrameCounter % 8))
13148 static unsigned int random = 1684108901;
13150 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13152 x = RND(lev_fieldx);
13153 y = RND(lev_fieldy);
13154 element = Feld[x][y];
13156 if (!IS_PLAYER(x,y) &&
13157 (element == EL_EMPTY ||
13158 CAN_GROW_INTO(element) ||
13159 element == EL_QUICKSAND_EMPTY ||
13160 element == EL_QUICKSAND_FAST_EMPTY ||
13161 element == EL_ACID_SPLASH_LEFT ||
13162 element == EL_ACID_SPLASH_RIGHT))
13164 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13165 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13166 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13167 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13168 Feld[x][y] = EL_AMOEBA_DROP;
13171 random = random * 129 + 1;
13177 if (game.explosions_delayed)
13180 game.explosions_delayed = FALSE;
13182 SCAN_PLAYFIELD(x, y)
13184 element = Feld[x][y];
13186 if (ExplodeField[x][y])
13187 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13188 else if (element == EL_EXPLOSION)
13189 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13191 ExplodeField[x][y] = EX_TYPE_NONE;
13194 game.explosions_delayed = TRUE;
13197 if (game.magic_wall_active)
13199 if (!(game.magic_wall_time_left % 4))
13201 int element = Feld[magic_wall_x][magic_wall_y];
13203 if (element == EL_BD_MAGIC_WALL_FULL ||
13204 element == EL_BD_MAGIC_WALL_ACTIVE ||
13205 element == EL_BD_MAGIC_WALL_EMPTYING)
13206 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13207 else if (element == EL_DC_MAGIC_WALL_FULL ||
13208 element == EL_DC_MAGIC_WALL_ACTIVE ||
13209 element == EL_DC_MAGIC_WALL_EMPTYING)
13210 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13212 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13215 if (game.magic_wall_time_left > 0)
13217 game.magic_wall_time_left--;
13219 if (!game.magic_wall_time_left)
13221 SCAN_PLAYFIELD(x, y)
13223 element = Feld[x][y];
13225 if (element == EL_MAGIC_WALL_ACTIVE ||
13226 element == EL_MAGIC_WALL_FULL)
13228 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13229 TEST_DrawLevelField(x, y);
13231 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13232 element == EL_BD_MAGIC_WALL_FULL)
13234 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13235 TEST_DrawLevelField(x, y);
13237 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13238 element == EL_DC_MAGIC_WALL_FULL)
13240 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13241 TEST_DrawLevelField(x, y);
13245 game.magic_wall_active = FALSE;
13250 if (game.light_time_left > 0)
13252 game.light_time_left--;
13254 if (game.light_time_left == 0)
13255 RedrawAllLightSwitchesAndInvisibleElements();
13258 if (game.timegate_time_left > 0)
13260 game.timegate_time_left--;
13262 if (game.timegate_time_left == 0)
13263 CloseAllOpenTimegates();
13266 if (game.lenses_time_left > 0)
13268 game.lenses_time_left--;
13270 if (game.lenses_time_left == 0)
13271 RedrawAllInvisibleElementsForLenses();
13274 if (game.magnify_time_left > 0)
13276 game.magnify_time_left--;
13278 if (game.magnify_time_left == 0)
13279 RedrawAllInvisibleElementsForMagnifier();
13282 for (i = 0; i < MAX_PLAYERS; i++)
13284 struct PlayerInfo *player = &stored_player[i];
13286 if (SHIELD_ON(player))
13288 if (player->shield_deadly_time_left)
13289 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13290 else if (player->shield_normal_time_left)
13291 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13295 #if USE_DELAYED_GFX_REDRAW
13296 SCAN_PLAYFIELD(x, y)
13299 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13301 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13302 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13305 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13306 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13308 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13309 DrawLevelField(x, y);
13311 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13312 DrawLevelFieldCrumbled(x, y);
13314 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13315 DrawLevelFieldCrumbledNeighbours(x, y);
13317 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13318 DrawTwinkleOnField(x, y);
13321 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13328 PlayAllPlayersSound();
13330 if (options.debug) /* calculate frames per second */
13332 static unsigned int fps_counter = 0;
13333 static int fps_frames = 0;
13334 unsigned int fps_delay_ms = Counter() - fps_counter;
13338 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13340 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13343 fps_counter = Counter();
13346 redraw_mask |= REDRAW_FPS;
13349 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13351 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13353 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13355 local_player->show_envelope = 0;
13359 debug_print_timestamp(0, "stop main loop profiling ");
13360 printf("----------------------------------------------------------\n");
13363 /* use random number generator in every frame to make it less predictable */
13364 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13368 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13370 int min_x = x, min_y = y, max_x = x, max_y = y;
13373 for (i = 0; i < MAX_PLAYERS; i++)
13375 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13377 if (!stored_player[i].active || &stored_player[i] == player)
13380 min_x = MIN(min_x, jx);
13381 min_y = MIN(min_y, jy);
13382 max_x = MAX(max_x, jx);
13383 max_y = MAX(max_y, jy);
13386 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13389 static boolean AllPlayersInVisibleScreen()
13393 for (i = 0; i < MAX_PLAYERS; i++)
13395 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13397 if (!stored_player[i].active)
13400 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13407 void ScrollLevel(int dx, int dy)
13410 /* (directly solved in BlitBitmap() now) */
13411 static Bitmap *bitmap_db_field2 = NULL;
13412 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13419 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13420 /* only horizontal XOR vertical scroll direction allowed */
13421 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13426 /* (directly solved in BlitBitmap() now) */
13427 if (bitmap_db_field2 == NULL)
13428 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13430 /* needed when blitting directly to same bitmap -- should not be needed with
13431 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13432 BlitBitmap(drawto_field, bitmap_db_field2,
13433 FX + TILEX * (dx == -1) - softscroll_offset,
13434 FY + TILEY * (dy == -1) - softscroll_offset,
13435 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13436 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13437 FX + TILEX * (dx == 1) - softscroll_offset,
13438 FY + TILEY * (dy == 1) - softscroll_offset);
13439 BlitBitmap(bitmap_db_field2, drawto_field,
13440 FX + TILEX * (dx == 1) - softscroll_offset,
13441 FY + TILEY * (dy == 1) - softscroll_offset,
13442 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13443 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13444 FX + TILEX * (dx == 1) - softscroll_offset,
13445 FY + TILEY * (dy == 1) - softscroll_offset);
13450 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13451 int xsize = (BX2 - BX1 + 1);
13452 int ysize = (BY2 - BY1 + 1);
13453 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13454 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13455 int step = (start < end ? +1 : -1);
13457 for (i = start; i != end; i += step)
13459 BlitBitmap(drawto_field, drawto_field,
13460 FX + TILEX * (dx != 0 ? i + step : 0),
13461 FY + TILEY * (dy != 0 ? i + step : 0),
13462 TILEX * (dx != 0 ? 1 : xsize),
13463 TILEY * (dy != 0 ? 1 : ysize),
13464 FX + TILEX * (dx != 0 ? i : 0),
13465 FY + TILEY * (dy != 0 ? i : 0));
13472 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13474 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13478 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13480 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13485 BlitBitmap(drawto_field, drawto_field,
13486 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13487 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13488 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13489 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13490 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13491 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13493 BlitBitmap(drawto_field, drawto_field,
13494 FX + TILEX * (dx == -1) - softscroll_offset,
13495 FY + TILEY * (dy == -1) - softscroll_offset,
13496 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13497 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13498 FX + TILEX * (dx == 1) - softscroll_offset,
13499 FY + TILEY * (dy == 1) - softscroll_offset);
13507 x = (dx == 1 ? BX1 : BX2);
13508 for (y = BY1; y <= BY2; y++)
13509 DrawScreenField(x, y);
13514 y = (dy == 1 ? BY1 : BY2);
13515 for (x = BX1; x <= BX2; x++)
13516 DrawScreenField(x, y);
13519 redraw_mask |= REDRAW_FIELD;
13522 static boolean canFallDown(struct PlayerInfo *player)
13524 int jx = player->jx, jy = player->jy;
13526 return (IN_LEV_FIELD(jx, jy + 1) &&
13527 (IS_FREE(jx, jy + 1) ||
13528 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13529 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13530 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13533 static boolean canPassField(int x, int y, int move_dir)
13535 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13536 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13537 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13538 int nextx = x + dx;
13539 int nexty = y + dy;
13540 int element = Feld[x][y];
13542 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13543 !CAN_MOVE(element) &&
13544 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13545 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13546 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13549 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13551 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13552 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13553 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13557 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13558 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13559 (IS_DIGGABLE(Feld[newx][newy]) ||
13560 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13561 canPassField(newx, newy, move_dir)));
13564 static void CheckGravityMovement(struct PlayerInfo *player)
13566 #if USE_PLAYER_GRAVITY
13567 if (player->gravity && !player->programmed_action)
13569 if (game.gravity && !player->programmed_action)
13572 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13573 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13574 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13575 int jx = player->jx, jy = player->jy;
13576 boolean player_is_moving_to_valid_field =
13577 (!player_is_snapping &&
13578 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13579 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13580 boolean player_can_fall_down = canFallDown(player);
13582 if (player_can_fall_down &&
13583 !player_is_moving_to_valid_field)
13584 player->programmed_action = MV_DOWN;
13588 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13590 return CheckGravityMovement(player);
13592 #if USE_PLAYER_GRAVITY
13593 if (player->gravity && !player->programmed_action)
13595 if (game.gravity && !player->programmed_action)
13598 int jx = player->jx, jy = player->jy;
13599 boolean field_under_player_is_free =
13600 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13601 boolean player_is_standing_on_valid_field =
13602 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13603 (IS_WALKABLE(Feld[jx][jy]) &&
13604 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13606 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13607 player->programmed_action = MV_DOWN;
13612 MovePlayerOneStep()
13613 -----------------------------------------------------------------------------
13614 dx, dy: direction (non-diagonal) to try to move the player to
13615 real_dx, real_dy: direction as read from input device (can be diagonal)
13618 boolean MovePlayerOneStep(struct PlayerInfo *player,
13619 int dx, int dy, int real_dx, int real_dy)
13621 int jx = player->jx, jy = player->jy;
13622 int new_jx = jx + dx, new_jy = jy + dy;
13623 #if !USE_FIXED_DONT_RUN_INTO
13627 boolean player_can_move = !player->cannot_move;
13629 if (!player->active || (!dx && !dy))
13630 return MP_NO_ACTION;
13632 player->MovDir = (dx < 0 ? MV_LEFT :
13633 dx > 0 ? MV_RIGHT :
13635 dy > 0 ? MV_DOWN : MV_NONE);
13637 if (!IN_LEV_FIELD(new_jx, new_jy))
13638 return MP_NO_ACTION;
13640 if (!player_can_move)
13642 if (player->MovPos == 0)
13644 player->is_moving = FALSE;
13645 player->is_digging = FALSE;
13646 player->is_collecting = FALSE;
13647 player->is_snapping = FALSE;
13648 player->is_pushing = FALSE;
13653 if (!options.network && game.centered_player_nr == -1 &&
13654 !AllPlayersInSight(player, new_jx, new_jy))
13655 return MP_NO_ACTION;
13657 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13658 return MP_NO_ACTION;
13661 #if !USE_FIXED_DONT_RUN_INTO
13662 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13664 /* (moved to DigField()) */
13665 if (player_can_move && DONT_RUN_INTO(element))
13667 if (element == EL_ACID && dx == 0 && dy == 1)
13669 SplashAcid(new_jx, new_jy);
13670 Feld[jx][jy] = EL_PLAYER_1;
13671 InitMovingField(jx, jy, MV_DOWN);
13672 Store[jx][jy] = EL_ACID;
13673 ContinueMoving(jx, jy);
13674 BuryPlayer(player);
13677 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13683 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13684 if (can_move != MP_MOVING)
13687 /* check if DigField() has caused relocation of the player */
13688 if (player->jx != jx || player->jy != jy)
13689 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13691 StorePlayer[jx][jy] = 0;
13692 player->last_jx = jx;
13693 player->last_jy = jy;
13694 player->jx = new_jx;
13695 player->jy = new_jy;
13696 StorePlayer[new_jx][new_jy] = player->element_nr;
13698 if (player->move_delay_value_next != -1)
13700 player->move_delay_value = player->move_delay_value_next;
13701 player->move_delay_value_next = -1;
13705 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13707 player->step_counter++;
13709 PlayerVisit[jx][jy] = FrameCounter;
13711 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13712 player->is_moving = TRUE;
13716 /* should better be called in MovePlayer(), but this breaks some tapes */
13717 ScrollPlayer(player, SCROLL_INIT);
13723 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13725 int jx = player->jx, jy = player->jy;
13726 int old_jx = jx, old_jy = jy;
13727 int moved = MP_NO_ACTION;
13729 if (!player->active)
13734 if (player->MovPos == 0)
13736 player->is_moving = FALSE;
13737 player->is_digging = FALSE;
13738 player->is_collecting = FALSE;
13739 player->is_snapping = FALSE;
13740 player->is_pushing = FALSE;
13746 if (player->move_delay > 0)
13749 player->move_delay = -1; /* set to "uninitialized" value */
13751 /* store if player is automatically moved to next field */
13752 player->is_auto_moving = (player->programmed_action != MV_NONE);
13754 /* remove the last programmed player action */
13755 player->programmed_action = 0;
13757 if (player->MovPos)
13759 /* should only happen if pre-1.2 tape recordings are played */
13760 /* this is only for backward compatibility */
13762 int original_move_delay_value = player->move_delay_value;
13765 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13769 /* scroll remaining steps with finest movement resolution */
13770 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13772 while (player->MovPos)
13774 ScrollPlayer(player, SCROLL_GO_ON);
13775 ScrollScreen(NULL, SCROLL_GO_ON);
13777 AdvanceFrameAndPlayerCounters(player->index_nr);
13783 player->move_delay_value = original_move_delay_value;
13786 player->is_active = FALSE;
13788 if (player->last_move_dir & MV_HORIZONTAL)
13790 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13791 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13795 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13796 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13799 #if USE_FIXED_BORDER_RUNNING_GFX
13800 if (!moved && !player->is_active)
13802 player->is_moving = FALSE;
13803 player->is_digging = FALSE;
13804 player->is_collecting = FALSE;
13805 player->is_snapping = FALSE;
13806 player->is_pushing = FALSE;
13814 if (moved & MP_MOVING && !ScreenMovPos &&
13815 (player->index_nr == game.centered_player_nr ||
13816 game.centered_player_nr == -1))
13818 if (moved & MP_MOVING && !ScreenMovPos &&
13819 (player == local_player || !options.network))
13822 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13823 int offset = game.scroll_delay_value;
13825 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13827 /* actual player has left the screen -- scroll in that direction */
13828 if (jx != old_jx) /* player has moved horizontally */
13829 scroll_x += (jx - old_jx);
13830 else /* player has moved vertically */
13831 scroll_y += (jy - old_jy);
13835 if (jx != old_jx) /* player has moved horizontally */
13837 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13838 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13839 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13841 /* don't scroll over playfield boundaries */
13842 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13843 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13845 /* don't scroll more than one field at a time */
13846 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13848 /* don't scroll against the player's moving direction */
13849 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13850 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13851 scroll_x = old_scroll_x;
13853 else /* player has moved vertically */
13855 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13856 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13857 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13859 /* don't scroll over playfield boundaries */
13860 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13861 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13863 /* don't scroll more than one field at a time */
13864 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13866 /* don't scroll against the player's moving direction */
13867 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13868 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13869 scroll_y = old_scroll_y;
13873 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13876 if (!options.network && game.centered_player_nr == -1 &&
13877 !AllPlayersInVisibleScreen())
13879 scroll_x = old_scroll_x;
13880 scroll_y = old_scroll_y;
13884 if (!options.network && !AllPlayersInVisibleScreen())
13886 scroll_x = old_scroll_x;
13887 scroll_y = old_scroll_y;
13892 ScrollScreen(player, SCROLL_INIT);
13893 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13898 player->StepFrame = 0;
13900 if (moved & MP_MOVING)
13902 if (old_jx != jx && old_jy == jy)
13903 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13904 else if (old_jx == jx && old_jy != jy)
13905 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13907 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13909 player->last_move_dir = player->MovDir;
13910 player->is_moving = TRUE;
13911 player->is_snapping = FALSE;
13912 player->is_switching = FALSE;
13913 player->is_dropping = FALSE;
13914 player->is_dropping_pressed = FALSE;
13915 player->drop_pressed_delay = 0;
13918 /* should better be called here than above, but this breaks some tapes */
13919 ScrollPlayer(player, SCROLL_INIT);
13924 CheckGravityMovementWhenNotMoving(player);
13926 player->is_moving = FALSE;
13928 /* at this point, the player is allowed to move, but cannot move right now
13929 (e.g. because of something blocking the way) -- ensure that the player
13930 is also allowed to move in the next frame (in old versions before 3.1.1,
13931 the player was forced to wait again for eight frames before next try) */
13933 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13934 player->move_delay = 0; /* allow direct movement in the next frame */
13937 if (player->move_delay == -1) /* not yet initialized by DigField() */
13938 player->move_delay = player->move_delay_value;
13940 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13942 TestIfPlayerTouchesBadThing(jx, jy);
13943 TestIfPlayerTouchesCustomElement(jx, jy);
13946 if (!player->active)
13947 RemovePlayer(player);
13952 void ScrollPlayer(struct PlayerInfo *player, int mode)
13954 int jx = player->jx, jy = player->jy;
13955 int last_jx = player->last_jx, last_jy = player->last_jy;
13956 int move_stepsize = TILEX / player->move_delay_value;
13958 #if USE_NEW_PLAYER_SPEED
13959 if (!player->active)
13962 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13965 if (!player->active || player->MovPos == 0)
13969 if (mode == SCROLL_INIT)
13971 player->actual_frame_counter = FrameCounter;
13972 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13974 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13975 Feld[last_jx][last_jy] == EL_EMPTY)
13977 int last_field_block_delay = 0; /* start with no blocking at all */
13978 int block_delay_adjustment = player->block_delay_adjustment;
13980 /* if player blocks last field, add delay for exactly one move */
13981 if (player->block_last_field)
13983 last_field_block_delay += player->move_delay_value;
13985 /* when blocking enabled, prevent moving up despite gravity */
13986 #if USE_PLAYER_GRAVITY
13987 if (player->gravity && player->MovDir == MV_UP)
13988 block_delay_adjustment = -1;
13990 if (game.gravity && player->MovDir == MV_UP)
13991 block_delay_adjustment = -1;
13995 /* add block delay adjustment (also possible when not blocking) */
13996 last_field_block_delay += block_delay_adjustment;
13998 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13999 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14002 #if USE_NEW_PLAYER_SPEED
14003 if (player->MovPos != 0) /* player has not yet reached destination */
14009 else if (!FrameReached(&player->actual_frame_counter, 1))
14012 #if USE_NEW_PLAYER_SPEED
14013 if (player->MovPos != 0)
14015 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14016 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14018 /* before DrawPlayer() to draw correct player graphic for this case */
14019 if (player->MovPos == 0)
14020 CheckGravityMovement(player);
14023 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14024 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14026 /* before DrawPlayer() to draw correct player graphic for this case */
14027 if (player->MovPos == 0)
14028 CheckGravityMovement(player);
14031 if (player->MovPos == 0) /* player reached destination field */
14033 if (player->move_delay_reset_counter > 0)
14035 player->move_delay_reset_counter--;
14037 if (player->move_delay_reset_counter == 0)
14039 /* continue with normal speed after quickly moving through gate */
14040 HALVE_PLAYER_SPEED(player);
14042 /* be able to make the next move without delay */
14043 player->move_delay = 0;
14047 player->last_jx = jx;
14048 player->last_jy = jy;
14050 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14051 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14053 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14055 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14056 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14058 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14060 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14061 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14063 DrawPlayer(player); /* needed here only to cleanup last field */
14064 RemovePlayer(player);
14066 if (local_player->friends_still_needed == 0 ||
14067 IS_SP_ELEMENT(Feld[jx][jy]))
14068 PlayerWins(player);
14071 /* this breaks one level: "machine", level 000 */
14073 int move_direction = player->MovDir;
14074 int enter_side = MV_DIR_OPPOSITE(move_direction);
14075 int leave_side = move_direction;
14076 int old_jx = last_jx;
14077 int old_jy = last_jy;
14078 int old_element = Feld[old_jx][old_jy];
14079 int new_element = Feld[jx][jy];
14081 if (IS_CUSTOM_ELEMENT(old_element))
14082 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14084 player->index_bit, leave_side);
14086 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14087 CE_PLAYER_LEAVES_X,
14088 player->index_bit, leave_side);
14090 if (IS_CUSTOM_ELEMENT(new_element))
14091 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14092 player->index_bit, enter_side);
14094 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14095 CE_PLAYER_ENTERS_X,
14096 player->index_bit, enter_side);
14098 #if USE_FIX_CE_ACTION_WITH_PLAYER
14099 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14100 CE_MOVE_OF_X, move_direction);
14102 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14103 CE_MOVE_OF_X, move_direction);
14107 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14109 TestIfPlayerTouchesBadThing(jx, jy);
14110 TestIfPlayerTouchesCustomElement(jx, jy);
14112 /* needed because pushed element has not yet reached its destination,
14113 so it would trigger a change event at its previous field location */
14114 if (!player->is_pushing)
14115 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14117 if (!player->active)
14118 RemovePlayer(player);
14121 if (!local_player->LevelSolved && level.use_step_counter)
14131 if (TimeLeft <= 10 && setup.time_limit)
14132 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14135 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14137 DisplayGameControlValues();
14139 DrawGameValue_Time(TimeLeft);
14142 if (!TimeLeft && setup.time_limit)
14143 for (i = 0; i < MAX_PLAYERS; i++)
14144 KillPlayer(&stored_player[i]);
14147 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14149 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14151 DisplayGameControlValues();
14154 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14155 DrawGameValue_Time(TimePlayed);
14159 if (tape.single_step && tape.recording && !tape.pausing &&
14160 !player->programmed_action)
14161 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14165 void ScrollScreen(struct PlayerInfo *player, int mode)
14167 static unsigned int screen_frame_counter = 0;
14169 if (mode == SCROLL_INIT)
14171 /* set scrolling step size according to actual player's moving speed */
14172 ScrollStepSize = TILEX / player->move_delay_value;
14174 screen_frame_counter = FrameCounter;
14175 ScreenMovDir = player->MovDir;
14176 ScreenMovPos = player->MovPos;
14177 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14180 else if (!FrameReached(&screen_frame_counter, 1))
14185 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14186 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14187 redraw_mask |= REDRAW_FIELD;
14190 ScreenMovDir = MV_NONE;
14193 void TestIfPlayerTouchesCustomElement(int x, int y)
14195 static int xy[4][2] =
14202 static int trigger_sides[4][2] =
14204 /* center side border side */
14205 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14206 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14207 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14208 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14210 static int touch_dir[4] =
14212 MV_LEFT | MV_RIGHT,
14217 int center_element = Feld[x][y]; /* should always be non-moving! */
14220 for (i = 0; i < NUM_DIRECTIONS; i++)
14222 int xx = x + xy[i][0];
14223 int yy = y + xy[i][1];
14224 int center_side = trigger_sides[i][0];
14225 int border_side = trigger_sides[i][1];
14226 int border_element;
14228 if (!IN_LEV_FIELD(xx, yy))
14231 if (IS_PLAYER(x, y)) /* player found at center element */
14233 struct PlayerInfo *player = PLAYERINFO(x, y);
14235 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14236 border_element = Feld[xx][yy]; /* may be moving! */
14237 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14238 border_element = Feld[xx][yy];
14239 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14240 border_element = MovingOrBlocked2Element(xx, yy);
14242 continue; /* center and border element do not touch */
14244 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14245 player->index_bit, border_side);
14246 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14247 CE_PLAYER_TOUCHES_X,
14248 player->index_bit, border_side);
14250 #if USE_FIX_CE_ACTION_WITH_PLAYER
14252 /* use player element that is initially defined in the level playfield,
14253 not the player element that corresponds to the runtime player number
14254 (example: a level that contains EL_PLAYER_3 as the only player would
14255 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14256 int player_element = PLAYERINFO(x, y)->initial_element;
14258 CheckElementChangeBySide(xx, yy, border_element, player_element,
14259 CE_TOUCHING_X, border_side);
14263 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14265 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14267 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14269 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14270 continue; /* center and border element do not touch */
14273 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14274 player->index_bit, center_side);
14275 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14276 CE_PLAYER_TOUCHES_X,
14277 player->index_bit, center_side);
14279 #if USE_FIX_CE_ACTION_WITH_PLAYER
14281 /* use player element that is initially defined in the level playfield,
14282 not the player element that corresponds to the runtime player number
14283 (example: a level that contains EL_PLAYER_3 as the only player would
14284 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14285 int player_element = PLAYERINFO(xx, yy)->initial_element;
14287 CheckElementChangeBySide(x, y, center_element, player_element,
14288 CE_TOUCHING_X, center_side);
14297 #if USE_ELEMENT_TOUCHING_BUGFIX
14299 void TestIfElementTouchesCustomElement(int x, int y)
14301 static int xy[4][2] =
14308 static int trigger_sides[4][2] =
14310 /* center side border side */
14311 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14312 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14313 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14314 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14316 static int touch_dir[4] =
14318 MV_LEFT | MV_RIGHT,
14323 boolean change_center_element = FALSE;
14324 int center_element = Feld[x][y]; /* should always be non-moving! */
14325 int border_element_old[NUM_DIRECTIONS];
14328 for (i = 0; i < NUM_DIRECTIONS; i++)
14330 int xx = x + xy[i][0];
14331 int yy = y + xy[i][1];
14332 int border_element;
14334 border_element_old[i] = -1;
14336 if (!IN_LEV_FIELD(xx, yy))
14339 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14340 border_element = Feld[xx][yy]; /* may be moving! */
14341 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14342 border_element = Feld[xx][yy];
14343 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14344 border_element = MovingOrBlocked2Element(xx, yy);
14346 continue; /* center and border element do not touch */
14348 border_element_old[i] = border_element;
14351 for (i = 0; i < NUM_DIRECTIONS; i++)
14353 int xx = x + xy[i][0];
14354 int yy = y + xy[i][1];
14355 int center_side = trigger_sides[i][0];
14356 int border_element = border_element_old[i];
14358 if (border_element == -1)
14361 /* check for change of border element */
14362 CheckElementChangeBySide(xx, yy, border_element, center_element,
14363 CE_TOUCHING_X, center_side);
14365 /* (center element cannot be player, so we dont have to check this here) */
14368 for (i = 0; i < NUM_DIRECTIONS; i++)
14370 int xx = x + xy[i][0];
14371 int yy = y + xy[i][1];
14372 int border_side = trigger_sides[i][1];
14373 int border_element = border_element_old[i];
14375 if (border_element == -1)
14378 /* check for change of center element (but change it only once) */
14379 if (!change_center_element)
14380 change_center_element =
14381 CheckElementChangeBySide(x, y, center_element, border_element,
14382 CE_TOUCHING_X, border_side);
14384 #if USE_FIX_CE_ACTION_WITH_PLAYER
14385 if (IS_PLAYER(xx, yy))
14387 /* use player element that is initially defined in the level playfield,
14388 not the player element that corresponds to the runtime player number
14389 (example: a level that contains EL_PLAYER_3 as the only player would
14390 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14391 int player_element = PLAYERINFO(xx, yy)->initial_element;
14393 CheckElementChangeBySide(x, y, center_element, player_element,
14394 CE_TOUCHING_X, border_side);
14402 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14404 static int xy[4][2] =
14411 static int trigger_sides[4][2] =
14413 /* center side border side */
14414 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14415 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14416 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14417 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14419 static int touch_dir[4] =
14421 MV_LEFT | MV_RIGHT,
14426 boolean change_center_element = FALSE;
14427 int center_element = Feld[x][y]; /* should always be non-moving! */
14430 for (i = 0; i < NUM_DIRECTIONS; i++)
14432 int xx = x + xy[i][0];
14433 int yy = y + xy[i][1];
14434 int center_side = trigger_sides[i][0];
14435 int border_side = trigger_sides[i][1];
14436 int border_element;
14438 if (!IN_LEV_FIELD(xx, yy))
14441 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14442 border_element = Feld[xx][yy]; /* may be moving! */
14443 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14444 border_element = Feld[xx][yy];
14445 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14446 border_element = MovingOrBlocked2Element(xx, yy);
14448 continue; /* center and border element do not touch */
14450 /* check for change of center element (but change it only once) */
14451 if (!change_center_element)
14452 change_center_element =
14453 CheckElementChangeBySide(x, y, center_element, border_element,
14454 CE_TOUCHING_X, border_side);
14456 /* check for change of border element */
14457 CheckElementChangeBySide(xx, yy, border_element, center_element,
14458 CE_TOUCHING_X, center_side);
14464 void TestIfElementHitsCustomElement(int x, int y, int direction)
14466 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14467 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14468 int hitx = x + dx, hity = y + dy;
14469 int hitting_element = Feld[x][y];
14470 int touched_element;
14472 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14475 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14476 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14478 if (IN_LEV_FIELD(hitx, hity))
14480 int opposite_direction = MV_DIR_OPPOSITE(direction);
14481 int hitting_side = direction;
14482 int touched_side = opposite_direction;
14483 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14484 MovDir[hitx][hity] != direction ||
14485 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14491 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14492 CE_HITTING_X, touched_side);
14494 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14495 CE_HIT_BY_X, hitting_side);
14497 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14498 CE_HIT_BY_SOMETHING, opposite_direction);
14500 #if USE_FIX_CE_ACTION_WITH_PLAYER
14501 if (IS_PLAYER(hitx, hity))
14503 /* use player element that is initially defined in the level playfield,
14504 not the player element that corresponds to the runtime player number
14505 (example: a level that contains EL_PLAYER_3 as the only player would
14506 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14507 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14509 CheckElementChangeBySide(x, y, hitting_element, player_element,
14510 CE_HITTING_X, touched_side);
14516 /* "hitting something" is also true when hitting the playfield border */
14517 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14518 CE_HITTING_SOMETHING, direction);
14522 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14524 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14525 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14526 int hitx = x + dx, hity = y + dy;
14527 int hitting_element = Feld[x][y];
14528 int touched_element;
14530 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14531 !IS_FREE(hitx, hity) &&
14532 (!IS_MOVING(hitx, hity) ||
14533 MovDir[hitx][hity] != direction ||
14534 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14537 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14541 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14545 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14546 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14548 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14549 EP_CAN_SMASH_EVERYTHING, direction);
14551 if (IN_LEV_FIELD(hitx, hity))
14553 int opposite_direction = MV_DIR_OPPOSITE(direction);
14554 int hitting_side = direction;
14555 int touched_side = opposite_direction;
14557 int touched_element = MovingOrBlocked2Element(hitx, hity);
14560 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14561 MovDir[hitx][hity] != direction ||
14562 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14571 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14572 CE_SMASHED_BY_SOMETHING, opposite_direction);
14574 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14575 CE_OTHER_IS_SMASHING, touched_side);
14577 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14578 CE_OTHER_GETS_SMASHED, hitting_side);
14584 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14586 int i, kill_x = -1, kill_y = -1;
14588 int bad_element = -1;
14589 static int test_xy[4][2] =
14596 static int test_dir[4] =
14604 for (i = 0; i < NUM_DIRECTIONS; i++)
14606 int test_x, test_y, test_move_dir, test_element;
14608 test_x = good_x + test_xy[i][0];
14609 test_y = good_y + test_xy[i][1];
14611 if (!IN_LEV_FIELD(test_x, test_y))
14615 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14617 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14619 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14620 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14622 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14623 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14627 bad_element = test_element;
14633 if (kill_x != -1 || kill_y != -1)
14635 if (IS_PLAYER(good_x, good_y))
14637 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14639 if (player->shield_deadly_time_left > 0 &&
14640 !IS_INDESTRUCTIBLE(bad_element))
14641 Bang(kill_x, kill_y);
14642 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14643 KillPlayer(player);
14646 Bang(good_x, good_y);
14650 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14652 int i, kill_x = -1, kill_y = -1;
14653 int bad_element = Feld[bad_x][bad_y];
14654 static int test_xy[4][2] =
14661 static int touch_dir[4] =
14663 MV_LEFT | MV_RIGHT,
14668 static int test_dir[4] =
14676 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14679 for (i = 0; i < NUM_DIRECTIONS; i++)
14681 int test_x, test_y, test_move_dir, test_element;
14683 test_x = bad_x + test_xy[i][0];
14684 test_y = bad_y + test_xy[i][1];
14686 if (!IN_LEV_FIELD(test_x, test_y))
14690 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14692 test_element = Feld[test_x][test_y];
14694 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14695 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14697 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14698 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14700 /* good thing is player or penguin that does not move away */
14701 if (IS_PLAYER(test_x, test_y))
14703 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14705 if (bad_element == EL_ROBOT && player->is_moving)
14706 continue; /* robot does not kill player if he is moving */
14708 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14710 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14711 continue; /* center and border element do not touch */
14719 else if (test_element == EL_PENGUIN)
14729 if (kill_x != -1 || kill_y != -1)
14731 if (IS_PLAYER(kill_x, kill_y))
14733 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14735 if (player->shield_deadly_time_left > 0 &&
14736 !IS_INDESTRUCTIBLE(bad_element))
14737 Bang(bad_x, bad_y);
14738 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14739 KillPlayer(player);
14742 Bang(kill_x, kill_y);
14746 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14748 int bad_element = Feld[bad_x][bad_y];
14749 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14750 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14751 int test_x = bad_x + dx, test_y = bad_y + dy;
14752 int test_move_dir, test_element;
14753 int kill_x = -1, kill_y = -1;
14755 if (!IN_LEV_FIELD(test_x, test_y))
14759 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14761 test_element = Feld[test_x][test_y];
14763 if (test_move_dir != bad_move_dir)
14765 /* good thing can be player or penguin that does not move away */
14766 if (IS_PLAYER(test_x, test_y))
14768 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14770 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14771 player as being hit when he is moving towards the bad thing, because
14772 the "get hit by" condition would be lost after the player stops) */
14773 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14774 return; /* player moves away from bad thing */
14779 else if (test_element == EL_PENGUIN)
14786 if (kill_x != -1 || kill_y != -1)
14788 if (IS_PLAYER(kill_x, kill_y))
14790 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14792 if (player->shield_deadly_time_left > 0 &&
14793 !IS_INDESTRUCTIBLE(bad_element))
14794 Bang(bad_x, bad_y);
14795 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14796 KillPlayer(player);
14799 Bang(kill_x, kill_y);
14803 void TestIfPlayerTouchesBadThing(int x, int y)
14805 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14808 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14810 TestIfGoodThingHitsBadThing(x, y, move_dir);
14813 void TestIfBadThingTouchesPlayer(int x, int y)
14815 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14818 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14820 TestIfBadThingHitsGoodThing(x, y, move_dir);
14823 void TestIfFriendTouchesBadThing(int x, int y)
14825 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14828 void TestIfBadThingTouchesFriend(int x, int y)
14830 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14833 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14835 int i, kill_x = bad_x, kill_y = bad_y;
14836 static int xy[4][2] =
14844 for (i = 0; i < NUM_DIRECTIONS; i++)
14848 x = bad_x + xy[i][0];
14849 y = bad_y + xy[i][1];
14850 if (!IN_LEV_FIELD(x, y))
14853 element = Feld[x][y];
14854 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14855 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14863 if (kill_x != bad_x || kill_y != bad_y)
14864 Bang(bad_x, bad_y);
14867 void KillPlayer(struct PlayerInfo *player)
14869 int jx = player->jx, jy = player->jy;
14871 if (!player->active)
14875 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14876 player->killed, player->active, player->reanimated);
14879 /* the following code was introduced to prevent an infinite loop when calling
14881 -> CheckTriggeredElementChangeExt()
14882 -> ExecuteCustomElementAction()
14884 -> (infinitely repeating the above sequence of function calls)
14885 which occurs when killing the player while having a CE with the setting
14886 "kill player X when explosion of <player X>"; the solution using a new
14887 field "player->killed" was chosen for backwards compatibility, although
14888 clever use of the fields "player->active" etc. would probably also work */
14890 if (player->killed)
14894 player->killed = TRUE;
14896 /* remove accessible field at the player's position */
14897 Feld[jx][jy] = EL_EMPTY;
14899 /* deactivate shield (else Bang()/Explode() would not work right) */
14900 player->shield_normal_time_left = 0;
14901 player->shield_deadly_time_left = 0;
14904 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14905 player->killed, player->active, player->reanimated);
14911 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14912 player->killed, player->active, player->reanimated);
14915 #if USE_PLAYER_REANIMATION
14917 if (player->reanimated) /* killed player may have been reanimated */
14918 player->killed = player->reanimated = FALSE;
14920 BuryPlayer(player);
14922 if (player->killed) /* player may have been reanimated */
14923 BuryPlayer(player);
14926 BuryPlayer(player);
14930 static void KillPlayerUnlessEnemyProtected(int x, int y)
14932 if (!PLAYER_ENEMY_PROTECTED(x, y))
14933 KillPlayer(PLAYERINFO(x, y));
14936 static void KillPlayerUnlessExplosionProtected(int x, int y)
14938 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14939 KillPlayer(PLAYERINFO(x, y));
14942 void BuryPlayer(struct PlayerInfo *player)
14944 int jx = player->jx, jy = player->jy;
14946 if (!player->active)
14949 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14950 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14952 player->GameOver = TRUE;
14953 RemovePlayer(player);
14956 void RemovePlayer(struct PlayerInfo *player)
14958 int jx = player->jx, jy = player->jy;
14959 int i, found = FALSE;
14961 player->present = FALSE;
14962 player->active = FALSE;
14964 if (!ExplodeField[jx][jy])
14965 StorePlayer[jx][jy] = 0;
14967 if (player->is_moving)
14968 TEST_DrawLevelField(player->last_jx, player->last_jy);
14970 for (i = 0; i < MAX_PLAYERS; i++)
14971 if (stored_player[i].active)
14975 AllPlayersGone = TRUE;
14981 #if USE_NEW_SNAP_DELAY
14982 static void setFieldForSnapping(int x, int y, int element, int direction)
14984 struct ElementInfo *ei = &element_info[element];
14985 int direction_bit = MV_DIR_TO_BIT(direction);
14986 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14987 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14988 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14990 Feld[x][y] = EL_ELEMENT_SNAPPING;
14991 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14993 ResetGfxAnimation(x, y);
14995 GfxElement[x][y] = element;
14996 GfxAction[x][y] = action;
14997 GfxDir[x][y] = direction;
14998 GfxFrame[x][y] = -1;
15003 =============================================================================
15004 checkDiagonalPushing()
15005 -----------------------------------------------------------------------------
15006 check if diagonal input device direction results in pushing of object
15007 (by checking if the alternative direction is walkable, diggable, ...)
15008 =============================================================================
15011 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15012 int x, int y, int real_dx, int real_dy)
15014 int jx, jy, dx, dy, xx, yy;
15016 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
15019 /* diagonal direction: check alternative direction */
15024 xx = jx + (dx == 0 ? real_dx : 0);
15025 yy = jy + (dy == 0 ? real_dy : 0);
15027 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15031 =============================================================================
15033 -----------------------------------------------------------------------------
15034 x, y: field next to player (non-diagonal) to try to dig to
15035 real_dx, real_dy: direction as read from input device (can be diagonal)
15036 =============================================================================
15039 static int DigField(struct PlayerInfo *player,
15040 int oldx, int oldy, int x, int y,
15041 int real_dx, int real_dy, int mode)
15043 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15044 boolean player_was_pushing = player->is_pushing;
15045 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15046 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15047 int jx = oldx, jy = oldy;
15048 int dx = x - jx, dy = y - jy;
15049 int nextx = x + dx, nexty = y + dy;
15050 int move_direction = (dx == -1 ? MV_LEFT :
15051 dx == +1 ? MV_RIGHT :
15053 dy == +1 ? MV_DOWN : MV_NONE);
15054 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15055 int dig_side = MV_DIR_OPPOSITE(move_direction);
15056 int old_element = Feld[jx][jy];
15057 #if USE_FIXED_DONT_RUN_INTO
15058 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15064 if (is_player) /* function can also be called by EL_PENGUIN */
15066 if (player->MovPos == 0)
15068 player->is_digging = FALSE;
15069 player->is_collecting = FALSE;
15072 if (player->MovPos == 0) /* last pushing move finished */
15073 player->is_pushing = FALSE;
15075 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15077 player->is_switching = FALSE;
15078 player->push_delay = -1;
15080 return MP_NO_ACTION;
15084 #if !USE_FIXED_DONT_RUN_INTO
15085 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15086 return MP_NO_ACTION;
15089 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15090 old_element = Back[jx][jy];
15092 /* in case of element dropped at player position, check background */
15093 else if (Back[jx][jy] != EL_EMPTY &&
15094 game.engine_version >= VERSION_IDENT(2,2,0,0))
15095 old_element = Back[jx][jy];
15097 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15098 return MP_NO_ACTION; /* field has no opening in this direction */
15100 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15101 return MP_NO_ACTION; /* field has no opening in this direction */
15103 #if USE_FIXED_DONT_RUN_INTO
15104 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15108 Feld[jx][jy] = player->artwork_element;
15109 InitMovingField(jx, jy, MV_DOWN);
15110 Store[jx][jy] = EL_ACID;
15111 ContinueMoving(jx, jy);
15112 BuryPlayer(player);
15114 return MP_DONT_RUN_INTO;
15118 #if USE_FIXED_DONT_RUN_INTO
15119 if (player_can_move && DONT_RUN_INTO(element))
15121 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15123 return MP_DONT_RUN_INTO;
15127 #if USE_FIXED_DONT_RUN_INTO
15128 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15129 return MP_NO_ACTION;
15132 #if !USE_FIXED_DONT_RUN_INTO
15133 element = Feld[x][y];
15136 collect_count = element_info[element].collect_count_initial;
15138 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15139 return MP_NO_ACTION;
15141 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15142 player_can_move = player_can_move_or_snap;
15144 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15145 game.engine_version >= VERSION_IDENT(2,2,0,0))
15147 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15148 player->index_bit, dig_side);
15149 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15150 player->index_bit, dig_side);
15152 if (element == EL_DC_LANDMINE)
15155 if (Feld[x][y] != element) /* field changed by snapping */
15158 return MP_NO_ACTION;
15161 #if USE_PLAYER_GRAVITY
15162 if (player->gravity && is_player && !player->is_auto_moving &&
15163 canFallDown(player) && move_direction != MV_DOWN &&
15164 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15165 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15167 if (game.gravity && is_player && !player->is_auto_moving &&
15168 canFallDown(player) && move_direction != MV_DOWN &&
15169 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15170 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15173 if (player_can_move &&
15174 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15176 int sound_element = SND_ELEMENT(element);
15177 int sound_action = ACTION_WALKING;
15179 if (IS_RND_GATE(element))
15181 if (!player->key[RND_GATE_NR(element)])
15182 return MP_NO_ACTION;
15184 else if (IS_RND_GATE_GRAY(element))
15186 if (!player->key[RND_GATE_GRAY_NR(element)])
15187 return MP_NO_ACTION;
15189 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15191 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15192 return MP_NO_ACTION;
15194 else if (element == EL_EXIT_OPEN ||
15195 element == EL_EM_EXIT_OPEN ||
15197 element == EL_EM_EXIT_OPENING ||
15199 element == EL_STEEL_EXIT_OPEN ||
15200 element == EL_EM_STEEL_EXIT_OPEN ||
15202 element == EL_EM_STEEL_EXIT_OPENING ||
15204 element == EL_SP_EXIT_OPEN ||
15205 element == EL_SP_EXIT_OPENING)
15207 sound_action = ACTION_PASSING; /* player is passing exit */
15209 else if (element == EL_EMPTY)
15211 sound_action = ACTION_MOVING; /* nothing to walk on */
15214 /* play sound from background or player, whatever is available */
15215 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15216 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15218 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15220 else if (player_can_move &&
15221 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15223 if (!ACCESS_FROM(element, opposite_direction))
15224 return MP_NO_ACTION; /* field not accessible from this direction */
15226 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15227 return MP_NO_ACTION;
15229 if (IS_EM_GATE(element))
15231 if (!player->key[EM_GATE_NR(element)])
15232 return MP_NO_ACTION;
15234 else if (IS_EM_GATE_GRAY(element))
15236 if (!player->key[EM_GATE_GRAY_NR(element)])
15237 return MP_NO_ACTION;
15239 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15241 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15242 return MP_NO_ACTION;
15244 else if (IS_EMC_GATE(element))
15246 if (!player->key[EMC_GATE_NR(element)])
15247 return MP_NO_ACTION;
15249 else if (IS_EMC_GATE_GRAY(element))
15251 if (!player->key[EMC_GATE_GRAY_NR(element)])
15252 return MP_NO_ACTION;
15254 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15256 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15257 return MP_NO_ACTION;
15259 else if (element == EL_DC_GATE_WHITE ||
15260 element == EL_DC_GATE_WHITE_GRAY ||
15261 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15263 if (player->num_white_keys == 0)
15264 return MP_NO_ACTION;
15266 player->num_white_keys--;
15268 else if (IS_SP_PORT(element))
15270 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15271 element == EL_SP_GRAVITY_PORT_RIGHT ||
15272 element == EL_SP_GRAVITY_PORT_UP ||
15273 element == EL_SP_GRAVITY_PORT_DOWN)
15274 #if USE_PLAYER_GRAVITY
15275 player->gravity = !player->gravity;
15277 game.gravity = !game.gravity;
15279 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15280 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15281 element == EL_SP_GRAVITY_ON_PORT_UP ||
15282 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15283 #if USE_PLAYER_GRAVITY
15284 player->gravity = TRUE;
15286 game.gravity = TRUE;
15288 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15289 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15290 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15291 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15292 #if USE_PLAYER_GRAVITY
15293 player->gravity = FALSE;
15295 game.gravity = FALSE;
15299 /* automatically move to the next field with double speed */
15300 player->programmed_action = move_direction;
15302 if (player->move_delay_reset_counter == 0)
15304 player->move_delay_reset_counter = 2; /* two double speed steps */
15306 DOUBLE_PLAYER_SPEED(player);
15309 PlayLevelSoundAction(x, y, ACTION_PASSING);
15311 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15315 if (mode != DF_SNAP)
15317 GfxElement[x][y] = GFX_ELEMENT(element);
15318 player->is_digging = TRUE;
15321 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15323 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15324 player->index_bit, dig_side);
15326 if (mode == DF_SNAP)
15328 #if USE_NEW_SNAP_DELAY
15329 if (level.block_snap_field)
15330 setFieldForSnapping(x, y, element, move_direction);
15332 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15334 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15337 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15338 player->index_bit, dig_side);
15341 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15345 if (is_player && mode != DF_SNAP)
15347 GfxElement[x][y] = element;
15348 player->is_collecting = TRUE;
15351 if (element == EL_SPEED_PILL)
15353 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15355 else if (element == EL_EXTRA_TIME && level.time > 0)
15357 TimeLeft += level.extra_time;
15360 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15362 DisplayGameControlValues();
15364 DrawGameValue_Time(TimeLeft);
15367 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15369 player->shield_normal_time_left += level.shield_normal_time;
15370 if (element == EL_SHIELD_DEADLY)
15371 player->shield_deadly_time_left += level.shield_deadly_time;
15373 else if (element == EL_DYNAMITE ||
15374 element == EL_EM_DYNAMITE ||
15375 element == EL_SP_DISK_RED)
15377 if (player->inventory_size < MAX_INVENTORY_SIZE)
15378 player->inventory_element[player->inventory_size++] = element;
15380 DrawGameDoorValues();
15382 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15384 player->dynabomb_count++;
15385 player->dynabombs_left++;
15387 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15389 player->dynabomb_size++;
15391 else if (element == EL_DYNABOMB_INCREASE_POWER)
15393 player->dynabomb_xl = TRUE;
15395 else if (IS_KEY(element))
15397 player->key[KEY_NR(element)] = TRUE;
15399 DrawGameDoorValues();
15401 else if (element == EL_DC_KEY_WHITE)
15403 player->num_white_keys++;
15405 /* display white keys? */
15406 /* DrawGameDoorValues(); */
15408 else if (IS_ENVELOPE(element))
15410 player->show_envelope = element;
15412 else if (element == EL_EMC_LENSES)
15414 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15416 RedrawAllInvisibleElementsForLenses();
15418 else if (element == EL_EMC_MAGNIFIER)
15420 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15422 RedrawAllInvisibleElementsForMagnifier();
15424 else if (IS_DROPPABLE(element) ||
15425 IS_THROWABLE(element)) /* can be collected and dropped */
15429 if (collect_count == 0)
15430 player->inventory_infinite_element = element;
15432 for (i = 0; i < collect_count; i++)
15433 if (player->inventory_size < MAX_INVENTORY_SIZE)
15434 player->inventory_element[player->inventory_size++] = element;
15436 DrawGameDoorValues();
15438 else if (collect_count > 0)
15440 local_player->gems_still_needed -= collect_count;
15441 if (local_player->gems_still_needed < 0)
15442 local_player->gems_still_needed = 0;
15445 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15447 DisplayGameControlValues();
15449 DrawGameValue_Emeralds(local_player->gems_still_needed);
15453 RaiseScoreElement(element);
15454 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15457 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15458 player->index_bit, dig_side);
15460 if (mode == DF_SNAP)
15462 #if USE_NEW_SNAP_DELAY
15463 if (level.block_snap_field)
15464 setFieldForSnapping(x, y, element, move_direction);
15466 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15468 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15471 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15472 player->index_bit, dig_side);
15475 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15477 if (mode == DF_SNAP && element != EL_BD_ROCK)
15478 return MP_NO_ACTION;
15480 if (CAN_FALL(element) && dy)
15481 return MP_NO_ACTION;
15483 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15484 !(element == EL_SPRING && level.use_spring_bug))
15485 return MP_NO_ACTION;
15487 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15488 ((move_direction & MV_VERTICAL &&
15489 ((element_info[element].move_pattern & MV_LEFT &&
15490 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15491 (element_info[element].move_pattern & MV_RIGHT &&
15492 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15493 (move_direction & MV_HORIZONTAL &&
15494 ((element_info[element].move_pattern & MV_UP &&
15495 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15496 (element_info[element].move_pattern & MV_DOWN &&
15497 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15498 return MP_NO_ACTION;
15500 /* do not push elements already moving away faster than player */
15501 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15502 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15503 return MP_NO_ACTION;
15505 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15507 if (player->push_delay_value == -1 || !player_was_pushing)
15508 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15510 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15512 if (player->push_delay_value == -1)
15513 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15515 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15517 if (!player->is_pushing)
15518 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15521 player->is_pushing = TRUE;
15522 player->is_active = TRUE;
15524 if (!(IN_LEV_FIELD(nextx, nexty) &&
15525 (IS_FREE(nextx, nexty) ||
15526 (IS_SB_ELEMENT(element) &&
15527 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15528 (IS_CUSTOM_ELEMENT(element) &&
15529 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15530 return MP_NO_ACTION;
15532 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15533 return MP_NO_ACTION;
15535 if (player->push_delay == -1) /* new pushing; restart delay */
15536 player->push_delay = 0;
15538 if (player->push_delay < player->push_delay_value &&
15539 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15540 element != EL_SPRING && element != EL_BALLOON)
15542 /* make sure that there is no move delay before next try to push */
15543 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15544 player->move_delay = 0;
15546 return MP_NO_ACTION;
15549 if (IS_CUSTOM_ELEMENT(element) &&
15550 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15552 if (!DigFieldByCE(nextx, nexty, element))
15553 return MP_NO_ACTION;
15556 if (IS_SB_ELEMENT(element))
15558 if (element == EL_SOKOBAN_FIELD_FULL)
15560 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15561 local_player->sokobanfields_still_needed++;
15564 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15566 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15567 local_player->sokobanfields_still_needed--;
15570 Feld[x][y] = EL_SOKOBAN_OBJECT;
15572 if (Back[x][y] == Back[nextx][nexty])
15573 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15574 else if (Back[x][y] != 0)
15575 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15578 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15582 if (local_player->sokobanfields_still_needed == 0 &&
15583 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15585 if (local_player->sokobanfields_still_needed == 0 &&
15586 game.emulation == EMU_SOKOBAN)
15589 PlayerWins(player);
15591 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15595 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15597 InitMovingField(x, y, move_direction);
15598 GfxAction[x][y] = ACTION_PUSHING;
15600 if (mode == DF_SNAP)
15601 ContinueMoving(x, y);
15603 MovPos[x][y] = (dx != 0 ? dx : dy);
15605 Pushed[x][y] = TRUE;
15606 Pushed[nextx][nexty] = TRUE;
15608 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15609 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15611 player->push_delay_value = -1; /* get new value later */
15613 /* check for element change _after_ element has been pushed */
15614 if (game.use_change_when_pushing_bug)
15616 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15617 player->index_bit, dig_side);
15618 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15619 player->index_bit, dig_side);
15622 else if (IS_SWITCHABLE(element))
15624 if (PLAYER_SWITCHING(player, x, y))
15626 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15627 player->index_bit, dig_side);
15632 player->is_switching = TRUE;
15633 player->switch_x = x;
15634 player->switch_y = y;
15636 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15638 if (element == EL_ROBOT_WHEEL)
15640 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15644 game.robot_wheel_active = TRUE;
15646 TEST_DrawLevelField(x, y);
15648 else if (element == EL_SP_TERMINAL)
15652 SCAN_PLAYFIELD(xx, yy)
15654 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15656 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15657 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15660 else if (IS_BELT_SWITCH(element))
15662 ToggleBeltSwitch(x, y);
15664 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15665 element == EL_SWITCHGATE_SWITCH_DOWN ||
15666 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15667 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15669 ToggleSwitchgateSwitch(x, y);
15671 else if (element == EL_LIGHT_SWITCH ||
15672 element == EL_LIGHT_SWITCH_ACTIVE)
15674 ToggleLightSwitch(x, y);
15676 else if (element == EL_TIMEGATE_SWITCH ||
15677 element == EL_DC_TIMEGATE_SWITCH)
15679 ActivateTimegateSwitch(x, y);
15681 else if (element == EL_BALLOON_SWITCH_LEFT ||
15682 element == EL_BALLOON_SWITCH_RIGHT ||
15683 element == EL_BALLOON_SWITCH_UP ||
15684 element == EL_BALLOON_SWITCH_DOWN ||
15685 element == EL_BALLOON_SWITCH_NONE ||
15686 element == EL_BALLOON_SWITCH_ANY)
15688 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15689 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15690 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15691 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15692 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15695 else if (element == EL_LAMP)
15697 Feld[x][y] = EL_LAMP_ACTIVE;
15698 local_player->lights_still_needed--;
15700 ResetGfxAnimation(x, y);
15701 TEST_DrawLevelField(x, y);
15703 else if (element == EL_TIME_ORB_FULL)
15705 Feld[x][y] = EL_TIME_ORB_EMPTY;
15707 if (level.time > 0 || level.use_time_orb_bug)
15709 TimeLeft += level.time_orb_time;
15710 game.no_time_limit = FALSE;
15713 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15715 DisplayGameControlValues();
15717 DrawGameValue_Time(TimeLeft);
15721 ResetGfxAnimation(x, y);
15722 TEST_DrawLevelField(x, y);
15724 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15725 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15729 game.ball_state = !game.ball_state;
15731 SCAN_PLAYFIELD(xx, yy)
15733 int e = Feld[xx][yy];
15735 if (game.ball_state)
15737 if (e == EL_EMC_MAGIC_BALL)
15738 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15739 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15740 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15744 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15745 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15746 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15747 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15752 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15753 player->index_bit, dig_side);
15755 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15756 player->index_bit, dig_side);
15758 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15759 player->index_bit, dig_side);
15765 if (!PLAYER_SWITCHING(player, x, y))
15767 player->is_switching = TRUE;
15768 player->switch_x = x;
15769 player->switch_y = y;
15771 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15772 player->index_bit, dig_side);
15773 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15774 player->index_bit, dig_side);
15776 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15777 player->index_bit, dig_side);
15778 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15779 player->index_bit, dig_side);
15782 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15783 player->index_bit, dig_side);
15784 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15785 player->index_bit, dig_side);
15787 return MP_NO_ACTION;
15790 player->push_delay = -1;
15792 if (is_player) /* function can also be called by EL_PENGUIN */
15794 if (Feld[x][y] != element) /* really digged/collected something */
15796 player->is_collecting = !player->is_digging;
15797 player->is_active = TRUE;
15804 static boolean DigFieldByCE(int x, int y, int digging_element)
15806 int element = Feld[x][y];
15808 if (!IS_FREE(x, y))
15810 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15811 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15814 /* no element can dig solid indestructible elements */
15815 if (IS_INDESTRUCTIBLE(element) &&
15816 !IS_DIGGABLE(element) &&
15817 !IS_COLLECTIBLE(element))
15820 if (AmoebaNr[x][y] &&
15821 (element == EL_AMOEBA_FULL ||
15822 element == EL_BD_AMOEBA ||
15823 element == EL_AMOEBA_GROWING))
15825 AmoebaCnt[AmoebaNr[x][y]]--;
15826 AmoebaCnt2[AmoebaNr[x][y]]--;
15829 if (IS_MOVING(x, y))
15830 RemoveMovingField(x, y);
15834 TEST_DrawLevelField(x, y);
15837 /* if digged element was about to explode, prevent the explosion */
15838 ExplodeField[x][y] = EX_TYPE_NONE;
15840 PlayLevelSoundAction(x, y, action);
15843 Store[x][y] = EL_EMPTY;
15846 /* this makes it possible to leave the removed element again */
15847 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15848 Store[x][y] = element;
15850 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15852 int move_leave_element = element_info[digging_element].move_leave_element;
15854 /* this makes it possible to leave the removed element again */
15855 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15856 element : move_leave_element);
15863 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15865 int jx = player->jx, jy = player->jy;
15866 int x = jx + dx, y = jy + dy;
15867 int snap_direction = (dx == -1 ? MV_LEFT :
15868 dx == +1 ? MV_RIGHT :
15870 dy == +1 ? MV_DOWN : MV_NONE);
15871 boolean can_continue_snapping = (level.continuous_snapping &&
15872 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15874 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15877 if (!player->active || !IN_LEV_FIELD(x, y))
15885 if (player->MovPos == 0)
15886 player->is_pushing = FALSE;
15888 player->is_snapping = FALSE;
15890 if (player->MovPos == 0)
15892 player->is_moving = FALSE;
15893 player->is_digging = FALSE;
15894 player->is_collecting = FALSE;
15900 #if USE_NEW_CONTINUOUS_SNAPPING
15901 /* prevent snapping with already pressed snap key when not allowed */
15902 if (player->is_snapping && !can_continue_snapping)
15905 if (player->is_snapping)
15909 player->MovDir = snap_direction;
15911 if (player->MovPos == 0)
15913 player->is_moving = FALSE;
15914 player->is_digging = FALSE;
15915 player->is_collecting = FALSE;
15918 player->is_dropping = FALSE;
15919 player->is_dropping_pressed = FALSE;
15920 player->drop_pressed_delay = 0;
15922 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15925 player->is_snapping = TRUE;
15926 player->is_active = TRUE;
15928 if (player->MovPos == 0)
15930 player->is_moving = FALSE;
15931 player->is_digging = FALSE;
15932 player->is_collecting = FALSE;
15935 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15936 TEST_DrawLevelField(player->last_jx, player->last_jy);
15938 TEST_DrawLevelField(x, y);
15943 static boolean DropElement(struct PlayerInfo *player)
15945 int old_element, new_element;
15946 int dropx = player->jx, dropy = player->jy;
15947 int drop_direction = player->MovDir;
15948 int drop_side = drop_direction;
15950 int drop_element = get_next_dropped_element(player);
15952 int drop_element = (player->inventory_size > 0 ?
15953 player->inventory_element[player->inventory_size - 1] :
15954 player->inventory_infinite_element != EL_UNDEFINED ?
15955 player->inventory_infinite_element :
15956 player->dynabombs_left > 0 ?
15957 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15961 player->is_dropping_pressed = TRUE;
15963 /* do not drop an element on top of another element; when holding drop key
15964 pressed without moving, dropped element must move away before the next
15965 element can be dropped (this is especially important if the next element
15966 is dynamite, which can be placed on background for historical reasons) */
15967 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15970 if (IS_THROWABLE(drop_element))
15972 dropx += GET_DX_FROM_DIR(drop_direction);
15973 dropy += GET_DY_FROM_DIR(drop_direction);
15975 if (!IN_LEV_FIELD(dropx, dropy))
15979 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15980 new_element = drop_element; /* default: no change when dropping */
15982 /* check if player is active, not moving and ready to drop */
15983 if (!player->active || player->MovPos || player->drop_delay > 0)
15986 /* check if player has anything that can be dropped */
15987 if (new_element == EL_UNDEFINED)
15990 /* check if drop key was pressed long enough for EM style dynamite */
15991 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15994 /* check if anything can be dropped at the current position */
15995 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15998 /* collected custom elements can only be dropped on empty fields */
15999 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16002 if (old_element != EL_EMPTY)
16003 Back[dropx][dropy] = old_element; /* store old element on this field */
16005 ResetGfxAnimation(dropx, dropy);
16006 ResetRandomAnimationValue(dropx, dropy);
16008 if (player->inventory_size > 0 ||
16009 player->inventory_infinite_element != EL_UNDEFINED)
16011 if (player->inventory_size > 0)
16013 player->inventory_size--;
16015 DrawGameDoorValues();
16017 if (new_element == EL_DYNAMITE)
16018 new_element = EL_DYNAMITE_ACTIVE;
16019 else if (new_element == EL_EM_DYNAMITE)
16020 new_element = EL_EM_DYNAMITE_ACTIVE;
16021 else if (new_element == EL_SP_DISK_RED)
16022 new_element = EL_SP_DISK_RED_ACTIVE;
16025 Feld[dropx][dropy] = new_element;
16027 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16028 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16029 el2img(Feld[dropx][dropy]), 0);
16031 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16033 /* needed if previous element just changed to "empty" in the last frame */
16034 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16036 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16037 player->index_bit, drop_side);
16038 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16040 player->index_bit, drop_side);
16042 TestIfElementTouchesCustomElement(dropx, dropy);
16044 else /* player is dropping a dyna bomb */
16046 player->dynabombs_left--;
16048 Feld[dropx][dropy] = new_element;
16050 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16051 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16052 el2img(Feld[dropx][dropy]), 0);
16054 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16057 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16058 InitField_WithBug1(dropx, dropy, FALSE);
16060 new_element = Feld[dropx][dropy]; /* element might have changed */
16062 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16063 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16066 int move_direction;
16070 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16071 MovDir[dropx][dropy] = drop_direction;
16074 move_direction = MovDir[dropx][dropy];
16075 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16076 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16079 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16081 #if USE_FIX_IMPACT_COLLISION
16082 /* do not cause impact style collision by dropping elements that can fall */
16083 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16085 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16089 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16090 player->is_dropping = TRUE;
16092 player->drop_pressed_delay = 0;
16093 player->is_dropping_pressed = FALSE;
16095 player->drop_x = dropx;
16096 player->drop_y = dropy;
16101 /* ------------------------------------------------------------------------- */
16102 /* game sound playing functions */
16103 /* ------------------------------------------------------------------------- */
16105 static int *loop_sound_frame = NULL;
16106 static int *loop_sound_volume = NULL;
16108 void InitPlayLevelSound()
16110 int num_sounds = getSoundListSize();
16112 checked_free(loop_sound_frame);
16113 checked_free(loop_sound_volume);
16115 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16116 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16119 static void PlayLevelSound(int x, int y, int nr)
16121 int sx = SCREENX(x), sy = SCREENY(y);
16122 int volume, stereo_position;
16123 int max_distance = 8;
16124 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16126 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16127 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16130 if (!IN_LEV_FIELD(x, y) ||
16131 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16132 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16135 volume = SOUND_MAX_VOLUME;
16137 if (!IN_SCR_FIELD(sx, sy))
16139 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16140 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16142 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16145 stereo_position = (SOUND_MAX_LEFT +
16146 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16147 (SCR_FIELDX + 2 * max_distance));
16149 if (IS_LOOP_SOUND(nr))
16151 /* This assures that quieter loop sounds do not overwrite louder ones,
16152 while restarting sound volume comparison with each new game frame. */
16154 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16157 loop_sound_volume[nr] = volume;
16158 loop_sound_frame[nr] = FrameCounter;
16161 PlaySoundExt(nr, volume, stereo_position, type);
16164 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16166 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16167 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16168 y < LEVELY(BY1) ? LEVELY(BY1) :
16169 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16173 static void PlayLevelSoundAction(int x, int y, int action)
16175 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16178 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16180 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16182 if (sound_effect != SND_UNDEFINED)
16183 PlayLevelSound(x, y, sound_effect);
16186 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16189 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16191 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16192 PlayLevelSound(x, y, sound_effect);
16195 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16197 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16199 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16200 PlayLevelSound(x, y, sound_effect);
16203 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16205 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16207 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16208 StopSound(sound_effect);
16211 static void PlayLevelMusic()
16213 if (levelset.music[level_nr] != MUS_UNDEFINED)
16214 PlayMusic(levelset.music[level_nr]); /* from config file */
16216 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16219 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16221 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16222 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16223 int x = xx - 1 - offset;
16224 int y = yy - 1 - offset;
16229 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16233 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16237 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16241 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16245 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16249 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16253 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16256 case SAMPLE_android_clone:
16257 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16260 case SAMPLE_android_move:
16261 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16264 case SAMPLE_spring:
16265 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16269 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16273 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16276 case SAMPLE_eater_eat:
16277 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16281 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16284 case SAMPLE_collect:
16285 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16288 case SAMPLE_diamond:
16289 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16292 case SAMPLE_squash:
16293 /* !!! CHECK THIS !!! */
16295 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16297 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16301 case SAMPLE_wonderfall:
16302 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16306 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16310 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16314 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16318 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16322 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16326 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16329 case SAMPLE_wonder:
16330 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16334 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16337 case SAMPLE_exit_open:
16338 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16341 case SAMPLE_exit_leave:
16342 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16345 case SAMPLE_dynamite:
16346 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16350 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16354 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16358 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16362 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16366 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16370 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16374 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16379 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16381 int element = map_element_SP_to_RND(element_sp);
16382 int action = map_action_SP_to_RND(action_sp);
16383 int offset = (setup.sp_show_border_elements ? 0 : 1);
16384 int x = xx - offset;
16385 int y = yy - offset;
16388 printf("::: %d -> %d\n", element_sp, action_sp);
16391 PlayLevelSoundElementAction(x, y, element, action);
16394 void RaiseScore(int value)
16396 local_player->score += value;
16399 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16401 DisplayGameControlValues();
16403 DrawGameValue_Score(local_player->score);
16407 void RaiseScoreElement(int element)
16412 case EL_BD_DIAMOND:
16413 case EL_EMERALD_YELLOW:
16414 case EL_EMERALD_RED:
16415 case EL_EMERALD_PURPLE:
16416 case EL_SP_INFOTRON:
16417 RaiseScore(level.score[SC_EMERALD]);
16420 RaiseScore(level.score[SC_DIAMOND]);
16423 RaiseScore(level.score[SC_CRYSTAL]);
16426 RaiseScore(level.score[SC_PEARL]);
16429 case EL_BD_BUTTERFLY:
16430 case EL_SP_ELECTRON:
16431 RaiseScore(level.score[SC_BUG]);
16434 case EL_BD_FIREFLY:
16435 case EL_SP_SNIKSNAK:
16436 RaiseScore(level.score[SC_SPACESHIP]);
16439 case EL_DARK_YAMYAM:
16440 RaiseScore(level.score[SC_YAMYAM]);
16443 RaiseScore(level.score[SC_ROBOT]);
16446 RaiseScore(level.score[SC_PACMAN]);
16449 RaiseScore(level.score[SC_NUT]);
16452 case EL_EM_DYNAMITE:
16453 case EL_SP_DISK_RED:
16454 case EL_DYNABOMB_INCREASE_NUMBER:
16455 case EL_DYNABOMB_INCREASE_SIZE:
16456 case EL_DYNABOMB_INCREASE_POWER:
16457 RaiseScore(level.score[SC_DYNAMITE]);
16459 case EL_SHIELD_NORMAL:
16460 case EL_SHIELD_DEADLY:
16461 RaiseScore(level.score[SC_SHIELD]);
16463 case EL_EXTRA_TIME:
16464 RaiseScore(level.extra_time_score);
16478 case EL_DC_KEY_WHITE:
16479 RaiseScore(level.score[SC_KEY]);
16482 RaiseScore(element_info[element].collect_score);
16487 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16489 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16492 /* closing door required in case of envelope style request dialogs */
16494 CloseDoor(DOOR_CLOSE_1);
16497 #if defined(NETWORK_AVALIABLE)
16498 if (options.network)
16499 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16508 FadeSkipNextFadeIn();
16510 fading = fading_none;
16514 OpenDoor(DOOR_CLOSE_1);
16517 game_status = GAME_MODE_MAIN;
16520 DrawAndFadeInMainMenu(REDRAW_FIELD);
16528 FadeOut(REDRAW_FIELD);
16531 game_status = GAME_MODE_MAIN;
16533 DrawAndFadeInMainMenu(REDRAW_FIELD);
16537 else /* continue playing the game */
16539 if (tape.playing && tape.deactivate_display)
16540 TapeDeactivateDisplayOff(TRUE);
16542 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16544 if (tape.playing && tape.deactivate_display)
16545 TapeDeactivateDisplayOn();
16549 void RequestQuitGame(boolean ask_if_really_quit)
16551 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16552 boolean skip_request = AllPlayersGone || quick_quit;
16554 RequestQuitGameExt(skip_request, quick_quit,
16555 "Do you really want to quit the game?");
16559 /* ------------------------------------------------------------------------- */
16560 /* random generator functions */
16561 /* ------------------------------------------------------------------------- */
16563 unsigned int InitEngineRandom_RND(int seed)
16565 game.num_random_calls = 0;
16568 unsigned int rnd_seed = InitEngineRandom(seed);
16570 printf("::: START RND: %d\n", rnd_seed);
16575 return InitEngineRandom(seed);
16581 unsigned int RND(int max)
16585 game.num_random_calls++;
16587 return GetEngineRandom(max);
16594 /* ------------------------------------------------------------------------- */
16595 /* game engine snapshot handling functions */
16596 /* ------------------------------------------------------------------------- */
16598 struct EngineSnapshotInfo
16600 /* runtime values for custom element collect score */
16601 int collect_score[NUM_CUSTOM_ELEMENTS];
16603 /* runtime values for group element choice position */
16604 int choice_pos[NUM_GROUP_ELEMENTS];
16606 /* runtime values for belt position animations */
16607 int belt_graphic[4][NUM_BELT_PARTS];
16608 int belt_anim_mode[4][NUM_BELT_PARTS];
16611 static struct EngineSnapshotInfo engine_snapshot_rnd;
16612 static char *snapshot_level_identifier = NULL;
16613 static int snapshot_level_nr = -1;
16615 static void SaveEngineSnapshotValues_RND()
16617 static int belt_base_active_element[4] =
16619 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16620 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16621 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16622 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16626 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16628 int element = EL_CUSTOM_START + i;
16630 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16633 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16635 int element = EL_GROUP_START + i;
16637 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16640 for (i = 0; i < 4; i++)
16642 for (j = 0; j < NUM_BELT_PARTS; j++)
16644 int element = belt_base_active_element[i] + j;
16645 int graphic = el2img(element);
16646 int anim_mode = graphic_info[graphic].anim_mode;
16648 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16649 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16654 static void LoadEngineSnapshotValues_RND()
16656 unsigned int num_random_calls = game.num_random_calls;
16659 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16661 int element = EL_CUSTOM_START + i;
16663 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16666 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16668 int element = EL_GROUP_START + i;
16670 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16673 for (i = 0; i < 4; i++)
16675 for (j = 0; j < NUM_BELT_PARTS; j++)
16677 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16678 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16680 graphic_info[graphic].anim_mode = anim_mode;
16684 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16686 InitRND(tape.random_seed);
16687 for (i = 0; i < num_random_calls; i++)
16691 if (game.num_random_calls != num_random_calls)
16693 Error(ERR_INFO, "number of random calls out of sync");
16694 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16695 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16696 Error(ERR_EXIT, "this should not happen -- please debug");
16700 void SaveEngineSnapshot()
16702 /* do not save snapshots from editor */
16703 if (level_editor_test_game)
16706 /* free previous snapshot buffers, if needed */
16707 FreeEngineSnapshotBuffers();
16709 /* copy some special values to a structure better suited for the snapshot */
16711 SaveEngineSnapshotValues_RND();
16712 SaveEngineSnapshotValues_EM();
16713 SaveEngineSnapshotValues_SP();
16715 /* save values stored in special snapshot structure */
16717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16719 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16721 /* save further RND engine values */
16723 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16749 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16752 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16754 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16755 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16756 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16759 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16764 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16766 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16768 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16769 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16771 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16772 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16773 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16775 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16776 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16778 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16779 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16780 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16781 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16782 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16784 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16785 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16787 /* save level identification information */
16789 setString(&snapshot_level_identifier, leveldir_current->identifier);
16790 snapshot_level_nr = level_nr;
16793 ListNode *node = engine_snapshot_list_rnd;
16796 while (node != NULL)
16798 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16803 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16807 void LoadEngineSnapshot()
16809 /* restore generically stored snapshot buffers */
16811 LoadEngineSnapshotBuffers();
16813 /* restore special values from snapshot structure */
16815 LoadEngineSnapshotValues_RND();
16816 LoadEngineSnapshotValues_EM();
16817 LoadEngineSnapshotValues_SP();
16820 boolean CheckEngineSnapshot()
16822 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16823 snapshot_level_nr == level_nr);
16827 /* ---------- new game button stuff ---------------------------------------- */
16835 } gamebutton_info[NUM_GAME_BUTTONS] =
16838 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16839 GAME_CTRL_ID_STOP, "stop game"
16842 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16843 GAME_CTRL_ID_PAUSE, "pause game"
16846 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16847 GAME_CTRL_ID_PLAY, "play game"
16850 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16851 SOUND_CTRL_ID_MUSIC, "background music on/off"
16854 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16855 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16858 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16859 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16862 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
16863 GAME_CTRL_ID_SAVE, "save game"
16866 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
16867 GAME_CTRL_ID_LOAD, "load game"
16871 void CreateGameButtons()
16875 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16877 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16878 struct Rect *pos = gamebutton_info[i].pos;
16879 struct GadgetInfo *gi;
16882 unsigned int event_mask;
16883 int base_x = (tape.show_game_buttons ? VX : DX);
16884 int base_y = (tape.show_game_buttons ? VY : DY);
16885 int gd_x = gfx->src_x;
16886 int gd_y = gfx->src_y;
16887 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16888 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16889 int gd_xa = gfx->src_x + gfx->active_xoffset;
16890 int gd_ya = gfx->src_y + gfx->active_yoffset;
16891 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16892 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16895 if (gfx->bitmap == NULL)
16897 game_gadget[id] = NULL;
16902 if (id == GAME_CTRL_ID_STOP ||
16903 id == GAME_CTRL_ID_PAUSE ||
16904 id == GAME_CTRL_ID_PLAY ||
16905 id == GAME_CTRL_ID_SAVE ||
16906 id == GAME_CTRL_ID_LOAD)
16908 button_type = GD_TYPE_NORMAL_BUTTON;
16910 event_mask = GD_EVENT_RELEASED;
16914 button_type = GD_TYPE_CHECK_BUTTON;
16916 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16917 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16918 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16919 event_mask = GD_EVENT_PRESSED;
16922 gi = CreateGadget(GDI_CUSTOM_ID, id,
16923 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16924 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
16925 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
16926 GDI_WIDTH, gfx->width,
16927 GDI_HEIGHT, gfx->height,
16928 GDI_TYPE, button_type,
16929 GDI_STATE, GD_BUTTON_UNPRESSED,
16930 GDI_CHECKED, checked,
16931 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16932 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16933 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16934 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16935 GDI_DIRECT_DRAW, FALSE,
16936 GDI_EVENT_MASK, event_mask,
16937 GDI_CALLBACK_ACTION, HandleGameButtons,
16941 Error(ERR_EXIT, "cannot create gadget");
16943 game_gadget[id] = gi;
16947 void FreeGameButtons()
16951 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16952 FreeGadget(game_gadget[i]);
16955 void MapGameButtons()
16959 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16960 MapGadget(game_gadget[i]);
16963 void UnmapGameButtons()
16967 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16968 UnmapGadget(game_gadget[i]);
16971 void RedrawGameButtons()
16975 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16976 RedrawGadget(game_gadget[i]);
16979 static void HandleGameButtonsExt(int id)
16981 boolean handle_game_buttons =
16982 (game_status == GAME_MODE_PLAYING ||
16983 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16985 if (!handle_game_buttons)
16990 case GAME_CTRL_ID_STOP:
16991 if (game_status == GAME_MODE_MAIN)
16997 RequestQuitGame(TRUE);
17001 case GAME_CTRL_ID_PAUSE:
17002 if (options.network && game_status == GAME_MODE_PLAYING)
17004 #if defined(NETWORK_AVALIABLE)
17006 SendToServer_ContinuePlaying();
17008 SendToServer_PausePlaying();
17012 TapeTogglePause(TAPE_TOGGLE_MANUAL);
17015 case GAME_CTRL_ID_PLAY:
17016 if (game_status == GAME_MODE_MAIN)
17018 StartGameActions(options.network, setup.autorecord, level.random_seed);
17020 else if (tape.pausing)
17022 #if defined(NETWORK_AVALIABLE)
17023 if (options.network)
17024 SendToServer_ContinuePlaying();
17028 tape.pausing = FALSE;
17029 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17034 case SOUND_CTRL_ID_MUSIC:
17035 if (setup.sound_music)
17037 setup.sound_music = FALSE;
17041 else if (audio.music_available)
17043 setup.sound = setup.sound_music = TRUE;
17045 SetAudioMode(setup.sound);
17051 case SOUND_CTRL_ID_LOOPS:
17052 if (setup.sound_loops)
17053 setup.sound_loops = FALSE;
17054 else if (audio.loops_available)
17056 setup.sound = setup.sound_loops = TRUE;
17058 SetAudioMode(setup.sound);
17062 case SOUND_CTRL_ID_SIMPLE:
17063 if (setup.sound_simple)
17064 setup.sound_simple = FALSE;
17065 else if (audio.sound_available)
17067 setup.sound = setup.sound_simple = TRUE;
17069 SetAudioMode(setup.sound);
17073 case GAME_CTRL_ID_SAVE:
17077 case GAME_CTRL_ID_LOAD:
17086 static void HandleGameButtons(struct GadgetInfo *gi)
17088 HandleGameButtonsExt(gi->custom_id);
17091 void HandleSoundButtonKeys(Key key)
17094 if (key == setup.shortcut.sound_simple)
17095 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17096 else if (key == setup.shortcut.sound_loops)
17097 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17098 else if (key == setup.shortcut.sound_music)
17099 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17101 if (key == setup.shortcut.sound_simple)
17102 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17103 else if (key == setup.shortcut.sound_loops)
17104 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17105 else if (key == setup.shortcut.sound_music)
17106 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);