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 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3524 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3526 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3527 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3528 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3530 boolean do_fading = (game_status == GAME_MODE_MAIN);
3533 int initial_move_dir = MV_DOWN;
3535 int initial_move_dir = MV_NONE;
3540 game_status = GAME_MODE_PLAYING;
3547 if (!game.restart_level)
3548 CloseDoor(DOOR_CLOSE_1);
3551 if (level_editor_test_game)
3552 FadeSkipNextFadeIn();
3554 FadeSetEnterScreen();
3556 if (level_editor_test_game)
3557 fading = fading_none;
3559 fading = menu.destination;
3563 FadeOut(REDRAW_FIELD);
3566 FadeOut(REDRAW_FIELD);
3572 printf("::: FADING OUT: DONE\n");
3577 game_status = GAME_MODE_PLAYING;
3581 /* needed if different viewport properties defined for playing */
3582 ChangeViewportPropertiesIfNeeded();
3586 DrawCompleteVideoDisplay();
3590 InitGameControlValues();
3592 /* don't play tapes over network */
3593 network_playing = (options.network && !tape.playing);
3595 for (i = 0; i < MAX_PLAYERS; i++)
3597 struct PlayerInfo *player = &stored_player[i];
3599 player->index_nr = i;
3600 player->index_bit = (1 << i);
3601 player->element_nr = EL_PLAYER_1 + i;
3603 player->present = FALSE;
3604 player->active = FALSE;
3605 player->mapped = FALSE;
3607 player->killed = FALSE;
3608 player->reanimated = FALSE;
3611 player->effective_action = 0;
3612 player->programmed_action = 0;
3615 player->score_final = 0;
3617 player->gems_still_needed = level.gems_needed;
3618 player->sokobanfields_still_needed = 0;
3619 player->lights_still_needed = 0;
3620 player->friends_still_needed = 0;
3622 for (j = 0; j < MAX_NUM_KEYS; j++)
3623 player->key[j] = FALSE;
3625 player->num_white_keys = 0;
3627 player->dynabomb_count = 0;
3628 player->dynabomb_size = 1;
3629 player->dynabombs_left = 0;
3630 player->dynabomb_xl = FALSE;
3632 player->MovDir = initial_move_dir;
3635 player->GfxDir = initial_move_dir;
3636 player->GfxAction = ACTION_DEFAULT;
3638 player->StepFrame = 0;
3640 player->initial_element = player->element_nr;
3641 player->artwork_element =
3642 (level.use_artwork_element[i] ? level.artwork_element[i] :
3643 player->element_nr);
3644 player->use_murphy = FALSE;
3646 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3647 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3649 player->gravity = level.initial_player_gravity[i];
3651 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3653 player->actual_frame_counter = 0;
3655 player->step_counter = 0;
3657 player->last_move_dir = initial_move_dir;
3659 player->is_active = FALSE;
3661 player->is_waiting = FALSE;
3662 player->is_moving = FALSE;
3663 player->is_auto_moving = FALSE;
3664 player->is_digging = FALSE;
3665 player->is_snapping = FALSE;
3666 player->is_collecting = FALSE;
3667 player->is_pushing = FALSE;
3668 player->is_switching = FALSE;
3669 player->is_dropping = FALSE;
3670 player->is_dropping_pressed = FALSE;
3672 player->is_bored = FALSE;
3673 player->is_sleeping = FALSE;
3675 player->frame_counter_bored = -1;
3676 player->frame_counter_sleeping = -1;
3678 player->anim_delay_counter = 0;
3679 player->post_delay_counter = 0;
3681 player->dir_waiting = initial_move_dir;
3682 player->action_waiting = ACTION_DEFAULT;
3683 player->last_action_waiting = ACTION_DEFAULT;
3684 player->special_action_bored = ACTION_DEFAULT;
3685 player->special_action_sleeping = ACTION_DEFAULT;
3687 player->switch_x = -1;
3688 player->switch_y = -1;
3690 player->drop_x = -1;
3691 player->drop_y = -1;
3693 player->show_envelope = 0;
3695 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3697 player->push_delay = -1; /* initialized when pushing starts */
3698 player->push_delay_value = game.initial_push_delay_value;
3700 player->drop_delay = 0;
3701 player->drop_pressed_delay = 0;
3703 player->last_jx = -1;
3704 player->last_jy = -1;
3708 player->shield_normal_time_left = 0;
3709 player->shield_deadly_time_left = 0;
3711 player->inventory_infinite_element = EL_UNDEFINED;
3712 player->inventory_size = 0;
3714 if (level.use_initial_inventory[i])
3716 for (j = 0; j < level.initial_inventory_size[i]; j++)
3718 int element = level.initial_inventory_content[i][j];
3719 int collect_count = element_info[element].collect_count_initial;
3722 if (!IS_CUSTOM_ELEMENT(element))
3725 if (collect_count == 0)
3726 player->inventory_infinite_element = element;
3728 for (k = 0; k < collect_count; k++)
3729 if (player->inventory_size < MAX_INVENTORY_SIZE)
3730 player->inventory_element[player->inventory_size++] = element;
3734 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3735 SnapField(player, 0, 0);
3737 player->LevelSolved = FALSE;
3738 player->GameOver = FALSE;
3740 player->LevelSolved_GameWon = FALSE;
3741 player->LevelSolved_GameEnd = FALSE;
3742 player->LevelSolved_PanelOff = FALSE;
3743 player->LevelSolved_SaveTape = FALSE;
3744 player->LevelSolved_SaveScore = FALSE;
3745 player->LevelSolved_CountingTime = 0;
3746 player->LevelSolved_CountingScore = 0;
3748 map_player_action[i] = i;
3751 network_player_action_received = FALSE;
3753 #if defined(NETWORK_AVALIABLE)
3754 /* initial null action */
3755 if (network_playing)
3756 SendToServer_MovePlayer(MV_NONE);
3765 TimeLeft = level.time;
3768 ScreenMovDir = MV_NONE;
3772 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3774 AllPlayersGone = FALSE;
3776 game.no_time_limit = (level.time == 0);
3778 game.yamyam_content_nr = 0;
3779 game.robot_wheel_active = FALSE;
3780 game.magic_wall_active = FALSE;
3781 game.magic_wall_time_left = 0;
3782 game.light_time_left = 0;
3783 game.timegate_time_left = 0;
3784 game.switchgate_pos = 0;
3785 game.wind_direction = level.wind_direction_initial;
3787 #if !USE_PLAYER_GRAVITY
3788 game.gravity = FALSE;
3789 game.explosions_delayed = TRUE;
3792 game.lenses_time_left = 0;
3793 game.magnify_time_left = 0;
3795 game.ball_state = level.ball_state_initial;
3796 game.ball_content_nr = 0;
3798 game.envelope_active = FALSE;
3800 /* set focus to local player for network games, else to all players */
3801 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3802 game.centered_player_nr_next = game.centered_player_nr;
3803 game.set_centered_player = FALSE;
3805 if (network_playing && tape.recording)
3807 /* store client dependent player focus when recording network games */
3808 tape.centered_player_nr_next = game.centered_player_nr_next;
3809 tape.set_centered_player = TRUE;
3812 for (i = 0; i < NUM_BELTS; i++)
3814 game.belt_dir[i] = MV_NONE;
3815 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3818 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3819 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3821 #if DEBUG_INIT_PLAYER
3824 printf("Player status at level initialization:\n");
3828 SCAN_PLAYFIELD(x, y)
3830 Feld[x][y] = level.field[x][y];
3831 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3832 ChangeDelay[x][y] = 0;
3833 ChangePage[x][y] = -1;
3834 #if USE_NEW_CUSTOM_VALUE
3835 CustomValue[x][y] = 0; /* initialized in InitField() */
3837 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3839 WasJustMoving[x][y] = 0;
3840 WasJustFalling[x][y] = 0;
3841 CheckCollision[x][y] = 0;
3842 CheckImpact[x][y] = 0;
3844 Pushed[x][y] = FALSE;
3846 ChangeCount[x][y] = 0;
3847 ChangeEvent[x][y] = -1;
3849 ExplodePhase[x][y] = 0;
3850 ExplodeDelay[x][y] = 0;
3851 ExplodeField[x][y] = EX_TYPE_NONE;
3853 RunnerVisit[x][y] = 0;
3854 PlayerVisit[x][y] = 0;
3857 GfxRandom[x][y] = INIT_GFX_RANDOM();
3858 GfxElement[x][y] = EL_UNDEFINED;
3859 GfxAction[x][y] = ACTION_DEFAULT;
3860 GfxDir[x][y] = MV_NONE;
3861 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3864 SCAN_PLAYFIELD(x, y)
3866 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3868 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3870 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3873 InitField(x, y, TRUE);
3875 ResetGfxAnimation(x, y);
3880 for (i = 0; i < MAX_PLAYERS; i++)
3882 struct PlayerInfo *player = &stored_player[i];
3884 /* set number of special actions for bored and sleeping animation */
3885 player->num_special_action_bored =
3886 get_num_special_action(player->artwork_element,
3887 ACTION_BORING_1, ACTION_BORING_LAST);
3888 player->num_special_action_sleeping =
3889 get_num_special_action(player->artwork_element,
3890 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3893 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3894 emulate_sb ? EMU_SOKOBAN :
3895 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3897 #if USE_NEW_ALL_SLIPPERY
3898 /* initialize type of slippery elements */
3899 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3901 if (!IS_CUSTOM_ELEMENT(i))
3903 /* default: elements slip down either to the left or right randomly */
3904 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3906 /* SP style elements prefer to slip down on the left side */
3907 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3908 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3910 /* BD style elements prefer to slip down on the left side */
3911 if (game.emulation == EMU_BOULDERDASH)
3912 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3917 /* initialize explosion and ignition delay */
3918 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920 if (!IS_CUSTOM_ELEMENT(i))
3923 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3924 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3925 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3926 int last_phase = (num_phase + 1) * delay;
3927 int half_phase = (num_phase / 2) * delay;
3929 element_info[i].explosion_delay = last_phase - 1;
3930 element_info[i].ignition_delay = half_phase;
3932 if (i == EL_BLACK_ORB)
3933 element_info[i].ignition_delay = 1;
3937 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3938 element_info[i].explosion_delay = 1;
3940 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3941 element_info[i].ignition_delay = 1;
3945 /* correct non-moving belts to start moving left */
3946 for (i = 0; i < NUM_BELTS; i++)
3947 if (game.belt_dir[i] == MV_NONE)
3948 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3950 #if USE_NEW_PLAYER_ASSIGNMENTS
3951 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3952 /* choose default local player */
3953 local_player = &stored_player[0];
3955 for (i = 0; i < MAX_PLAYERS; i++)
3956 stored_player[i].connected = FALSE;
3958 local_player->connected = TRUE;
3959 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3962 printf("::: TEAM MODE: %d\n", game.team_mode);
3968 for (i = 0; i < MAX_PLAYERS; i++)
3969 stored_player[i].connected = tape.player_participates[i];
3971 /* try to guess locally connected team mode players (needed for correct
3972 assignment of player figures from level to locally playing players) */
3974 for (i = 0; i < MAX_PLAYERS; i++)
3975 if (tape.player_participates[i])
3976 stored_player[i].connected = TRUE;
3979 else if (game.team_mode && !options.network)
3981 /* try to guess locally connected team mode players (needed for correct
3982 assignment of player figures from level to locally playing players) */
3984 for (i = 0; i < MAX_PLAYERS; i++)
3985 if (setup.input[i].use_joystick ||
3986 setup.input[i].key.left != KSYM_UNDEFINED)
3987 stored_player[i].connected = TRUE;
3990 #if DEBUG_INIT_PLAYER
3993 printf("Player status after level initialization:\n");
3995 for (i = 0; i < MAX_PLAYERS; i++)
3997 struct PlayerInfo *player = &stored_player[i];
3999 printf("- player %d: present == %d, connected == %d, active == %d",
4005 if (local_player == player)
4006 printf(" (local player)");
4013 #if DEBUG_INIT_PLAYER
4015 printf("Reassigning players ...\n");
4018 /* check if any connected player was not found in playfield */
4019 for (i = 0; i < MAX_PLAYERS; i++)
4021 struct PlayerInfo *player = &stored_player[i];
4023 if (player->connected && !player->present)
4025 struct PlayerInfo *field_player = NULL;
4027 #if DEBUG_INIT_PLAYER
4029 printf("- looking for field player for player %d ...\n", i + 1);
4032 /* assign first free player found that is present in the playfield */
4035 /* first try: look for unmapped playfield player that is not connected */
4036 for (j = 0; j < MAX_PLAYERS; j++)
4037 if (field_player == NULL &&
4038 stored_player[j].present &&
4039 !stored_player[j].mapped &&
4040 !stored_player[j].connected)
4041 field_player = &stored_player[j];
4043 /* second try: look for *any* unmapped playfield player */
4044 for (j = 0; j < MAX_PLAYERS; j++)
4045 if (field_player == NULL &&
4046 stored_player[j].present &&
4047 !stored_player[j].mapped)
4048 field_player = &stored_player[j];
4050 /* first try: look for unmapped playfield player that is not connected */
4051 if (field_player == NULL)
4052 for (j = 0; j < MAX_PLAYERS; j++)
4053 if (stored_player[j].present &&
4054 !stored_player[j].mapped &&
4055 !stored_player[j].connected)
4056 field_player = &stored_player[j];
4058 /* second try: look for *any* unmapped playfield player */
4059 if (field_player == NULL)
4060 for (j = 0; j < MAX_PLAYERS; j++)
4061 if (stored_player[j].present &&
4062 !stored_player[j].mapped)
4063 field_player = &stored_player[j];
4066 if (field_player != NULL)
4068 int jx = field_player->jx, jy = field_player->jy;
4070 #if DEBUG_INIT_PLAYER
4072 printf("- found player %d\n", field_player->index_nr + 1);
4075 player->present = FALSE;
4076 player->active = FALSE;
4078 field_player->present = TRUE;
4079 field_player->active = TRUE;
4082 player->initial_element = field_player->initial_element;
4083 player->artwork_element = field_player->artwork_element;
4085 player->block_last_field = field_player->block_last_field;
4086 player->block_delay_adjustment = field_player->block_delay_adjustment;
4089 StorePlayer[jx][jy] = field_player->element_nr;
4091 field_player->jx = field_player->last_jx = jx;
4092 field_player->jy = field_player->last_jy = jy;
4094 if (local_player == player)
4095 local_player = field_player;
4097 map_player_action[field_player->index_nr] = i;
4099 field_player->mapped = TRUE;
4101 #if DEBUG_INIT_PLAYER
4103 printf("- map_player_action[%d] == %d\n",
4104 field_player->index_nr + 1, i + 1);
4109 if (player->connected && player->present)
4110 player->mapped = TRUE;
4113 #if DEBUG_INIT_PLAYER
4116 printf("Player status after player assignment (first stage):\n");
4118 for (i = 0; i < MAX_PLAYERS; i++)
4120 struct PlayerInfo *player = &stored_player[i];
4122 printf("- player %d: present == %d, connected == %d, active == %d",
4128 if (local_player == player)
4129 printf(" (local player)");
4138 /* check if any connected player was not found in playfield */
4139 for (i = 0; i < MAX_PLAYERS; i++)
4141 struct PlayerInfo *player = &stored_player[i];
4143 if (player->connected && !player->present)
4145 for (j = 0; j < MAX_PLAYERS; j++)
4147 struct PlayerInfo *field_player = &stored_player[j];
4148 int jx = field_player->jx, jy = field_player->jy;
4150 /* assign first free player found that is present in the playfield */
4151 if (field_player->present && !field_player->connected)
4153 player->present = TRUE;
4154 player->active = TRUE;
4156 field_player->present = FALSE;
4157 field_player->active = FALSE;
4159 player->initial_element = field_player->initial_element;
4160 player->artwork_element = field_player->artwork_element;
4162 player->block_last_field = field_player->block_last_field;
4163 player->block_delay_adjustment = field_player->block_delay_adjustment;
4165 StorePlayer[jx][jy] = player->element_nr;
4167 player->jx = player->last_jx = jx;
4168 player->jy = player->last_jy = jy;
4178 printf("::: local_player->present == %d\n", local_player->present);
4183 /* when playing a tape, eliminate all players who do not participate */
4185 #if USE_NEW_PLAYER_ASSIGNMENTS
4188 if (!game.team_mode)
4191 for (i = 0; i < MAX_PLAYERS; i++)
4193 if (stored_player[i].active &&
4194 !tape.player_participates[map_player_action[i]])
4196 struct PlayerInfo *player = &stored_player[i];
4197 int jx = player->jx, jy = player->jy;
4199 #if DEBUG_INIT_PLAYER
4201 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4204 player->active = FALSE;
4205 StorePlayer[jx][jy] = 0;
4206 Feld[jx][jy] = EL_EMPTY;
4212 for (i = 0; i < MAX_PLAYERS; i++)
4214 if (stored_player[i].active &&
4215 !tape.player_participates[i])
4217 struct PlayerInfo *player = &stored_player[i];
4218 int jx = player->jx, jy = player->jy;
4220 player->active = FALSE;
4221 StorePlayer[jx][jy] = 0;
4222 Feld[jx][jy] = EL_EMPTY;
4227 else if (!options.network && !game.team_mode) /* && !tape.playing */
4229 /* when in single player mode, eliminate all but the first active player */
4231 for (i = 0; i < MAX_PLAYERS; i++)
4233 if (stored_player[i].active)
4235 for (j = i + 1; j < MAX_PLAYERS; j++)
4237 if (stored_player[j].active)
4239 struct PlayerInfo *player = &stored_player[j];
4240 int jx = player->jx, jy = player->jy;
4242 player->active = FALSE;
4243 player->present = FALSE;
4245 StorePlayer[jx][jy] = 0;
4246 Feld[jx][jy] = EL_EMPTY;
4253 /* when recording the game, store which players take part in the game */
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257 for (i = 0; i < MAX_PLAYERS; i++)
4258 if (stored_player[i].connected)
4259 tape.player_participates[i] = TRUE;
4261 for (i = 0; i < MAX_PLAYERS; i++)
4262 if (stored_player[i].active)
4263 tape.player_participates[i] = TRUE;
4267 #if DEBUG_INIT_PLAYER
4270 printf("Player status after player assignment (final stage):\n");
4272 for (i = 0; i < MAX_PLAYERS; i++)
4274 struct PlayerInfo *player = &stored_player[i];
4276 printf("- player %d: present == %d, connected == %d, active == %d",
4282 if (local_player == player)
4283 printf(" (local player)");
4290 if (BorderElement == EL_EMPTY)
4293 SBX_Right = lev_fieldx - SCR_FIELDX;
4295 SBY_Lower = lev_fieldy - SCR_FIELDY;
4300 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4302 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4307 // printf("::: START-0: %d, %d\n", lev_fieldx, SCR_FIELDX);
4308 // printf("::: START-1: %d, %d\n", SBX_Left, SBX_Right);
4311 if (full_lev_fieldx <= SCR_FIELDX)
4312 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4314 if (full_lev_fieldy <= SCR_FIELDY)
4315 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4317 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4318 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4320 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4321 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4325 printf("::: START-2: %d, %d (%d)\n", SBX_Left, SBX_Right,
4326 SBX_Right - SBX_Left + 1);
4330 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4332 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4335 if (EVEN(SCR_FIELDX))
4337 if (EVEN(SCR_FIELDY))
4342 printf("::: START-3: %d, %d\n", SBX_Left, SBX_Right);
4348 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4349 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4351 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4352 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4355 /* if local player not found, look for custom element that might create
4356 the player (make some assumptions about the right custom element) */
4357 if (!local_player->present)
4359 int start_x = 0, start_y = 0;
4360 int found_rating = 0;
4361 int found_element = EL_UNDEFINED;
4362 int player_nr = local_player->index_nr;
4364 SCAN_PLAYFIELD(x, y)
4366 int element = Feld[x][y];
4371 if (level.use_start_element[player_nr] &&
4372 level.start_element[player_nr] == element &&
4379 found_element = element;
4382 if (!IS_CUSTOM_ELEMENT(element))
4385 if (CAN_CHANGE(element))
4387 for (i = 0; i < element_info[element].num_change_pages; i++)
4389 /* check for player created from custom element as single target */
4390 content = element_info[element].change_page[i].target_element;
4391 is_player = ELEM_IS_PLAYER(content);
4393 if (is_player && (found_rating < 3 ||
4394 (found_rating == 3 && element < found_element)))
4400 found_element = element;
4405 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4407 /* check for player created from custom element as explosion content */
4408 content = element_info[element].content.e[xx][yy];
4409 is_player = ELEM_IS_PLAYER(content);
4411 if (is_player && (found_rating < 2 ||
4412 (found_rating == 2 && element < found_element)))
4414 start_x = x + xx - 1;
4415 start_y = y + yy - 1;
4418 found_element = element;
4421 if (!CAN_CHANGE(element))
4424 for (i = 0; i < element_info[element].num_change_pages; i++)
4426 /* check for player created from custom element as extended target */
4428 element_info[element].change_page[i].target_content.e[xx][yy];
4430 is_player = ELEM_IS_PLAYER(content);
4432 if (is_player && (found_rating < 1 ||
4433 (found_rating == 1 && element < found_element)))
4435 start_x = x + xx - 1;
4436 start_y = y + yy - 1;
4439 found_element = element;
4445 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4446 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4449 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4450 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4455 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4456 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4457 local_player->jx - MIDPOSX);
4459 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4460 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4461 local_player->jy - MIDPOSY);
4465 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4469 /* do not use PLAYING mask for fading out from main screen */
4470 game_status = GAME_MODE_MAIN;
4477 if (!game.restart_level)
4478 CloseDoor(DOOR_CLOSE_1);
4481 if (level_editor_test_game)
4482 FadeSkipNextFadeIn();
4484 FadeSetEnterScreen();
4486 if (level_editor_test_game)
4487 fading = fading_none;
4489 fading = menu.destination;
4493 FadeOut(REDRAW_FIELD);
4496 FadeOut(REDRAW_FIELD);
4502 game_status = GAME_MODE_PLAYING;
4505 /* !!! FIX THIS (START) !!! */
4506 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4508 InitGameEngine_EM();
4510 /* blit playfield from scroll buffer to normal back buffer for fading in */
4511 BlitScreenToBitmap_EM(backbuffer);
4513 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4515 InitGameEngine_SP();
4517 /* blit playfield from scroll buffer to normal back buffer for fading in */
4518 BlitScreenToBitmap_SP(backbuffer);
4525 /* after drawing the level, correct some elements */
4526 if (game.timegate_time_left == 0)
4527 CloseAllOpenTimegates();
4530 BlitScreenToBitmap(backbuffer);
4532 /* blit playfield from scroll buffer to normal back buffer for fading in */
4533 if (setup.soft_scrolling)
4534 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4537 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4539 /* !!! FIX THIS (END) !!! */
4542 FadeIn(REDRAW_FIELD);
4545 FadeIn(REDRAW_FIELD);
4550 if (!game.restart_level)
4552 /* copy default game door content to main double buffer */
4555 /* !!! CHECK AGAIN !!! */
4556 SetPanelBackground();
4557 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4558 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4560 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4562 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4563 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4564 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4565 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4568 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4569 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4573 SetPanelBackground();
4574 SetDrawBackgroundMask(REDRAW_DOOR_1);
4577 UpdateAndDisplayGameControlValues();
4579 UpdateGameDoorValues();
4580 DrawGameDoorValues();
4583 if (!game.restart_level)
4587 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4588 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4589 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4593 /* copy actual game door content to door double buffer for OpenDoor() */
4595 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4597 BlitBitmap(drawto, bitmap_db_door,
4598 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4601 OpenDoor(DOOR_OPEN_ALL);
4603 PlaySound(SND_GAME_STARTING);
4605 if (setup.sound_music)
4608 KeyboardAutoRepeatOffUnlessAutoplay();
4610 #if DEBUG_INIT_PLAYER
4613 printf("Player status (final):\n");
4615 for (i = 0; i < MAX_PLAYERS; i++)
4617 struct PlayerInfo *player = &stored_player[i];
4619 printf("- player %d: present == %d, connected == %d, active == %d",
4625 if (local_player == player)
4626 printf(" (local player)");
4641 if (!game.restart_level && !tape.playing)
4643 LevelStats_incPlayed(level_nr);
4645 SaveLevelSetup_SeriesInfo();
4648 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4652 game.restart_level = FALSE;
4655 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4657 /* this is used for non-R'n'D game engines to update certain engine values */
4659 /* needed to determine if sounds are played within the visible screen area */
4660 scroll_x = actual_scroll_x;
4661 scroll_y = actual_scroll_y;
4664 void InitMovDir(int x, int y)
4666 int i, element = Feld[x][y];
4667 static int xy[4][2] =
4674 static int direction[3][4] =
4676 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4677 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4678 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4687 Feld[x][y] = EL_BUG;
4688 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4691 case EL_SPACESHIP_RIGHT:
4692 case EL_SPACESHIP_UP:
4693 case EL_SPACESHIP_LEFT:
4694 case EL_SPACESHIP_DOWN:
4695 Feld[x][y] = EL_SPACESHIP;
4696 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4699 case EL_BD_BUTTERFLY_RIGHT:
4700 case EL_BD_BUTTERFLY_UP:
4701 case EL_BD_BUTTERFLY_LEFT:
4702 case EL_BD_BUTTERFLY_DOWN:
4703 Feld[x][y] = EL_BD_BUTTERFLY;
4704 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4707 case EL_BD_FIREFLY_RIGHT:
4708 case EL_BD_FIREFLY_UP:
4709 case EL_BD_FIREFLY_LEFT:
4710 case EL_BD_FIREFLY_DOWN:
4711 Feld[x][y] = EL_BD_FIREFLY;
4712 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4715 case EL_PACMAN_RIGHT:
4717 case EL_PACMAN_LEFT:
4718 case EL_PACMAN_DOWN:
4719 Feld[x][y] = EL_PACMAN;
4720 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4723 case EL_YAMYAM_LEFT:
4724 case EL_YAMYAM_RIGHT:
4726 case EL_YAMYAM_DOWN:
4727 Feld[x][y] = EL_YAMYAM;
4728 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4731 case EL_SP_SNIKSNAK:
4732 MovDir[x][y] = MV_UP;
4735 case EL_SP_ELECTRON:
4736 MovDir[x][y] = MV_LEFT;
4743 Feld[x][y] = EL_MOLE;
4744 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4748 if (IS_CUSTOM_ELEMENT(element))
4750 struct ElementInfo *ei = &element_info[element];
4751 int move_direction_initial = ei->move_direction_initial;
4752 int move_pattern = ei->move_pattern;
4754 if (move_direction_initial == MV_START_PREVIOUS)
4756 if (MovDir[x][y] != MV_NONE)
4759 move_direction_initial = MV_START_AUTOMATIC;
4762 if (move_direction_initial == MV_START_RANDOM)
4763 MovDir[x][y] = 1 << RND(4);
4764 else if (move_direction_initial & MV_ANY_DIRECTION)
4765 MovDir[x][y] = move_direction_initial;
4766 else if (move_pattern == MV_ALL_DIRECTIONS ||
4767 move_pattern == MV_TURNING_LEFT ||
4768 move_pattern == MV_TURNING_RIGHT ||
4769 move_pattern == MV_TURNING_LEFT_RIGHT ||
4770 move_pattern == MV_TURNING_RIGHT_LEFT ||
4771 move_pattern == MV_TURNING_RANDOM)
4772 MovDir[x][y] = 1 << RND(4);
4773 else if (move_pattern == MV_HORIZONTAL)
4774 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4775 else if (move_pattern == MV_VERTICAL)
4776 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4777 else if (move_pattern & MV_ANY_DIRECTION)
4778 MovDir[x][y] = element_info[element].move_pattern;
4779 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4780 move_pattern == MV_ALONG_RIGHT_SIDE)
4782 /* use random direction as default start direction */
4783 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4784 MovDir[x][y] = 1 << RND(4);
4786 for (i = 0; i < NUM_DIRECTIONS; i++)
4788 int x1 = x + xy[i][0];
4789 int y1 = y + xy[i][1];
4791 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4793 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4794 MovDir[x][y] = direction[0][i];
4796 MovDir[x][y] = direction[1][i];
4805 MovDir[x][y] = 1 << RND(4);
4807 if (element != EL_BUG &&
4808 element != EL_SPACESHIP &&
4809 element != EL_BD_BUTTERFLY &&
4810 element != EL_BD_FIREFLY)
4813 for (i = 0; i < NUM_DIRECTIONS; i++)
4815 int x1 = x + xy[i][0];
4816 int y1 = y + xy[i][1];
4818 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4820 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4822 MovDir[x][y] = direction[0][i];
4825 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4826 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4828 MovDir[x][y] = direction[1][i];
4837 GfxDir[x][y] = MovDir[x][y];
4840 void InitAmoebaNr(int x, int y)
4843 int group_nr = AmoebeNachbarNr(x, y);
4847 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4849 if (AmoebaCnt[i] == 0)
4857 AmoebaNr[x][y] = group_nr;
4858 AmoebaCnt[group_nr]++;
4859 AmoebaCnt2[group_nr]++;
4862 static void PlayerWins(struct PlayerInfo *player)
4864 player->LevelSolved = TRUE;
4865 player->GameOver = TRUE;
4867 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4868 level.native_em_level->lev->score : player->score);
4870 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4872 player->LevelSolved_CountingScore = player->score_final;
4877 static int time, time_final;
4878 static int score, score_final;
4879 static int game_over_delay_1 = 0;
4880 static int game_over_delay_2 = 0;
4881 int game_over_delay_value_1 = 50;
4882 int game_over_delay_value_2 = 50;
4884 if (!local_player->LevelSolved_GameWon)
4888 /* do not start end game actions before the player stops moving (to exit) */
4889 if (local_player->MovPos)
4892 local_player->LevelSolved_GameWon = TRUE;
4893 local_player->LevelSolved_SaveTape = tape.recording;
4894 local_player->LevelSolved_SaveScore = !tape.playing;
4898 LevelStats_incSolved(level_nr);
4900 SaveLevelSetup_SeriesInfo();
4903 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4907 if (tape.auto_play) /* tape might already be stopped here */
4908 tape.auto_play_level_solved = TRUE;
4914 game_over_delay_1 = game_over_delay_value_1;
4915 game_over_delay_2 = game_over_delay_value_2;
4917 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4918 score = score_final = local_player->score_final;
4923 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4925 else if (game.no_time_limit && TimePlayed < 999)
4928 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4931 local_player->score_final = score_final;
4933 if (level_editor_test_game)
4936 score = score_final;
4939 local_player->LevelSolved_CountingTime = time;
4940 local_player->LevelSolved_CountingScore = score;
4942 game_panel_controls[GAME_PANEL_TIME].value = time;
4943 game_panel_controls[GAME_PANEL_SCORE].value = score;
4945 DisplayGameControlValues();
4947 DrawGameValue_Time(time);
4948 DrawGameValue_Score(score);
4952 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4954 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4956 /* close exit door after last player */
4957 if ((AllPlayersGone &&
4958 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4959 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4960 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4961 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4962 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4964 int element = Feld[ExitX][ExitY];
4967 if (element == EL_EM_EXIT_OPEN ||
4968 element == EL_EM_STEEL_EXIT_OPEN)
4975 Feld[ExitX][ExitY] =
4976 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4977 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4978 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4979 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4980 EL_EM_STEEL_EXIT_CLOSING);
4982 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4986 /* player disappears */
4987 DrawLevelField(ExitX, ExitY);
4990 for (i = 0; i < MAX_PLAYERS; i++)
4992 struct PlayerInfo *player = &stored_player[i];
4994 if (player->present)
4996 RemovePlayer(player);
4998 /* player disappears */
4999 DrawLevelField(player->jx, player->jy);
5004 PlaySound(SND_GAME_WINNING);
5007 if (game_over_delay_1 > 0)
5009 game_over_delay_1--;
5014 if (time != time_final)
5016 int time_to_go = ABS(time_final - time);
5017 int time_count_dir = (time < time_final ? +1 : -1);
5018 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5020 time += time_count_steps * time_count_dir;
5021 score += time_count_steps * level.score[SC_TIME_BONUS];
5024 local_player->LevelSolved_CountingTime = time;
5025 local_player->LevelSolved_CountingScore = score;
5027 game_panel_controls[GAME_PANEL_TIME].value = time;
5028 game_panel_controls[GAME_PANEL_SCORE].value = score;
5030 DisplayGameControlValues();
5032 DrawGameValue_Time(time);
5033 DrawGameValue_Score(score);
5036 if (time == time_final)
5037 StopSound(SND_GAME_LEVELTIME_BONUS);
5038 else if (setup.sound_loops)
5039 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5041 PlaySound(SND_GAME_LEVELTIME_BONUS);
5046 local_player->LevelSolved_PanelOff = TRUE;
5048 if (game_over_delay_2 > 0)
5050 game_over_delay_2--;
5063 boolean raise_level = FALSE;
5065 local_player->LevelSolved_GameEnd = TRUE;
5067 CloseDoor(DOOR_CLOSE_1);
5069 if (local_player->LevelSolved_SaveTape)
5076 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5078 SaveTape(tape.level_nr); /* ask to save tape */
5082 if (level_editor_test_game)
5084 game_status = GAME_MODE_MAIN;
5087 DrawAndFadeInMainMenu(REDRAW_FIELD);
5095 if (!local_player->LevelSolved_SaveScore)
5098 FadeOut(REDRAW_FIELD);
5101 game_status = GAME_MODE_MAIN;
5103 DrawAndFadeInMainMenu(REDRAW_FIELD);
5108 if (level_nr == leveldir_current->handicap_level)
5110 leveldir_current->handicap_level++;
5112 SaveLevelSetup_SeriesInfo();
5115 if (level_nr < leveldir_current->last_level)
5116 raise_level = TRUE; /* advance to next level */
5118 if ((hi_pos = NewHiScore()) >= 0)
5120 game_status = GAME_MODE_SCORES;
5122 DrawHallOfFame(hi_pos);
5133 FadeOut(REDRAW_FIELD);
5136 game_status = GAME_MODE_MAIN;
5144 DrawAndFadeInMainMenu(REDRAW_FIELD);
5153 LoadScore(level_nr);
5155 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5156 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5159 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5161 if (local_player->score_final > highscore[k].Score)
5163 /* player has made it to the hall of fame */
5165 if (k < MAX_SCORE_ENTRIES - 1)
5167 int m = MAX_SCORE_ENTRIES - 1;
5170 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5171 if (strEqual(setup.player_name, highscore[l].Name))
5173 if (m == k) /* player's new highscore overwrites his old one */
5177 for (l = m; l > k; l--)
5179 strcpy(highscore[l].Name, highscore[l - 1].Name);
5180 highscore[l].Score = highscore[l - 1].Score;
5187 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5188 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5189 highscore[k].Score = local_player->score_final;
5195 else if (!strncmp(setup.player_name, highscore[k].Name,
5196 MAX_PLAYER_NAME_LEN))
5197 break; /* player already there with a higher score */
5203 SaveScore(level_nr);
5208 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5210 int element = Feld[x][y];
5211 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5212 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5213 int horiz_move = (dx != 0);
5214 int sign = (horiz_move ? dx : dy);
5215 int step = sign * element_info[element].move_stepsize;
5217 /* special values for move stepsize for spring and things on conveyor belt */
5220 if (CAN_FALL(element) &&
5221 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5222 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5223 else if (element == EL_SPRING)
5224 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5230 inline static int getElementMoveStepsize(int x, int y)
5232 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5235 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5237 if (player->GfxAction != action || player->GfxDir != dir)
5240 printf("Player frame reset! (%d => %d, %d => %d)\n",
5241 player->GfxAction, action, player->GfxDir, dir);
5244 player->GfxAction = action;
5245 player->GfxDir = dir;
5247 player->StepFrame = 0;
5251 #if USE_GFX_RESET_GFX_ANIMATION
5252 static void ResetGfxFrame(int x, int y, boolean redraw)
5254 int element = Feld[x][y];
5255 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5256 int last_gfx_frame = GfxFrame[x][y];
5258 if (graphic_info[graphic].anim_global_sync)
5259 GfxFrame[x][y] = FrameCounter;
5260 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5261 GfxFrame[x][y] = CustomValue[x][y];
5262 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5263 GfxFrame[x][y] = element_info[element].collect_score;
5264 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5265 GfxFrame[x][y] = ChangeDelay[x][y];
5267 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5268 DrawLevelGraphicAnimation(x, y, graphic);
5272 static void ResetGfxAnimation(int x, int y)
5274 GfxAction[x][y] = ACTION_DEFAULT;
5275 GfxDir[x][y] = MovDir[x][y];
5278 #if USE_GFX_RESET_GFX_ANIMATION
5279 ResetGfxFrame(x, y, FALSE);
5283 static void ResetRandomAnimationValue(int x, int y)
5285 GfxRandom[x][y] = INIT_GFX_RANDOM();
5288 void InitMovingField(int x, int y, int direction)
5290 int element = Feld[x][y];
5291 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5292 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5295 boolean is_moving_before, is_moving_after;
5297 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5300 /* check if element was/is moving or being moved before/after mode change */
5303 is_moving_before = (WasJustMoving[x][y] != 0);
5305 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5306 is_moving_before = WasJustMoving[x][y];
5309 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5311 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5313 /* reset animation only for moving elements which change direction of moving
5314 or which just started or stopped moving
5315 (else CEs with property "can move" / "not moving" are reset each frame) */
5316 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5318 if (is_moving_before != is_moving_after ||
5319 direction != MovDir[x][y])
5320 ResetGfxAnimation(x, y);
5322 if ((is_moving_before || is_moving_after) && !continues_moving)
5323 ResetGfxAnimation(x, y);
5326 if (!continues_moving)
5327 ResetGfxAnimation(x, y);
5330 MovDir[x][y] = direction;
5331 GfxDir[x][y] = direction;
5333 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5334 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5335 direction == MV_DOWN && CAN_FALL(element) ?
5336 ACTION_FALLING : ACTION_MOVING);
5338 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5339 ACTION_FALLING : ACTION_MOVING);
5342 /* this is needed for CEs with property "can move" / "not moving" */
5344 if (is_moving_after)
5346 if (Feld[newx][newy] == EL_EMPTY)
5347 Feld[newx][newy] = EL_BLOCKED;
5349 MovDir[newx][newy] = MovDir[x][y];
5351 #if USE_NEW_CUSTOM_VALUE
5352 CustomValue[newx][newy] = CustomValue[x][y];
5355 GfxFrame[newx][newy] = GfxFrame[x][y];
5356 GfxRandom[newx][newy] = GfxRandom[x][y];
5357 GfxAction[newx][newy] = GfxAction[x][y];
5358 GfxDir[newx][newy] = GfxDir[x][y];
5362 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5364 int direction = MovDir[x][y];
5365 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5366 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5372 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5374 int oldx = x, oldy = y;
5375 int direction = MovDir[x][y];
5377 if (direction == MV_LEFT)
5379 else if (direction == MV_RIGHT)
5381 else if (direction == MV_UP)
5383 else if (direction == MV_DOWN)
5386 *comes_from_x = oldx;
5387 *comes_from_y = oldy;
5390 int MovingOrBlocked2Element(int x, int y)
5392 int element = Feld[x][y];
5394 if (element == EL_BLOCKED)
5398 Blocked2Moving(x, y, &oldx, &oldy);
5399 return Feld[oldx][oldy];
5405 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5407 /* like MovingOrBlocked2Element(), but if element is moving
5408 and (x,y) is the field the moving element is just leaving,
5409 return EL_BLOCKED instead of the element value */
5410 int element = Feld[x][y];
5412 if (IS_MOVING(x, y))
5414 if (element == EL_BLOCKED)
5418 Blocked2Moving(x, y, &oldx, &oldy);
5419 return Feld[oldx][oldy];
5428 static void RemoveField(int x, int y)
5430 Feld[x][y] = EL_EMPTY;
5436 #if USE_NEW_CUSTOM_VALUE
5437 CustomValue[x][y] = 0;
5441 ChangeDelay[x][y] = 0;
5442 ChangePage[x][y] = -1;
5443 Pushed[x][y] = FALSE;
5446 ExplodeField[x][y] = EX_TYPE_NONE;
5449 GfxElement[x][y] = EL_UNDEFINED;
5450 GfxAction[x][y] = ACTION_DEFAULT;
5451 GfxDir[x][y] = MV_NONE;
5453 /* !!! this would prevent the removed tile from being redrawn !!! */
5454 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5458 void RemoveMovingField(int x, int y)
5460 int oldx = x, oldy = y, newx = x, newy = y;
5461 int element = Feld[x][y];
5462 int next_element = EL_UNDEFINED;
5464 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5467 if (IS_MOVING(x, y))
5469 Moving2Blocked(x, y, &newx, &newy);
5471 if (Feld[newx][newy] != EL_BLOCKED)
5473 /* element is moving, but target field is not free (blocked), but
5474 already occupied by something different (example: acid pool);
5475 in this case, only remove the moving field, but not the target */
5477 RemoveField(oldx, oldy);
5479 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5481 TEST_DrawLevelField(oldx, oldy);
5486 else if (element == EL_BLOCKED)
5488 Blocked2Moving(x, y, &oldx, &oldy);
5489 if (!IS_MOVING(oldx, oldy))
5493 if (element == EL_BLOCKED &&
5494 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5495 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5496 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5497 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5498 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5499 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5500 next_element = get_next_element(Feld[oldx][oldy]);
5502 RemoveField(oldx, oldy);
5503 RemoveField(newx, newy);
5505 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5507 if (next_element != EL_UNDEFINED)
5508 Feld[oldx][oldy] = next_element;
5510 TEST_DrawLevelField(oldx, oldy);
5511 TEST_DrawLevelField(newx, newy);
5514 void DrawDynamite(int x, int y)
5516 int sx = SCREENX(x), sy = SCREENY(y);
5517 int graphic = el2img(Feld[x][y]);
5520 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5523 if (IS_WALKABLE_INSIDE(Back[x][y]))
5527 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5528 else if (Store[x][y])
5529 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5531 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5533 if (Back[x][y] || Store[x][y])
5534 DrawGraphicThruMask(sx, sy, graphic, frame);
5536 DrawGraphic(sx, sy, graphic, frame);
5539 void CheckDynamite(int x, int y)
5541 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5545 if (MovDelay[x][y] != 0)
5548 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5554 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5559 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5561 boolean num_checked_players = 0;
5564 for (i = 0; i < MAX_PLAYERS; i++)
5566 if (stored_player[i].active)
5568 int sx = stored_player[i].jx;
5569 int sy = stored_player[i].jy;
5571 if (num_checked_players == 0)
5578 *sx1 = MIN(*sx1, sx);
5579 *sy1 = MIN(*sy1, sy);
5580 *sx2 = MAX(*sx2, sx);
5581 *sy2 = MAX(*sy2, sy);
5584 num_checked_players++;
5589 static boolean checkIfAllPlayersFitToScreen_RND()
5591 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5593 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5595 return (sx2 - sx1 < SCR_FIELDX &&
5596 sy2 - sy1 < SCR_FIELDY);
5599 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5601 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5603 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5605 *sx = (sx1 + sx2) / 2;
5606 *sy = (sy1 + sy2) / 2;
5609 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5610 boolean center_screen, boolean quick_relocation)
5612 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5613 boolean no_delay = (tape.warp_forward);
5614 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5615 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5617 if (quick_relocation)
5619 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5621 if (!level.shifted_relocation || center_screen)
5623 /* quick relocation (without scrolling), with centering of screen */
5625 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5626 x > SBX_Right + MIDPOSX ? SBX_Right :
5629 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5630 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5635 /* quick relocation (without scrolling), but do not center screen */
5637 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5638 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5641 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5645 int offset_x = x + (scroll_x - center_scroll_x);
5646 int offset_y = y + (scroll_y - center_scroll_y);
5648 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5649 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5650 offset_x - MIDPOSX);
5652 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5653 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5654 offset_y - MIDPOSY);
5660 if (!level.shifted_relocation || center_screen)
5662 /* quick relocation (without scrolling), with centering of screen */
5664 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5665 x > SBX_Right + MIDPOSX ? SBX_Right :
5668 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5669 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5674 /* quick relocation (without scrolling), but do not center screen */
5676 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5677 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5680 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5684 int offset_x = x + (scroll_x - center_scroll_x);
5685 int offset_y = y + (scroll_y - center_scroll_y);
5687 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5688 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5689 offset_x - MIDPOSX);
5691 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5692 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5693 offset_y - MIDPOSY);
5696 /* quick relocation (without scrolling), inside visible screen area */
5698 int offset = game.scroll_delay_value;
5700 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5701 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5702 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5704 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5705 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5706 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5708 /* don't scroll over playfield boundaries */
5709 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5710 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5712 /* don't scroll over playfield boundaries */
5713 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5714 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5718 RedrawPlayfield(TRUE, 0,0,0,0);
5723 int scroll_xx, scroll_yy;
5725 if (!level.shifted_relocation || center_screen)
5727 /* visible relocation (with scrolling), with centering of screen */
5729 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5730 x > SBX_Right + MIDPOSX ? SBX_Right :
5733 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5734 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5739 /* visible relocation (with scrolling), but do not center screen */
5741 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5742 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5745 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5749 int offset_x = x + (scroll_x - center_scroll_x);
5750 int offset_y = y + (scroll_y - center_scroll_y);
5752 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5753 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5754 offset_x - MIDPOSX);
5756 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5757 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5758 offset_y - MIDPOSY);
5763 /* visible relocation (with scrolling), with centering of screen */
5765 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5766 x > SBX_Right + MIDPOSX ? SBX_Right :
5769 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5770 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5774 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5776 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5779 int fx = FX, fy = FY;
5781 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5782 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5784 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5790 fx += dx * TILEX / 2;
5791 fy += dy * TILEY / 2;
5793 ScrollLevel(dx, dy);
5796 /* scroll in two steps of half tile size to make things smoother */
5797 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5799 Delay(wait_delay_value);
5801 /* scroll second step to align at full tile size */
5803 Delay(wait_delay_value);
5808 Delay(wait_delay_value);
5812 void RelocatePlayer(int jx, int jy, int el_player_raw)
5814 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5815 int player_nr = GET_PLAYER_NR(el_player);
5816 struct PlayerInfo *player = &stored_player[player_nr];
5817 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5818 boolean no_delay = (tape.warp_forward);
5819 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5820 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5821 int old_jx = player->jx;
5822 int old_jy = player->jy;
5823 int old_element = Feld[old_jx][old_jy];
5824 int element = Feld[jx][jy];
5825 boolean player_relocated = (old_jx != jx || old_jy != jy);
5827 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5828 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5829 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5830 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5831 int leave_side_horiz = move_dir_horiz;
5832 int leave_side_vert = move_dir_vert;
5833 int enter_side = enter_side_horiz | enter_side_vert;
5834 int leave_side = leave_side_horiz | leave_side_vert;
5836 if (player->GameOver) /* do not reanimate dead player */
5839 if (!player_relocated) /* no need to relocate the player */
5842 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5844 RemoveField(jx, jy); /* temporarily remove newly placed player */
5845 DrawLevelField(jx, jy);
5848 if (player->present)
5850 while (player->MovPos)
5852 ScrollPlayer(player, SCROLL_GO_ON);
5853 ScrollScreen(NULL, SCROLL_GO_ON);
5855 AdvanceFrameAndPlayerCounters(player->index_nr);
5860 Delay(wait_delay_value);
5863 DrawPlayer(player); /* needed here only to cleanup last field */
5864 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5866 player->is_moving = FALSE;
5869 if (IS_CUSTOM_ELEMENT(old_element))
5870 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5872 player->index_bit, leave_side);
5874 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5876 player->index_bit, leave_side);
5878 Feld[jx][jy] = el_player;
5879 InitPlayerField(jx, jy, el_player, TRUE);
5881 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5882 possible that the relocation target field did not contain a player element,
5883 but a walkable element, to which the new player was relocated -- in this
5884 case, restore that (already initialized!) element on the player field */
5885 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5887 Feld[jx][jy] = element; /* restore previously existing element */
5889 /* !!! do not initialize already initialized element a second time !!! */
5890 /* (this causes at least problems with "element creation" CE trigger for
5891 already existing elements, and existing Sokoban fields counted twice) */
5892 InitField(jx, jy, FALSE);
5896 /* only visually relocate centered player */
5897 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5898 FALSE, level.instant_relocation);
5900 TestIfPlayerTouchesBadThing(jx, jy);
5901 TestIfPlayerTouchesCustomElement(jx, jy);
5903 if (IS_CUSTOM_ELEMENT(element))
5904 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5905 player->index_bit, enter_side);
5907 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5908 player->index_bit, enter_side);
5911 if (player->is_switching)
5913 /* ensure that relocation while still switching an element does not cause
5914 a new element to be treated as also switched directly after relocation
5915 (this is important for teleporter switches that teleport the player to
5916 a place where another teleporter switch is in the same direction, which
5917 would then incorrectly be treated as immediately switched before the
5918 direction key that caused the switch was released) */
5920 player->switch_x += jx - old_jx;
5921 player->switch_y += jy - old_jy;
5926 void Explode(int ex, int ey, int phase, int mode)
5932 /* !!! eliminate this variable !!! */
5933 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5935 if (game.explosions_delayed)
5937 ExplodeField[ex][ey] = mode;
5941 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5943 int center_element = Feld[ex][ey];
5944 int artwork_element, explosion_element; /* set these values later */
5947 /* --- This is only really needed (and now handled) in "Impact()". --- */
5948 /* do not explode moving elements that left the explode field in time */
5949 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5950 center_element == EL_EMPTY &&
5951 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5956 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5957 if (mode == EX_TYPE_NORMAL ||
5958 mode == EX_TYPE_CENTER ||
5959 mode == EX_TYPE_CROSS)
5960 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5963 /* remove things displayed in background while burning dynamite */
5964 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5967 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5969 /* put moving element to center field (and let it explode there) */
5970 center_element = MovingOrBlocked2Element(ex, ey);
5971 RemoveMovingField(ex, ey);
5972 Feld[ex][ey] = center_element;
5975 /* now "center_element" is finally determined -- set related values now */
5976 artwork_element = center_element; /* for custom player artwork */
5977 explosion_element = center_element; /* for custom player artwork */
5979 if (IS_PLAYER(ex, ey))
5981 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5983 artwork_element = stored_player[player_nr].artwork_element;
5985 if (level.use_explosion_element[player_nr])
5987 explosion_element = level.explosion_element[player_nr];
5988 artwork_element = explosion_element;
5993 if (mode == EX_TYPE_NORMAL ||
5994 mode == EX_TYPE_CENTER ||
5995 mode == EX_TYPE_CROSS)
5996 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5999 last_phase = element_info[explosion_element].explosion_delay + 1;
6001 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6003 int xx = x - ex + 1;
6004 int yy = y - ey + 1;
6007 if (!IN_LEV_FIELD(x, y) ||
6008 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6009 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
6012 element = Feld[x][y];
6014 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6016 element = MovingOrBlocked2Element(x, y);
6018 if (!IS_EXPLOSION_PROOF(element))
6019 RemoveMovingField(x, y);
6022 /* indestructible elements can only explode in center (but not flames) */
6023 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6024 mode == EX_TYPE_BORDER)) ||
6025 element == EL_FLAMES)
6028 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6029 behaviour, for example when touching a yamyam that explodes to rocks
6030 with active deadly shield, a rock is created under the player !!! */
6031 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6033 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6034 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6035 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6037 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6040 if (IS_ACTIVE_BOMB(element))
6042 /* re-activate things under the bomb like gate or penguin */
6043 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6050 /* save walkable background elements while explosion on same tile */
6051 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6052 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6053 Back[x][y] = element;
6055 /* ignite explodable elements reached by other explosion */
6056 if (element == EL_EXPLOSION)
6057 element = Store2[x][y];
6059 if (AmoebaNr[x][y] &&
6060 (element == EL_AMOEBA_FULL ||
6061 element == EL_BD_AMOEBA ||
6062 element == EL_AMOEBA_GROWING))
6064 AmoebaCnt[AmoebaNr[x][y]]--;
6065 AmoebaCnt2[AmoebaNr[x][y]]--;
6070 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6072 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6074 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6076 if (PLAYERINFO(ex, ey)->use_murphy)
6077 Store[x][y] = EL_EMPTY;
6080 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6081 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6082 else if (ELEM_IS_PLAYER(center_element))
6083 Store[x][y] = EL_EMPTY;
6084 else if (center_element == EL_YAMYAM)
6085 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6086 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6087 Store[x][y] = element_info[center_element].content.e[xx][yy];
6089 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6090 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6091 otherwise) -- FIX THIS !!! */
6092 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6093 Store[x][y] = element_info[element].content.e[1][1];
6095 else if (!CAN_EXPLODE(element))
6096 Store[x][y] = element_info[element].content.e[1][1];
6099 Store[x][y] = EL_EMPTY;
6101 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6102 center_element == EL_AMOEBA_TO_DIAMOND)
6103 Store2[x][y] = element;
6105 Feld[x][y] = EL_EXPLOSION;
6106 GfxElement[x][y] = artwork_element;
6108 ExplodePhase[x][y] = 1;
6109 ExplodeDelay[x][y] = last_phase;
6114 if (center_element == EL_YAMYAM)
6115 game.yamyam_content_nr =
6116 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6128 GfxFrame[x][y] = 0; /* restart explosion animation */
6130 last_phase = ExplodeDelay[x][y];
6132 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6136 /* activate this even in non-DEBUG version until cause for crash in
6137 getGraphicAnimationFrame() (see below) is found and eliminated */
6143 /* this can happen if the player leaves an explosion just in time */
6144 if (GfxElement[x][y] == EL_UNDEFINED)
6145 GfxElement[x][y] = EL_EMPTY;
6147 if (GfxElement[x][y] == EL_UNDEFINED)
6150 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6151 printf("Explode(): This should never happen!\n");
6154 GfxElement[x][y] = EL_EMPTY;
6160 border_element = Store2[x][y];
6161 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6162 border_element = StorePlayer[x][y];
6164 if (phase == element_info[border_element].ignition_delay ||
6165 phase == last_phase)
6167 boolean border_explosion = FALSE;
6169 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6170 !PLAYER_EXPLOSION_PROTECTED(x, y))
6172 KillPlayerUnlessExplosionProtected(x, y);
6173 border_explosion = TRUE;
6175 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6177 Feld[x][y] = Store2[x][y];
6180 border_explosion = TRUE;
6182 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6184 AmoebeUmwandeln(x, y);
6186 border_explosion = TRUE;
6189 /* if an element just explodes due to another explosion (chain-reaction),
6190 do not immediately end the new explosion when it was the last frame of
6191 the explosion (as it would be done in the following "if"-statement!) */
6192 if (border_explosion && phase == last_phase)
6196 if (phase == last_phase)
6200 element = Feld[x][y] = Store[x][y];
6201 Store[x][y] = Store2[x][y] = 0;
6202 GfxElement[x][y] = EL_UNDEFINED;
6204 /* player can escape from explosions and might therefore be still alive */
6205 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6206 element <= EL_PLAYER_IS_EXPLODING_4)
6208 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6209 int explosion_element = EL_PLAYER_1 + player_nr;
6210 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6211 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6213 if (level.use_explosion_element[player_nr])
6214 explosion_element = level.explosion_element[player_nr];
6216 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6217 element_info[explosion_element].content.e[xx][yy]);
6220 /* restore probably existing indestructible background element */
6221 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6222 element = Feld[x][y] = Back[x][y];
6225 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6226 GfxDir[x][y] = MV_NONE;
6227 ChangeDelay[x][y] = 0;
6228 ChangePage[x][y] = -1;
6230 #if USE_NEW_CUSTOM_VALUE
6231 CustomValue[x][y] = 0;
6234 InitField_WithBug2(x, y, FALSE);
6236 TEST_DrawLevelField(x, y);
6238 TestIfElementTouchesCustomElement(x, y);
6240 if (GFX_CRUMBLED(element))
6241 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6243 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6244 StorePlayer[x][y] = 0;
6246 if (ELEM_IS_PLAYER(element))
6247 RelocatePlayer(x, y, element);
6249 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6251 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6252 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6255 TEST_DrawLevelFieldCrumbled(x, y);
6257 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6259 DrawLevelElement(x, y, Back[x][y]);
6260 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6262 else if (IS_WALKABLE_UNDER(Back[x][y]))
6264 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6265 DrawLevelElementThruMask(x, y, Back[x][y]);
6267 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6268 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6272 void DynaExplode(int ex, int ey)
6275 int dynabomb_element = Feld[ex][ey];
6276 int dynabomb_size = 1;
6277 boolean dynabomb_xl = FALSE;
6278 struct PlayerInfo *player;
6279 static int xy[4][2] =
6287 if (IS_ACTIVE_BOMB(dynabomb_element))
6289 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6290 dynabomb_size = player->dynabomb_size;
6291 dynabomb_xl = player->dynabomb_xl;
6292 player->dynabombs_left++;
6295 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6297 for (i = 0; i < NUM_DIRECTIONS; i++)
6299 for (j = 1; j <= dynabomb_size; j++)
6301 int x = ex + j * xy[i][0];
6302 int y = ey + j * xy[i][1];
6305 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6308 element = Feld[x][y];
6310 /* do not restart explosions of fields with active bombs */
6311 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6314 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6316 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6317 !IS_DIGGABLE(element) && !dynabomb_xl)
6323 void Bang(int x, int y)
6325 int element = MovingOrBlocked2Element(x, y);
6326 int explosion_type = EX_TYPE_NORMAL;
6328 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6330 struct PlayerInfo *player = PLAYERINFO(x, y);
6332 #if USE_FIX_CE_ACTION_WITH_PLAYER
6333 element = Feld[x][y] = player->initial_element;
6335 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6336 player->element_nr);
6339 if (level.use_explosion_element[player->index_nr])
6341 int explosion_element = level.explosion_element[player->index_nr];
6343 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6344 explosion_type = EX_TYPE_CROSS;
6345 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6346 explosion_type = EX_TYPE_CENTER;
6354 case EL_BD_BUTTERFLY:
6357 case EL_DARK_YAMYAM:
6361 RaiseScoreElement(element);
6364 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6365 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6366 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6367 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6368 case EL_DYNABOMB_INCREASE_NUMBER:
6369 case EL_DYNABOMB_INCREASE_SIZE:
6370 case EL_DYNABOMB_INCREASE_POWER:
6371 explosion_type = EX_TYPE_DYNA;
6374 case EL_DC_LANDMINE:
6376 case EL_EM_EXIT_OPEN:
6377 case EL_EM_STEEL_EXIT_OPEN:
6379 explosion_type = EX_TYPE_CENTER;
6384 case EL_LAMP_ACTIVE:
6385 case EL_AMOEBA_TO_DIAMOND:
6386 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6387 explosion_type = EX_TYPE_CENTER;
6391 if (element_info[element].explosion_type == EXPLODES_CROSS)
6392 explosion_type = EX_TYPE_CROSS;
6393 else if (element_info[element].explosion_type == EXPLODES_1X1)
6394 explosion_type = EX_TYPE_CENTER;
6398 if (explosion_type == EX_TYPE_DYNA)
6401 Explode(x, y, EX_PHASE_START, explosion_type);
6403 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6406 void SplashAcid(int x, int y)
6408 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6409 (!IN_LEV_FIELD(x - 1, y - 2) ||
6410 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6411 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6413 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6414 (!IN_LEV_FIELD(x + 1, y - 2) ||
6415 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6416 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6418 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6421 static void InitBeltMovement()
6423 static int belt_base_element[4] =
6425 EL_CONVEYOR_BELT_1_LEFT,
6426 EL_CONVEYOR_BELT_2_LEFT,
6427 EL_CONVEYOR_BELT_3_LEFT,
6428 EL_CONVEYOR_BELT_4_LEFT
6430 static int belt_base_active_element[4] =
6432 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6433 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6434 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6435 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6440 /* set frame order for belt animation graphic according to belt direction */
6441 for (i = 0; i < NUM_BELTS; i++)
6445 for (j = 0; j < NUM_BELT_PARTS; j++)
6447 int element = belt_base_active_element[belt_nr] + j;
6448 int graphic_1 = el2img(element);
6449 int graphic_2 = el2panelimg(element);
6451 if (game.belt_dir[i] == MV_LEFT)
6453 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6454 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6458 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6459 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6464 SCAN_PLAYFIELD(x, y)
6466 int element = Feld[x][y];
6468 for (i = 0; i < NUM_BELTS; i++)
6470 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6472 int e_belt_nr = getBeltNrFromBeltElement(element);
6475 if (e_belt_nr == belt_nr)
6477 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6479 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6486 static void ToggleBeltSwitch(int x, int y)
6488 static int belt_base_element[4] =
6490 EL_CONVEYOR_BELT_1_LEFT,
6491 EL_CONVEYOR_BELT_2_LEFT,
6492 EL_CONVEYOR_BELT_3_LEFT,
6493 EL_CONVEYOR_BELT_4_LEFT
6495 static int belt_base_active_element[4] =
6497 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6498 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6499 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6500 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6502 static int belt_base_switch_element[4] =
6504 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6505 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6506 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6507 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6509 static int belt_move_dir[4] =
6517 int element = Feld[x][y];
6518 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6519 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6520 int belt_dir = belt_move_dir[belt_dir_nr];
6523 if (!IS_BELT_SWITCH(element))
6526 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6527 game.belt_dir[belt_nr] = belt_dir;
6529 if (belt_dir_nr == 3)
6532 /* set frame order for belt animation graphic according to belt direction */
6533 for (i = 0; i < NUM_BELT_PARTS; i++)
6535 int element = belt_base_active_element[belt_nr] + i;
6536 int graphic_1 = el2img(element);
6537 int graphic_2 = el2panelimg(element);
6539 if (belt_dir == MV_LEFT)
6541 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6542 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6546 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6547 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6551 SCAN_PLAYFIELD(xx, yy)
6553 int element = Feld[xx][yy];
6555 if (IS_BELT_SWITCH(element))
6557 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6559 if (e_belt_nr == belt_nr)
6561 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6562 TEST_DrawLevelField(xx, yy);
6565 else if (IS_BELT(element) && belt_dir != MV_NONE)
6567 int e_belt_nr = getBeltNrFromBeltElement(element);
6569 if (e_belt_nr == belt_nr)
6571 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6573 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6574 TEST_DrawLevelField(xx, yy);
6577 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6579 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6581 if (e_belt_nr == belt_nr)
6583 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6585 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6586 TEST_DrawLevelField(xx, yy);
6592 static void ToggleSwitchgateSwitch(int x, int y)
6596 game.switchgate_pos = !game.switchgate_pos;
6598 SCAN_PLAYFIELD(xx, yy)
6600 int element = Feld[xx][yy];
6602 #if !USE_BOTH_SWITCHGATE_SWITCHES
6603 if (element == EL_SWITCHGATE_SWITCH_UP ||
6604 element == EL_SWITCHGATE_SWITCH_DOWN)
6606 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6607 TEST_DrawLevelField(xx, yy);
6609 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6610 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6612 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6613 TEST_DrawLevelField(xx, yy);
6616 if (element == EL_SWITCHGATE_SWITCH_UP)
6618 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6619 TEST_DrawLevelField(xx, yy);
6621 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6623 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6624 TEST_DrawLevelField(xx, yy);
6626 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6628 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6629 TEST_DrawLevelField(xx, yy);
6631 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6633 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6634 TEST_DrawLevelField(xx, yy);
6637 else if (element == EL_SWITCHGATE_OPEN ||
6638 element == EL_SWITCHGATE_OPENING)
6640 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6642 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6644 else if (element == EL_SWITCHGATE_CLOSED ||
6645 element == EL_SWITCHGATE_CLOSING)
6647 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6649 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6654 static int getInvisibleActiveFromInvisibleElement(int element)
6656 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6657 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6658 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6662 static int getInvisibleFromInvisibleActiveElement(int element)
6664 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6665 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6666 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6670 static void RedrawAllLightSwitchesAndInvisibleElements()
6674 SCAN_PLAYFIELD(x, y)
6676 int element = Feld[x][y];
6678 if (element == EL_LIGHT_SWITCH &&
6679 game.light_time_left > 0)
6681 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6682 TEST_DrawLevelField(x, y);
6684 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6685 game.light_time_left == 0)
6687 Feld[x][y] = EL_LIGHT_SWITCH;
6688 TEST_DrawLevelField(x, y);
6690 else if (element == EL_EMC_DRIPPER &&
6691 game.light_time_left > 0)
6693 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6694 TEST_DrawLevelField(x, y);
6696 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6697 game.light_time_left == 0)
6699 Feld[x][y] = EL_EMC_DRIPPER;
6700 TEST_DrawLevelField(x, y);
6702 else if (element == EL_INVISIBLE_STEELWALL ||
6703 element == EL_INVISIBLE_WALL ||
6704 element == EL_INVISIBLE_SAND)
6706 if (game.light_time_left > 0)
6707 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6709 TEST_DrawLevelField(x, y);
6711 /* uncrumble neighbour fields, if needed */
6712 if (element == EL_INVISIBLE_SAND)
6713 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6715 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6716 element == EL_INVISIBLE_WALL_ACTIVE ||
6717 element == EL_INVISIBLE_SAND_ACTIVE)
6719 if (game.light_time_left == 0)
6720 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6722 TEST_DrawLevelField(x, y);
6724 /* re-crumble neighbour fields, if needed */
6725 if (element == EL_INVISIBLE_SAND)
6726 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6731 static void RedrawAllInvisibleElementsForLenses()
6735 SCAN_PLAYFIELD(x, y)
6737 int element = Feld[x][y];
6739 if (element == EL_EMC_DRIPPER &&
6740 game.lenses_time_left > 0)
6742 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6743 TEST_DrawLevelField(x, y);
6745 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6746 game.lenses_time_left == 0)
6748 Feld[x][y] = EL_EMC_DRIPPER;
6749 TEST_DrawLevelField(x, y);
6751 else if (element == EL_INVISIBLE_STEELWALL ||
6752 element == EL_INVISIBLE_WALL ||
6753 element == EL_INVISIBLE_SAND)
6755 if (game.lenses_time_left > 0)
6756 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6758 TEST_DrawLevelField(x, y);
6760 /* uncrumble neighbour fields, if needed */
6761 if (element == EL_INVISIBLE_SAND)
6762 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6764 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6765 element == EL_INVISIBLE_WALL_ACTIVE ||
6766 element == EL_INVISIBLE_SAND_ACTIVE)
6768 if (game.lenses_time_left == 0)
6769 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6771 TEST_DrawLevelField(x, y);
6773 /* re-crumble neighbour fields, if needed */
6774 if (element == EL_INVISIBLE_SAND)
6775 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6780 static void RedrawAllInvisibleElementsForMagnifier()
6784 SCAN_PLAYFIELD(x, y)
6786 int element = Feld[x][y];
6788 if (element == EL_EMC_FAKE_GRASS &&
6789 game.magnify_time_left > 0)
6791 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6792 TEST_DrawLevelField(x, y);
6794 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6795 game.magnify_time_left == 0)
6797 Feld[x][y] = EL_EMC_FAKE_GRASS;
6798 TEST_DrawLevelField(x, y);
6800 else if (IS_GATE_GRAY(element) &&
6801 game.magnify_time_left > 0)
6803 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6804 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6805 IS_EM_GATE_GRAY(element) ?
6806 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6807 IS_EMC_GATE_GRAY(element) ?
6808 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6809 IS_DC_GATE_GRAY(element) ?
6810 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6812 TEST_DrawLevelField(x, y);
6814 else if (IS_GATE_GRAY_ACTIVE(element) &&
6815 game.magnify_time_left == 0)
6817 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6818 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6819 IS_EM_GATE_GRAY_ACTIVE(element) ?
6820 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6821 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6822 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6823 IS_DC_GATE_GRAY_ACTIVE(element) ?
6824 EL_DC_GATE_WHITE_GRAY :
6826 TEST_DrawLevelField(x, y);
6831 static void ToggleLightSwitch(int x, int y)
6833 int element = Feld[x][y];
6835 game.light_time_left =
6836 (element == EL_LIGHT_SWITCH ?
6837 level.time_light * FRAMES_PER_SECOND : 0);
6839 RedrawAllLightSwitchesAndInvisibleElements();
6842 static void ActivateTimegateSwitch(int x, int y)
6846 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6848 SCAN_PLAYFIELD(xx, yy)
6850 int element = Feld[xx][yy];
6852 if (element == EL_TIMEGATE_CLOSED ||
6853 element == EL_TIMEGATE_CLOSING)
6855 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6856 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6860 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6862 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6863 TEST_DrawLevelField(xx, yy);
6870 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6871 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6873 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6877 void Impact(int x, int y)
6879 boolean last_line = (y == lev_fieldy - 1);
6880 boolean object_hit = FALSE;
6881 boolean impact = (last_line || object_hit);
6882 int element = Feld[x][y];
6883 int smashed = EL_STEELWALL;
6885 if (!last_line) /* check if element below was hit */
6887 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6890 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6891 MovDir[x][y + 1] != MV_DOWN ||
6892 MovPos[x][y + 1] <= TILEY / 2));
6894 /* do not smash moving elements that left the smashed field in time */
6895 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6896 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6899 #if USE_QUICKSAND_IMPACT_BUGFIX
6900 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6902 RemoveMovingField(x, y + 1);
6903 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6904 Feld[x][y + 2] = EL_ROCK;
6905 TEST_DrawLevelField(x, y + 2);
6910 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6912 RemoveMovingField(x, y + 1);
6913 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6914 Feld[x][y + 2] = EL_ROCK;
6915 TEST_DrawLevelField(x, y + 2);
6922 smashed = MovingOrBlocked2Element(x, y + 1);
6924 impact = (last_line || object_hit);
6927 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6929 SplashAcid(x, y + 1);
6933 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6934 /* only reset graphic animation if graphic really changes after impact */
6936 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6938 ResetGfxAnimation(x, y);
6939 TEST_DrawLevelField(x, y);
6942 if (impact && CAN_EXPLODE_IMPACT(element))
6947 else if (impact && element == EL_PEARL &&
6948 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6950 ResetGfxAnimation(x, y);
6952 Feld[x][y] = EL_PEARL_BREAKING;
6953 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6956 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6958 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6963 if (impact && element == EL_AMOEBA_DROP)
6965 if (object_hit && IS_PLAYER(x, y + 1))
6966 KillPlayerUnlessEnemyProtected(x, y + 1);
6967 else if (object_hit && smashed == EL_PENGUIN)
6971 Feld[x][y] = EL_AMOEBA_GROWING;
6972 Store[x][y] = EL_AMOEBA_WET;
6974 ResetRandomAnimationValue(x, y);
6979 if (object_hit) /* check which object was hit */
6981 if ((CAN_PASS_MAGIC_WALL(element) &&
6982 (smashed == EL_MAGIC_WALL ||
6983 smashed == EL_BD_MAGIC_WALL)) ||
6984 (CAN_PASS_DC_MAGIC_WALL(element) &&
6985 smashed == EL_DC_MAGIC_WALL))
6988 int activated_magic_wall =
6989 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6990 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6991 EL_DC_MAGIC_WALL_ACTIVE);
6993 /* activate magic wall / mill */
6994 SCAN_PLAYFIELD(xx, yy)
6996 if (Feld[xx][yy] == smashed)
6997 Feld[xx][yy] = activated_magic_wall;
7000 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7001 game.magic_wall_active = TRUE;
7003 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7004 SND_MAGIC_WALL_ACTIVATING :
7005 smashed == EL_BD_MAGIC_WALL ?
7006 SND_BD_MAGIC_WALL_ACTIVATING :
7007 SND_DC_MAGIC_WALL_ACTIVATING));
7010 if (IS_PLAYER(x, y + 1))
7012 if (CAN_SMASH_PLAYER(element))
7014 KillPlayerUnlessEnemyProtected(x, y + 1);
7018 else if (smashed == EL_PENGUIN)
7020 if (CAN_SMASH_PLAYER(element))
7026 else if (element == EL_BD_DIAMOND)
7028 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7034 else if (((element == EL_SP_INFOTRON ||
7035 element == EL_SP_ZONK) &&
7036 (smashed == EL_SP_SNIKSNAK ||
7037 smashed == EL_SP_ELECTRON ||
7038 smashed == EL_SP_DISK_ORANGE)) ||
7039 (element == EL_SP_INFOTRON &&
7040 smashed == EL_SP_DISK_YELLOW))
7045 else if (CAN_SMASH_EVERYTHING(element))
7047 if (IS_CLASSIC_ENEMY(smashed) ||
7048 CAN_EXPLODE_SMASHED(smashed))
7053 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7055 if (smashed == EL_LAMP ||
7056 smashed == EL_LAMP_ACTIVE)
7061 else if (smashed == EL_NUT)
7063 Feld[x][y + 1] = EL_NUT_BREAKING;
7064 PlayLevelSound(x, y, SND_NUT_BREAKING);
7065 RaiseScoreElement(EL_NUT);
7068 else if (smashed == EL_PEARL)
7070 ResetGfxAnimation(x, y);
7072 Feld[x][y + 1] = EL_PEARL_BREAKING;
7073 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7076 else if (smashed == EL_DIAMOND)
7078 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7079 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7082 else if (IS_BELT_SWITCH(smashed))
7084 ToggleBeltSwitch(x, y + 1);
7086 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7087 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7088 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7089 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7091 ToggleSwitchgateSwitch(x, y + 1);
7093 else if (smashed == EL_LIGHT_SWITCH ||
7094 smashed == EL_LIGHT_SWITCH_ACTIVE)
7096 ToggleLightSwitch(x, y + 1);
7101 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7104 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7106 CheckElementChangeBySide(x, y + 1, smashed, element,
7107 CE_SWITCHED, CH_SIDE_TOP);
7108 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7114 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7119 /* play sound of magic wall / mill */
7121 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7122 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7123 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7125 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7126 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7127 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7128 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7129 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7130 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7135 /* play sound of object that hits the ground */
7136 if (last_line || object_hit)
7137 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7140 inline static void TurnRoundExt(int x, int y)
7152 { 0, 0 }, { 0, 0 }, { 0, 0 },
7157 int left, right, back;
7161 { MV_DOWN, MV_UP, MV_RIGHT },
7162 { MV_UP, MV_DOWN, MV_LEFT },
7164 { MV_LEFT, MV_RIGHT, MV_DOWN },
7168 { MV_RIGHT, MV_LEFT, MV_UP }
7171 int element = Feld[x][y];
7172 int move_pattern = element_info[element].move_pattern;
7174 int old_move_dir = MovDir[x][y];
7175 int left_dir = turn[old_move_dir].left;
7176 int right_dir = turn[old_move_dir].right;
7177 int back_dir = turn[old_move_dir].back;
7179 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7180 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7181 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7182 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7184 int left_x = x + left_dx, left_y = y + left_dy;
7185 int right_x = x + right_dx, right_y = y + right_dy;
7186 int move_x = x + move_dx, move_y = y + move_dy;
7190 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7192 TestIfBadThingTouchesOtherBadThing(x, y);
7194 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7195 MovDir[x][y] = right_dir;
7196 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7197 MovDir[x][y] = left_dir;
7199 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7201 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7204 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7206 TestIfBadThingTouchesOtherBadThing(x, y);
7208 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7209 MovDir[x][y] = left_dir;
7210 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7211 MovDir[x][y] = right_dir;
7213 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7215 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7218 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7220 TestIfBadThingTouchesOtherBadThing(x, y);
7222 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7223 MovDir[x][y] = left_dir;
7224 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7225 MovDir[x][y] = right_dir;
7227 if (MovDir[x][y] != old_move_dir)
7230 else if (element == EL_YAMYAM)
7232 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7233 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7235 if (can_turn_left && can_turn_right)
7236 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7237 else if (can_turn_left)
7238 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7239 else if (can_turn_right)
7240 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7242 MovDir[x][y] = back_dir;
7244 MovDelay[x][y] = 16 + 16 * RND(3);
7246 else if (element == EL_DARK_YAMYAM)
7248 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7250 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7253 if (can_turn_left && can_turn_right)
7254 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7255 else if (can_turn_left)
7256 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7257 else if (can_turn_right)
7258 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7260 MovDir[x][y] = back_dir;
7262 MovDelay[x][y] = 16 + 16 * RND(3);
7264 else if (element == EL_PACMAN)
7266 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7267 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7269 if (can_turn_left && can_turn_right)
7270 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7271 else if (can_turn_left)
7272 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7273 else if (can_turn_right)
7274 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7276 MovDir[x][y] = back_dir;
7278 MovDelay[x][y] = 6 + RND(40);
7280 else if (element == EL_PIG)
7282 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7283 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7284 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7285 boolean should_turn_left, should_turn_right, should_move_on;
7287 int rnd = RND(rnd_value);
7289 should_turn_left = (can_turn_left &&
7291 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7292 y + back_dy + left_dy)));
7293 should_turn_right = (can_turn_right &&
7295 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7296 y + back_dy + right_dy)));
7297 should_move_on = (can_move_on &&
7300 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7301 y + move_dy + left_dy) ||
7302 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7303 y + move_dy + right_dy)));
7305 if (should_turn_left || should_turn_right || should_move_on)
7307 if (should_turn_left && should_turn_right && should_move_on)
7308 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7309 rnd < 2 * rnd_value / 3 ? right_dir :
7311 else if (should_turn_left && should_turn_right)
7312 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7313 else if (should_turn_left && should_move_on)
7314 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7315 else if (should_turn_right && should_move_on)
7316 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7317 else if (should_turn_left)
7318 MovDir[x][y] = left_dir;
7319 else if (should_turn_right)
7320 MovDir[x][y] = right_dir;
7321 else if (should_move_on)
7322 MovDir[x][y] = old_move_dir;
7324 else if (can_move_on && rnd > rnd_value / 8)
7325 MovDir[x][y] = old_move_dir;
7326 else if (can_turn_left && can_turn_right)
7327 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7328 else if (can_turn_left && rnd > rnd_value / 8)
7329 MovDir[x][y] = left_dir;
7330 else if (can_turn_right && rnd > rnd_value/8)
7331 MovDir[x][y] = right_dir;
7333 MovDir[x][y] = back_dir;
7335 xx = x + move_xy[MovDir[x][y]].dx;
7336 yy = y + move_xy[MovDir[x][y]].dy;
7338 if (!IN_LEV_FIELD(xx, yy) ||
7339 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7340 MovDir[x][y] = old_move_dir;
7344 else if (element == EL_DRAGON)
7346 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7347 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7348 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7350 int rnd = RND(rnd_value);
7352 if (can_move_on && rnd > rnd_value / 8)
7353 MovDir[x][y] = old_move_dir;
7354 else if (can_turn_left && can_turn_right)
7355 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7356 else if (can_turn_left && rnd > rnd_value / 8)
7357 MovDir[x][y] = left_dir;
7358 else if (can_turn_right && rnd > rnd_value / 8)
7359 MovDir[x][y] = right_dir;
7361 MovDir[x][y] = back_dir;
7363 xx = x + move_xy[MovDir[x][y]].dx;
7364 yy = y + move_xy[MovDir[x][y]].dy;
7366 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7367 MovDir[x][y] = old_move_dir;
7371 else if (element == EL_MOLE)
7373 boolean can_move_on =
7374 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7375 IS_AMOEBOID(Feld[move_x][move_y]) ||
7376 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7379 boolean can_turn_left =
7380 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7381 IS_AMOEBOID(Feld[left_x][left_y])));
7383 boolean can_turn_right =
7384 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7385 IS_AMOEBOID(Feld[right_x][right_y])));
7387 if (can_turn_left && can_turn_right)
7388 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7389 else if (can_turn_left)
7390 MovDir[x][y] = left_dir;
7392 MovDir[x][y] = right_dir;
7395 if (MovDir[x][y] != old_move_dir)
7398 else if (element == EL_BALLOON)
7400 MovDir[x][y] = game.wind_direction;
7403 else if (element == EL_SPRING)
7405 #if USE_NEW_SPRING_BUMPER
7406 if (MovDir[x][y] & MV_HORIZONTAL)
7408 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7409 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7411 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7412 ResetGfxAnimation(move_x, move_y);
7413 TEST_DrawLevelField(move_x, move_y);
7415 MovDir[x][y] = back_dir;
7417 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7418 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7419 MovDir[x][y] = MV_NONE;
7422 if (MovDir[x][y] & MV_HORIZONTAL &&
7423 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7424 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7425 MovDir[x][y] = MV_NONE;
7430 else if (element == EL_ROBOT ||
7431 element == EL_SATELLITE ||
7432 element == EL_PENGUIN ||
7433 element == EL_EMC_ANDROID)
7435 int attr_x = -1, attr_y = -1;
7446 for (i = 0; i < MAX_PLAYERS; i++)
7448 struct PlayerInfo *player = &stored_player[i];
7449 int jx = player->jx, jy = player->jy;
7451 if (!player->active)
7455 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7463 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7464 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7465 game.engine_version < VERSION_IDENT(3,1,0,0)))
7471 if (element == EL_PENGUIN)
7474 static int xy[4][2] =
7482 for (i = 0; i < NUM_DIRECTIONS; i++)
7484 int ex = x + xy[i][0];
7485 int ey = y + xy[i][1];
7487 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7488 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7489 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7490 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7499 MovDir[x][y] = MV_NONE;
7501 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7502 else if (attr_x > x)
7503 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7505 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7506 else if (attr_y > y)
7507 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7509 if (element == EL_ROBOT)
7513 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7514 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7515 Moving2Blocked(x, y, &newx, &newy);
7517 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7518 MovDelay[x][y] = 8 + 8 * !RND(3);
7520 MovDelay[x][y] = 16;
7522 else if (element == EL_PENGUIN)
7528 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7530 boolean first_horiz = RND(2);
7531 int new_move_dir = MovDir[x][y];
7534 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7535 Moving2Blocked(x, y, &newx, &newy);
7537 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7541 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7542 Moving2Blocked(x, y, &newx, &newy);
7544 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7547 MovDir[x][y] = old_move_dir;
7551 else if (element == EL_SATELLITE)
7557 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7559 boolean first_horiz = RND(2);
7560 int new_move_dir = MovDir[x][y];
7563 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7564 Moving2Blocked(x, y, &newx, &newy);
7566 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7570 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7571 Moving2Blocked(x, y, &newx, &newy);
7573 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7576 MovDir[x][y] = old_move_dir;
7580 else if (element == EL_EMC_ANDROID)
7582 static int check_pos[16] =
7584 -1, /* 0 => (invalid) */
7585 7, /* 1 => MV_LEFT */
7586 3, /* 2 => MV_RIGHT */
7587 -1, /* 3 => (invalid) */
7589 0, /* 5 => MV_LEFT | MV_UP */
7590 2, /* 6 => MV_RIGHT | MV_UP */
7591 -1, /* 7 => (invalid) */
7592 5, /* 8 => MV_DOWN */
7593 6, /* 9 => MV_LEFT | MV_DOWN */
7594 4, /* 10 => MV_RIGHT | MV_DOWN */
7595 -1, /* 11 => (invalid) */
7596 -1, /* 12 => (invalid) */
7597 -1, /* 13 => (invalid) */
7598 -1, /* 14 => (invalid) */
7599 -1, /* 15 => (invalid) */
7607 { -1, -1, MV_LEFT | MV_UP },
7609 { +1, -1, MV_RIGHT | MV_UP },
7610 { +1, 0, MV_RIGHT },
7611 { +1, +1, MV_RIGHT | MV_DOWN },
7613 { -1, +1, MV_LEFT | MV_DOWN },
7616 int start_pos, check_order;
7617 boolean can_clone = FALSE;
7620 /* check if there is any free field around current position */
7621 for (i = 0; i < 8; i++)
7623 int newx = x + check_xy[i].dx;
7624 int newy = y + check_xy[i].dy;
7626 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7634 if (can_clone) /* randomly find an element to clone */
7638 start_pos = check_pos[RND(8)];
7639 check_order = (RND(2) ? -1 : +1);
7641 for (i = 0; i < 8; i++)
7643 int pos_raw = start_pos + i * check_order;
7644 int pos = (pos_raw + 8) % 8;
7645 int newx = x + check_xy[pos].dx;
7646 int newy = y + check_xy[pos].dy;
7648 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7650 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7651 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7653 Store[x][y] = Feld[newx][newy];
7662 if (can_clone) /* randomly find a direction to move */
7666 start_pos = check_pos[RND(8)];
7667 check_order = (RND(2) ? -1 : +1);
7669 for (i = 0; i < 8; i++)
7671 int pos_raw = start_pos + i * check_order;
7672 int pos = (pos_raw + 8) % 8;
7673 int newx = x + check_xy[pos].dx;
7674 int newy = y + check_xy[pos].dy;
7675 int new_move_dir = check_xy[pos].dir;
7677 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7679 MovDir[x][y] = new_move_dir;
7680 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7689 if (can_clone) /* cloning and moving successful */
7692 /* cannot clone -- try to move towards player */
7694 start_pos = check_pos[MovDir[x][y] & 0x0f];
7695 check_order = (RND(2) ? -1 : +1);
7697 for (i = 0; i < 3; i++)
7699 /* first check start_pos, then previous/next or (next/previous) pos */
7700 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7701 int pos = (pos_raw + 8) % 8;
7702 int newx = x + check_xy[pos].dx;
7703 int newy = y + check_xy[pos].dy;
7704 int new_move_dir = check_xy[pos].dir;
7706 if (IS_PLAYER(newx, newy))
7709 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7711 MovDir[x][y] = new_move_dir;
7712 MovDelay[x][y] = level.android_move_time * 8 + 1;
7719 else if (move_pattern == MV_TURNING_LEFT ||
7720 move_pattern == MV_TURNING_RIGHT ||
7721 move_pattern == MV_TURNING_LEFT_RIGHT ||
7722 move_pattern == MV_TURNING_RIGHT_LEFT ||
7723 move_pattern == MV_TURNING_RANDOM ||
7724 move_pattern == MV_ALL_DIRECTIONS)
7726 boolean can_turn_left =
7727 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7728 boolean can_turn_right =
7729 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7731 if (element_info[element].move_stepsize == 0) /* "not moving" */
7734 if (move_pattern == MV_TURNING_LEFT)
7735 MovDir[x][y] = left_dir;
7736 else if (move_pattern == MV_TURNING_RIGHT)
7737 MovDir[x][y] = right_dir;
7738 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7739 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7740 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7741 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7742 else if (move_pattern == MV_TURNING_RANDOM)
7743 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7744 can_turn_right && !can_turn_left ? right_dir :
7745 RND(2) ? left_dir : right_dir);
7746 else if (can_turn_left && can_turn_right)
7747 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7748 else if (can_turn_left)
7749 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7750 else if (can_turn_right)
7751 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7753 MovDir[x][y] = back_dir;
7755 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7757 else if (move_pattern == MV_HORIZONTAL ||
7758 move_pattern == MV_VERTICAL)
7760 if (move_pattern & old_move_dir)
7761 MovDir[x][y] = back_dir;
7762 else if (move_pattern == MV_HORIZONTAL)
7763 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7764 else if (move_pattern == MV_VERTICAL)
7765 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7767 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7769 else if (move_pattern & MV_ANY_DIRECTION)
7771 MovDir[x][y] = move_pattern;
7772 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7774 else if (move_pattern & MV_WIND_DIRECTION)
7776 MovDir[x][y] = game.wind_direction;
7777 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7779 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7781 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7782 MovDir[x][y] = left_dir;
7783 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7784 MovDir[x][y] = right_dir;
7786 if (MovDir[x][y] != old_move_dir)
7787 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7789 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7791 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7792 MovDir[x][y] = right_dir;
7793 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7794 MovDir[x][y] = left_dir;
7796 if (MovDir[x][y] != old_move_dir)
7797 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7799 else if (move_pattern == MV_TOWARDS_PLAYER ||
7800 move_pattern == MV_AWAY_FROM_PLAYER)
7802 int attr_x = -1, attr_y = -1;
7804 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7815 for (i = 0; i < MAX_PLAYERS; i++)
7817 struct PlayerInfo *player = &stored_player[i];
7818 int jx = player->jx, jy = player->jy;
7820 if (!player->active)
7824 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7832 MovDir[x][y] = MV_NONE;
7834 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7835 else if (attr_x > x)
7836 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7838 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7839 else if (attr_y > y)
7840 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7842 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7844 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7846 boolean first_horiz = RND(2);
7847 int new_move_dir = MovDir[x][y];
7849 if (element_info[element].move_stepsize == 0) /* "not moving" */
7851 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7852 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7858 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7859 Moving2Blocked(x, y, &newx, &newy);
7861 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7865 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7866 Moving2Blocked(x, y, &newx, &newy);
7868 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7871 MovDir[x][y] = old_move_dir;
7874 else if (move_pattern == MV_WHEN_PUSHED ||
7875 move_pattern == MV_WHEN_DROPPED)
7877 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7878 MovDir[x][y] = MV_NONE;
7882 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7884 static int test_xy[7][2] =
7894 static int test_dir[7] =
7904 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7905 int move_preference = -1000000; /* start with very low preference */
7906 int new_move_dir = MV_NONE;
7907 int start_test = RND(4);
7910 for (i = 0; i < NUM_DIRECTIONS; i++)
7912 int move_dir = test_dir[start_test + i];
7913 int move_dir_preference;
7915 xx = x + test_xy[start_test + i][0];
7916 yy = y + test_xy[start_test + i][1];
7918 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7919 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7921 new_move_dir = move_dir;
7926 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7929 move_dir_preference = -1 * RunnerVisit[xx][yy];
7930 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7931 move_dir_preference = PlayerVisit[xx][yy];
7933 if (move_dir_preference > move_preference)
7935 /* prefer field that has not been visited for the longest time */
7936 move_preference = move_dir_preference;
7937 new_move_dir = move_dir;
7939 else if (move_dir_preference == move_preference &&
7940 move_dir == old_move_dir)
7942 /* prefer last direction when all directions are preferred equally */
7943 move_preference = move_dir_preference;
7944 new_move_dir = move_dir;
7948 MovDir[x][y] = new_move_dir;
7949 if (old_move_dir != new_move_dir)
7950 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7954 static void TurnRound(int x, int y)
7956 int direction = MovDir[x][y];
7960 GfxDir[x][y] = MovDir[x][y];
7962 if (direction != MovDir[x][y])
7966 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7968 ResetGfxFrame(x, y, FALSE);
7971 static boolean JustBeingPushed(int x, int y)
7975 for (i = 0; i < MAX_PLAYERS; i++)
7977 struct PlayerInfo *player = &stored_player[i];
7979 if (player->active && player->is_pushing && player->MovPos)
7981 int next_jx = player->jx + (player->jx - player->last_jx);
7982 int next_jy = player->jy + (player->jy - player->last_jy);
7984 if (x == next_jx && y == next_jy)
7992 void StartMoving(int x, int y)
7994 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7995 int element = Feld[x][y];
8000 if (MovDelay[x][y] == 0)
8001 GfxAction[x][y] = ACTION_DEFAULT;
8003 if (CAN_FALL(element) && y < lev_fieldy - 1)
8005 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
8006 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8007 if (JustBeingPushed(x, y))
8010 if (element == EL_QUICKSAND_FULL)
8012 if (IS_FREE(x, y + 1))
8014 InitMovingField(x, y, MV_DOWN);
8015 started_moving = TRUE;
8017 Feld[x][y] = EL_QUICKSAND_EMPTYING;
8018 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8019 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8020 Store[x][y] = EL_ROCK;
8022 Store[x][y] = EL_ROCK;
8025 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8027 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8029 if (!MovDelay[x][y])
8031 MovDelay[x][y] = TILEY + 1;
8033 ResetGfxAnimation(x, y);
8034 ResetGfxAnimation(x, y + 1);
8039 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8040 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8047 Feld[x][y] = EL_QUICKSAND_EMPTY;
8048 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8049 Store[x][y + 1] = Store[x][y];
8052 PlayLevelSoundAction(x, y, ACTION_FILLING);
8054 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8056 if (!MovDelay[x][y])
8058 MovDelay[x][y] = TILEY + 1;
8060 ResetGfxAnimation(x, y);
8061 ResetGfxAnimation(x, y + 1);
8066 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8067 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8074 Feld[x][y] = EL_QUICKSAND_EMPTY;
8075 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8076 Store[x][y + 1] = Store[x][y];
8079 PlayLevelSoundAction(x, y, ACTION_FILLING);
8082 else if (element == EL_QUICKSAND_FAST_FULL)
8084 if (IS_FREE(x, y + 1))
8086 InitMovingField(x, y, MV_DOWN);
8087 started_moving = TRUE;
8089 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8090 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8091 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8092 Store[x][y] = EL_ROCK;
8094 Store[x][y] = EL_ROCK;
8097 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8099 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8101 if (!MovDelay[x][y])
8103 MovDelay[x][y] = TILEY + 1;
8105 ResetGfxAnimation(x, y);
8106 ResetGfxAnimation(x, y + 1);
8111 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8112 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8119 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8120 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8121 Store[x][y + 1] = Store[x][y];
8124 PlayLevelSoundAction(x, y, ACTION_FILLING);
8126 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8128 if (!MovDelay[x][y])
8130 MovDelay[x][y] = TILEY + 1;
8132 ResetGfxAnimation(x, y);
8133 ResetGfxAnimation(x, y + 1);
8138 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8139 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8146 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8147 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8148 Store[x][y + 1] = Store[x][y];
8151 PlayLevelSoundAction(x, y, ACTION_FILLING);
8154 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8155 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8157 InitMovingField(x, y, MV_DOWN);
8158 started_moving = TRUE;
8160 Feld[x][y] = EL_QUICKSAND_FILLING;
8161 Store[x][y] = element;
8163 PlayLevelSoundAction(x, y, ACTION_FILLING);
8165 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8166 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8168 InitMovingField(x, y, MV_DOWN);
8169 started_moving = TRUE;
8171 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8172 Store[x][y] = element;
8174 PlayLevelSoundAction(x, y, ACTION_FILLING);
8176 else if (element == EL_MAGIC_WALL_FULL)
8178 if (IS_FREE(x, y + 1))
8180 InitMovingField(x, y, MV_DOWN);
8181 started_moving = TRUE;
8183 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8184 Store[x][y] = EL_CHANGED(Store[x][y]);
8186 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8188 if (!MovDelay[x][y])
8189 MovDelay[x][y] = TILEY / 4 + 1;
8198 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8199 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8200 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8204 else if (element == EL_BD_MAGIC_WALL_FULL)
8206 if (IS_FREE(x, y + 1))
8208 InitMovingField(x, y, MV_DOWN);
8209 started_moving = TRUE;
8211 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8212 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8214 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8216 if (!MovDelay[x][y])
8217 MovDelay[x][y] = TILEY / 4 + 1;
8226 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8227 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8228 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8232 else if (element == EL_DC_MAGIC_WALL_FULL)
8234 if (IS_FREE(x, y + 1))
8236 InitMovingField(x, y, MV_DOWN);
8237 started_moving = TRUE;
8239 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8240 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8242 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8244 if (!MovDelay[x][y])
8245 MovDelay[x][y] = TILEY / 4 + 1;
8254 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8255 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8256 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8260 else if ((CAN_PASS_MAGIC_WALL(element) &&
8261 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8262 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8263 (CAN_PASS_DC_MAGIC_WALL(element) &&
8264 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8267 InitMovingField(x, y, MV_DOWN);
8268 started_moving = TRUE;
8271 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8272 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8273 EL_DC_MAGIC_WALL_FILLING);
8274 Store[x][y] = element;
8276 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8278 SplashAcid(x, y + 1);
8280 InitMovingField(x, y, MV_DOWN);
8281 started_moving = TRUE;
8283 Store[x][y] = EL_ACID;
8286 #if USE_FIX_IMPACT_COLLISION
8287 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8288 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8290 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8291 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8293 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8294 CAN_FALL(element) && WasJustFalling[x][y] &&
8295 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8297 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8298 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8299 (Feld[x][y + 1] == EL_BLOCKED)))
8301 /* this is needed for a special case not covered by calling "Impact()"
8302 from "ContinueMoving()": if an element moves to a tile directly below
8303 another element which was just falling on that tile (which was empty
8304 in the previous frame), the falling element above would just stop
8305 instead of smashing the element below (in previous version, the above
8306 element was just checked for "moving" instead of "falling", resulting
8307 in incorrect smashes caused by horizontal movement of the above
8308 element; also, the case of the player being the element to smash was
8309 simply not covered here... :-/ ) */
8311 CheckCollision[x][y] = 0;
8312 CheckImpact[x][y] = 0;
8316 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8318 if (MovDir[x][y] == MV_NONE)
8320 InitMovingField(x, y, MV_DOWN);
8321 started_moving = TRUE;
8324 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8326 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8327 MovDir[x][y] = MV_DOWN;
8329 InitMovingField(x, y, MV_DOWN);
8330 started_moving = TRUE;
8332 else if (element == EL_AMOEBA_DROP)
8334 Feld[x][y] = EL_AMOEBA_GROWING;
8335 Store[x][y] = EL_AMOEBA_WET;
8337 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8338 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8339 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8340 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8342 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8343 (IS_FREE(x - 1, y + 1) ||
8344 Feld[x - 1][y + 1] == EL_ACID));
8345 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8346 (IS_FREE(x + 1, y + 1) ||
8347 Feld[x + 1][y + 1] == EL_ACID));
8348 boolean can_fall_any = (can_fall_left || can_fall_right);
8349 boolean can_fall_both = (can_fall_left && can_fall_right);
8350 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8352 #if USE_NEW_ALL_SLIPPERY
8353 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8355 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8356 can_fall_right = FALSE;
8357 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8358 can_fall_left = FALSE;
8359 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8360 can_fall_right = FALSE;
8361 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8362 can_fall_left = FALSE;
8364 can_fall_any = (can_fall_left || can_fall_right);
8365 can_fall_both = FALSE;
8368 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8370 if (slippery_type == SLIPPERY_ONLY_LEFT)
8371 can_fall_right = FALSE;
8372 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8373 can_fall_left = FALSE;
8374 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8375 can_fall_right = FALSE;
8376 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8377 can_fall_left = FALSE;
8379 can_fall_any = (can_fall_left || can_fall_right);
8380 can_fall_both = (can_fall_left && can_fall_right);
8384 #if USE_NEW_ALL_SLIPPERY
8386 #if USE_NEW_SP_SLIPPERY
8387 /* !!! better use the same properties as for custom elements here !!! */
8388 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8389 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8391 can_fall_right = FALSE; /* slip down on left side */
8392 can_fall_both = FALSE;
8397 #if USE_NEW_ALL_SLIPPERY
8400 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8401 can_fall_right = FALSE; /* slip down on left side */
8403 can_fall_left = !(can_fall_right = RND(2));
8405 can_fall_both = FALSE;
8410 if (game.emulation == EMU_BOULDERDASH ||
8411 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8412 can_fall_right = FALSE; /* slip down on left side */
8414 can_fall_left = !(can_fall_right = RND(2));
8416 can_fall_both = FALSE;
8422 /* if not determined otherwise, prefer left side for slipping down */
8423 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8424 started_moving = TRUE;
8428 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8430 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8433 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8434 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8435 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8436 int belt_dir = game.belt_dir[belt_nr];
8438 if ((belt_dir == MV_LEFT && left_is_free) ||
8439 (belt_dir == MV_RIGHT && right_is_free))
8441 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8443 InitMovingField(x, y, belt_dir);
8444 started_moving = TRUE;
8446 Pushed[x][y] = TRUE;
8447 Pushed[nextx][y] = TRUE;
8449 GfxAction[x][y] = ACTION_DEFAULT;
8453 MovDir[x][y] = 0; /* if element was moving, stop it */
8458 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8460 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8462 if (CAN_MOVE(element) && !started_moving)
8465 int move_pattern = element_info[element].move_pattern;
8470 if (MovDir[x][y] == MV_NONE)
8472 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8473 x, y, element, element_info[element].token_name);
8474 printf("StartMoving(): This should never happen!\n");
8479 Moving2Blocked(x, y, &newx, &newy);
8481 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8484 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8485 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8487 WasJustMoving[x][y] = 0;
8488 CheckCollision[x][y] = 0;
8490 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8492 if (Feld[x][y] != element) /* element has changed */
8496 if (!MovDelay[x][y]) /* start new movement phase */
8498 /* all objects that can change their move direction after each step
8499 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8501 if (element != EL_YAMYAM &&
8502 element != EL_DARK_YAMYAM &&
8503 element != EL_PACMAN &&
8504 !(move_pattern & MV_ANY_DIRECTION) &&
8505 move_pattern != MV_TURNING_LEFT &&
8506 move_pattern != MV_TURNING_RIGHT &&
8507 move_pattern != MV_TURNING_LEFT_RIGHT &&
8508 move_pattern != MV_TURNING_RIGHT_LEFT &&
8509 move_pattern != MV_TURNING_RANDOM)
8513 if (MovDelay[x][y] && (element == EL_BUG ||
8514 element == EL_SPACESHIP ||
8515 element == EL_SP_SNIKSNAK ||
8516 element == EL_SP_ELECTRON ||
8517 element == EL_MOLE))
8518 TEST_DrawLevelField(x, y);
8522 if (MovDelay[x][y]) /* wait some time before next movement */
8526 if (element == EL_ROBOT ||
8527 element == EL_YAMYAM ||
8528 element == EL_DARK_YAMYAM)
8530 DrawLevelElementAnimationIfNeeded(x, y, element);
8531 PlayLevelSoundAction(x, y, ACTION_WAITING);
8533 else if (element == EL_SP_ELECTRON)
8534 DrawLevelElementAnimationIfNeeded(x, y, element);
8535 else if (element == EL_DRAGON)
8538 int dir = MovDir[x][y];
8539 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8540 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8541 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8542 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8543 dir == MV_UP ? IMG_FLAMES_1_UP :
8544 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8545 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8547 GfxAction[x][y] = ACTION_ATTACKING;
8549 if (IS_PLAYER(x, y))
8550 DrawPlayerField(x, y);
8552 TEST_DrawLevelField(x, y);
8554 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8556 for (i = 1; i <= 3; i++)
8558 int xx = x + i * dx;
8559 int yy = y + i * dy;
8560 int sx = SCREENX(xx);
8561 int sy = SCREENY(yy);
8562 int flame_graphic = graphic + (i - 1);
8564 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8569 int flamed = MovingOrBlocked2Element(xx, yy);
8573 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8575 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8576 RemoveMovingField(xx, yy);
8578 RemoveField(xx, yy);
8580 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8583 RemoveMovingField(xx, yy);
8586 ChangeDelay[xx][yy] = 0;
8588 Feld[xx][yy] = EL_FLAMES;
8590 if (IN_SCR_FIELD(sx, sy))
8592 TEST_DrawLevelFieldCrumbled(xx, yy);
8593 DrawGraphic(sx, sy, flame_graphic, frame);
8598 if (Feld[xx][yy] == EL_FLAMES)
8599 Feld[xx][yy] = EL_EMPTY;
8600 TEST_DrawLevelField(xx, yy);
8605 if (MovDelay[x][y]) /* element still has to wait some time */
8607 PlayLevelSoundAction(x, y, ACTION_WAITING);
8613 /* now make next step */
8615 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8617 if (DONT_COLLIDE_WITH(element) &&
8618 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8619 !PLAYER_ENEMY_PROTECTED(newx, newy))
8621 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8626 else if (CAN_MOVE_INTO_ACID(element) &&
8627 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8628 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8629 (MovDir[x][y] == MV_DOWN ||
8630 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8632 SplashAcid(newx, newy);
8633 Store[x][y] = EL_ACID;
8635 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8637 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8638 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8639 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8640 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8643 TEST_DrawLevelField(x, y);
8645 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8646 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8647 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8649 local_player->friends_still_needed--;
8650 if (!local_player->friends_still_needed &&
8651 !local_player->GameOver && AllPlayersGone)
8652 PlayerWins(local_player);
8656 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8658 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8659 TEST_DrawLevelField(newx, newy);
8661 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8663 else if (!IS_FREE(newx, newy))
8665 GfxAction[x][y] = ACTION_WAITING;
8667 if (IS_PLAYER(x, y))
8668 DrawPlayerField(x, y);
8670 TEST_DrawLevelField(x, y);
8675 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8677 if (IS_FOOD_PIG(Feld[newx][newy]))
8679 if (IS_MOVING(newx, newy))
8680 RemoveMovingField(newx, newy);
8683 Feld[newx][newy] = EL_EMPTY;
8684 TEST_DrawLevelField(newx, newy);
8687 PlayLevelSound(x, y, SND_PIG_DIGGING);
8689 else if (!IS_FREE(newx, newy))
8691 if (IS_PLAYER(x, y))
8692 DrawPlayerField(x, y);
8694 TEST_DrawLevelField(x, y);
8699 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8701 if (Store[x][y] != EL_EMPTY)
8703 boolean can_clone = FALSE;
8706 /* check if element to clone is still there */
8707 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8709 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8717 /* cannot clone or target field not free anymore -- do not clone */
8718 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8719 Store[x][y] = EL_EMPTY;
8722 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8724 if (IS_MV_DIAGONAL(MovDir[x][y]))
8726 int diagonal_move_dir = MovDir[x][y];
8727 int stored = Store[x][y];
8728 int change_delay = 8;
8731 /* android is moving diagonally */
8733 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8735 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8736 GfxElement[x][y] = EL_EMC_ANDROID;
8737 GfxAction[x][y] = ACTION_SHRINKING;
8738 GfxDir[x][y] = diagonal_move_dir;
8739 ChangeDelay[x][y] = change_delay;
8741 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8744 DrawLevelGraphicAnimation(x, y, graphic);
8745 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8747 if (Feld[newx][newy] == EL_ACID)
8749 SplashAcid(newx, newy);
8754 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8756 Store[newx][newy] = EL_EMC_ANDROID;
8757 GfxElement[newx][newy] = EL_EMC_ANDROID;
8758 GfxAction[newx][newy] = ACTION_GROWING;
8759 GfxDir[newx][newy] = diagonal_move_dir;
8760 ChangeDelay[newx][newy] = change_delay;
8762 graphic = el_act_dir2img(GfxElement[newx][newy],
8763 GfxAction[newx][newy], GfxDir[newx][newy]);
8765 DrawLevelGraphicAnimation(newx, newy, graphic);
8766 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8772 Feld[newx][newy] = EL_EMPTY;
8773 TEST_DrawLevelField(newx, newy);
8775 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8778 else if (!IS_FREE(newx, newy))
8781 if (IS_PLAYER(x, y))
8782 DrawPlayerField(x, y);
8784 TEST_DrawLevelField(x, y);
8790 else if (IS_CUSTOM_ELEMENT(element) &&
8791 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8794 if (!DigFieldByCE(newx, newy, element))
8797 int new_element = Feld[newx][newy];
8799 if (!IS_FREE(newx, newy))
8801 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8802 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8805 /* no element can dig solid indestructible elements */
8806 if (IS_INDESTRUCTIBLE(new_element) &&
8807 !IS_DIGGABLE(new_element) &&
8808 !IS_COLLECTIBLE(new_element))
8811 if (AmoebaNr[newx][newy] &&
8812 (new_element == EL_AMOEBA_FULL ||
8813 new_element == EL_BD_AMOEBA ||
8814 new_element == EL_AMOEBA_GROWING))
8816 AmoebaCnt[AmoebaNr[newx][newy]]--;
8817 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8820 if (IS_MOVING(newx, newy))
8821 RemoveMovingField(newx, newy);
8824 RemoveField(newx, newy);
8825 TEST_DrawLevelField(newx, newy);
8828 /* if digged element was about to explode, prevent the explosion */
8829 ExplodeField[newx][newy] = EX_TYPE_NONE;
8831 PlayLevelSoundAction(x, y, action);
8834 Store[newx][newy] = EL_EMPTY;
8837 /* this makes it possible to leave the removed element again */
8838 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8839 Store[newx][newy] = new_element;
8841 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8843 int move_leave_element = element_info[element].move_leave_element;
8845 /* this makes it possible to leave the removed element again */
8846 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8847 new_element : move_leave_element);
8853 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8855 RunnerVisit[x][y] = FrameCounter;
8856 PlayerVisit[x][y] /= 8; /* expire player visit path */
8859 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8861 if (!IS_FREE(newx, newy))
8863 if (IS_PLAYER(x, y))
8864 DrawPlayerField(x, y);
8866 TEST_DrawLevelField(x, y);
8872 boolean wanna_flame = !RND(10);
8873 int dx = newx - x, dy = newy - y;
8874 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8875 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8876 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8877 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8878 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8879 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8882 IS_CLASSIC_ENEMY(element1) ||
8883 IS_CLASSIC_ENEMY(element2)) &&
8884 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8885 element1 != EL_FLAMES && element2 != EL_FLAMES)
8887 ResetGfxAnimation(x, y);
8888 GfxAction[x][y] = ACTION_ATTACKING;
8890 if (IS_PLAYER(x, y))
8891 DrawPlayerField(x, y);
8893 TEST_DrawLevelField(x, y);
8895 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8897 MovDelay[x][y] = 50;
8901 RemoveField(newx, newy);
8903 Feld[newx][newy] = EL_FLAMES;
8904 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8907 RemoveField(newx1, newy1);
8909 Feld[newx1][newy1] = EL_FLAMES;
8911 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8914 RemoveField(newx2, newy2);
8916 Feld[newx2][newy2] = EL_FLAMES;
8923 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8924 Feld[newx][newy] == EL_DIAMOND)
8926 if (IS_MOVING(newx, newy))
8927 RemoveMovingField(newx, newy);
8930 Feld[newx][newy] = EL_EMPTY;
8931 TEST_DrawLevelField(newx, newy);
8934 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8936 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8937 IS_FOOD_DARK_YAMYAM(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]]--;
8949 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8951 RemoveMovingField(newx, newy);
8954 if (IS_MOVING(newx, newy))
8956 RemoveMovingField(newx, newy);
8961 Feld[newx][newy] = EL_EMPTY;
8962 TEST_DrawLevelField(newx, newy);
8965 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8967 else if ((element == EL_PACMAN || element == EL_MOLE)
8968 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8970 if (AmoebaNr[newx][newy])
8972 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8973 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8974 Feld[newx][newy] == EL_BD_AMOEBA)
8975 AmoebaCnt[AmoebaNr[newx][newy]]--;
8978 if (element == EL_MOLE)
8980 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8981 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8983 ResetGfxAnimation(x, y);
8984 GfxAction[x][y] = ACTION_DIGGING;
8985 TEST_DrawLevelField(x, y);
8987 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8989 return; /* wait for shrinking amoeba */
8991 else /* element == EL_PACMAN */
8993 Feld[newx][newy] = EL_EMPTY;
8994 TEST_DrawLevelField(newx, newy);
8995 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8998 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8999 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9000 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9002 /* wait for shrinking amoeba to completely disappear */
9005 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9007 /* object was running against a wall */
9012 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9013 if (move_pattern & MV_ANY_DIRECTION &&
9014 move_pattern == MovDir[x][y])
9016 int blocking_element =
9017 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9019 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9022 element = Feld[x][y]; /* element might have changed */
9026 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
9027 DrawLevelElementAnimation(x, y, element);
9029 if (DONT_TOUCH(element))
9030 TestIfBadThingTouchesPlayer(x, y);
9035 InitMovingField(x, y, MovDir[x][y]);
9037 PlayLevelSoundAction(x, y, ACTION_MOVING);
9041 ContinueMoving(x, y);
9044 void ContinueMoving(int x, int y)
9046 int element = Feld[x][y];
9047 struct ElementInfo *ei = &element_info[element];
9048 int direction = MovDir[x][y];
9049 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9050 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9051 int newx = x + dx, newy = y + dy;
9052 int stored = Store[x][y];
9053 int stored_new = Store[newx][newy];
9054 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9055 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9056 boolean last_line = (newy == lev_fieldy - 1);
9058 MovPos[x][y] += getElementMoveStepsize(x, y);
9060 if (pushed_by_player) /* special case: moving object pushed by player */
9061 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9063 if (ABS(MovPos[x][y]) < TILEX)
9066 int ee = Feld[x][y];
9067 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9068 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9070 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9071 x, y, ABS(MovPos[x][y]),
9073 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9076 TEST_DrawLevelField(x, y);
9078 return; /* element is still moving */
9081 /* element reached destination field */
9083 Feld[x][y] = EL_EMPTY;
9084 Feld[newx][newy] = element;
9085 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9087 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9089 element = Feld[newx][newy] = EL_ACID;
9091 else if (element == EL_MOLE)
9093 Feld[x][y] = EL_SAND;
9095 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9097 else if (element == EL_QUICKSAND_FILLING)
9099 element = Feld[newx][newy] = get_next_element(element);
9100 Store[newx][newy] = Store[x][y];
9102 else if (element == EL_QUICKSAND_EMPTYING)
9104 Feld[x][y] = get_next_element(element);
9105 element = Feld[newx][newy] = Store[x][y];
9107 else if (element == EL_QUICKSAND_FAST_FILLING)
9109 element = Feld[newx][newy] = get_next_element(element);
9110 Store[newx][newy] = Store[x][y];
9112 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9114 Feld[x][y] = get_next_element(element);
9115 element = Feld[newx][newy] = Store[x][y];
9117 else if (element == EL_MAGIC_WALL_FILLING)
9119 element = Feld[newx][newy] = get_next_element(element);
9120 if (!game.magic_wall_active)
9121 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9122 Store[newx][newy] = Store[x][y];
9124 else if (element == EL_MAGIC_WALL_EMPTYING)
9126 Feld[x][y] = get_next_element(element);
9127 if (!game.magic_wall_active)
9128 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9129 element = Feld[newx][newy] = Store[x][y];
9131 #if USE_NEW_CUSTOM_VALUE
9132 InitField(newx, newy, FALSE);
9135 else if (element == EL_BD_MAGIC_WALL_FILLING)
9137 element = Feld[newx][newy] = get_next_element(element);
9138 if (!game.magic_wall_active)
9139 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9140 Store[newx][newy] = Store[x][y];
9142 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9144 Feld[x][y] = get_next_element(element);
9145 if (!game.magic_wall_active)
9146 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9147 element = Feld[newx][newy] = Store[x][y];
9149 #if USE_NEW_CUSTOM_VALUE
9150 InitField(newx, newy, FALSE);
9153 else if (element == EL_DC_MAGIC_WALL_FILLING)
9155 element = Feld[newx][newy] = get_next_element(element);
9156 if (!game.magic_wall_active)
9157 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9158 Store[newx][newy] = Store[x][y];
9160 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9162 Feld[x][y] = get_next_element(element);
9163 if (!game.magic_wall_active)
9164 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9165 element = Feld[newx][newy] = Store[x][y];
9167 #if USE_NEW_CUSTOM_VALUE
9168 InitField(newx, newy, FALSE);
9171 else if (element == EL_AMOEBA_DROPPING)
9173 Feld[x][y] = get_next_element(element);
9174 element = Feld[newx][newy] = Store[x][y];
9176 else if (element == EL_SOKOBAN_OBJECT)
9179 Feld[x][y] = Back[x][y];
9181 if (Back[newx][newy])
9182 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9184 Back[x][y] = Back[newx][newy] = 0;
9187 Store[x][y] = EL_EMPTY;
9192 MovDelay[newx][newy] = 0;
9194 if (CAN_CHANGE_OR_HAS_ACTION(element))
9196 /* copy element change control values to new field */
9197 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9198 ChangePage[newx][newy] = ChangePage[x][y];
9199 ChangeCount[newx][newy] = ChangeCount[x][y];
9200 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9203 #if USE_NEW_CUSTOM_VALUE
9204 CustomValue[newx][newy] = CustomValue[x][y];
9207 ChangeDelay[x][y] = 0;
9208 ChangePage[x][y] = -1;
9209 ChangeCount[x][y] = 0;
9210 ChangeEvent[x][y] = -1;
9212 #if USE_NEW_CUSTOM_VALUE
9213 CustomValue[x][y] = 0;
9216 /* copy animation control values to new field */
9217 GfxFrame[newx][newy] = GfxFrame[x][y];
9218 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9219 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9220 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9222 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9224 /* some elements can leave other elements behind after moving */
9226 if (ei->move_leave_element != EL_EMPTY &&
9227 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9228 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9230 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9231 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9232 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9235 int move_leave_element = ei->move_leave_element;
9239 /* this makes it possible to leave the removed element again */
9240 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9241 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9243 /* this makes it possible to leave the removed element again */
9244 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9245 move_leave_element = stored;
9248 /* this makes it possible to leave the removed element again */
9249 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9250 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9251 move_leave_element = stored;
9254 Feld[x][y] = move_leave_element;
9256 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9257 MovDir[x][y] = direction;
9259 InitField(x, y, FALSE);
9261 if (GFX_CRUMBLED(Feld[x][y]))
9262 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9264 if (ELEM_IS_PLAYER(move_leave_element))
9265 RelocatePlayer(x, y, move_leave_element);
9268 /* do this after checking for left-behind element */
9269 ResetGfxAnimation(x, y); /* reset animation values for old field */
9271 if (!CAN_MOVE(element) ||
9272 (CAN_FALL(element) && direction == MV_DOWN &&
9273 (element == EL_SPRING ||
9274 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9275 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9276 GfxDir[x][y] = MovDir[newx][newy] = 0;
9278 TEST_DrawLevelField(x, y);
9279 TEST_DrawLevelField(newx, newy);
9281 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9283 /* prevent pushed element from moving on in pushed direction */
9284 if (pushed_by_player && CAN_MOVE(element) &&
9285 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9286 !(element_info[element].move_pattern & direction))
9287 TurnRound(newx, newy);
9289 /* prevent elements on conveyor belt from moving on in last direction */
9290 if (pushed_by_conveyor && CAN_FALL(element) &&
9291 direction & MV_HORIZONTAL)
9292 MovDir[newx][newy] = 0;
9294 if (!pushed_by_player)
9296 int nextx = newx + dx, nexty = newy + dy;
9297 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9299 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9301 if (CAN_FALL(element) && direction == MV_DOWN)
9302 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9304 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9305 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9307 #if USE_FIX_IMPACT_COLLISION
9308 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9309 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9313 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9315 TestIfBadThingTouchesPlayer(newx, newy);
9316 TestIfBadThingTouchesFriend(newx, newy);
9318 if (!IS_CUSTOM_ELEMENT(element))
9319 TestIfBadThingTouchesOtherBadThing(newx, newy);
9321 else if (element == EL_PENGUIN)
9322 TestIfFriendTouchesBadThing(newx, newy);
9324 if (DONT_GET_HIT_BY(element))
9326 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9329 /* give the player one last chance (one more frame) to move away */
9330 if (CAN_FALL(element) && direction == MV_DOWN &&
9331 (last_line || (!IS_FREE(x, newy + 1) &&
9332 (!IS_PLAYER(x, newy + 1) ||
9333 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9336 if (pushed_by_player && !game.use_change_when_pushing_bug)
9338 int push_side = MV_DIR_OPPOSITE(direction);
9339 struct PlayerInfo *player = PLAYERINFO(x, y);
9341 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9342 player->index_bit, push_side);
9343 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9344 player->index_bit, push_side);
9347 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9348 MovDelay[newx][newy] = 1;
9350 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9352 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9355 if (ChangePage[newx][newy] != -1) /* delayed change */
9357 int page = ChangePage[newx][newy];
9358 struct ElementChangeInfo *change = &ei->change_page[page];
9360 ChangePage[newx][newy] = -1;
9362 if (change->can_change)
9364 if (ChangeElement(newx, newy, element, page))
9366 if (change->post_change_function)
9367 change->post_change_function(newx, newy);
9371 if (change->has_action)
9372 ExecuteCustomElementAction(newx, newy, element, page);
9376 TestIfElementHitsCustomElement(newx, newy, direction);
9377 TestIfPlayerTouchesCustomElement(newx, newy);
9378 TestIfElementTouchesCustomElement(newx, newy);
9380 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9381 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9382 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9383 MV_DIR_OPPOSITE(direction));
9386 int AmoebeNachbarNr(int ax, int ay)
9389 int element = Feld[ax][ay];
9391 static int xy[4][2] =
9399 for (i = 0; i < NUM_DIRECTIONS; i++)
9401 int x = ax + xy[i][0];
9402 int y = ay + xy[i][1];
9404 if (!IN_LEV_FIELD(x, y))
9407 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9408 group_nr = AmoebaNr[x][y];
9414 void AmoebenVereinigen(int ax, int ay)
9416 int i, x, y, xx, yy;
9417 int new_group_nr = AmoebaNr[ax][ay];
9418 static int xy[4][2] =
9426 if (new_group_nr == 0)
9429 for (i = 0; i < NUM_DIRECTIONS; i++)
9434 if (!IN_LEV_FIELD(x, y))
9437 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9438 Feld[x][y] == EL_BD_AMOEBA ||
9439 Feld[x][y] == EL_AMOEBA_DEAD) &&
9440 AmoebaNr[x][y] != new_group_nr)
9442 int old_group_nr = AmoebaNr[x][y];
9444 if (old_group_nr == 0)
9447 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9448 AmoebaCnt[old_group_nr] = 0;
9449 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9450 AmoebaCnt2[old_group_nr] = 0;
9452 SCAN_PLAYFIELD(xx, yy)
9454 if (AmoebaNr[xx][yy] == old_group_nr)
9455 AmoebaNr[xx][yy] = new_group_nr;
9461 void AmoebeUmwandeln(int ax, int ay)
9465 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9467 int group_nr = AmoebaNr[ax][ay];
9472 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9473 printf("AmoebeUmwandeln(): This should never happen!\n");
9478 SCAN_PLAYFIELD(x, y)
9480 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9483 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9487 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9488 SND_AMOEBA_TURNING_TO_GEM :
9489 SND_AMOEBA_TURNING_TO_ROCK));
9494 static int xy[4][2] =
9502 for (i = 0; i < NUM_DIRECTIONS; i++)
9507 if (!IN_LEV_FIELD(x, y))
9510 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9512 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9513 SND_AMOEBA_TURNING_TO_GEM :
9514 SND_AMOEBA_TURNING_TO_ROCK));
9521 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9524 int group_nr = AmoebaNr[ax][ay];
9525 boolean done = FALSE;
9530 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9531 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9536 SCAN_PLAYFIELD(x, y)
9538 if (AmoebaNr[x][y] == group_nr &&
9539 (Feld[x][y] == EL_AMOEBA_DEAD ||
9540 Feld[x][y] == EL_BD_AMOEBA ||
9541 Feld[x][y] == EL_AMOEBA_GROWING))
9544 Feld[x][y] = new_element;
9545 InitField(x, y, FALSE);
9546 TEST_DrawLevelField(x, y);
9552 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9553 SND_BD_AMOEBA_TURNING_TO_ROCK :
9554 SND_BD_AMOEBA_TURNING_TO_GEM));
9557 void AmoebeWaechst(int x, int y)
9559 static unsigned int sound_delay = 0;
9560 static unsigned int sound_delay_value = 0;
9562 if (!MovDelay[x][y]) /* start new growing cycle */
9566 if (DelayReached(&sound_delay, sound_delay_value))
9568 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9569 sound_delay_value = 30;
9573 if (MovDelay[x][y]) /* wait some time before growing bigger */
9576 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9578 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9579 6 - MovDelay[x][y]);
9581 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9584 if (!MovDelay[x][y])
9586 Feld[x][y] = Store[x][y];
9588 TEST_DrawLevelField(x, y);
9593 void AmoebaDisappearing(int x, int y)
9595 static unsigned int sound_delay = 0;
9596 static unsigned int sound_delay_value = 0;
9598 if (!MovDelay[x][y]) /* start new shrinking cycle */
9602 if (DelayReached(&sound_delay, sound_delay_value))
9603 sound_delay_value = 30;
9606 if (MovDelay[x][y]) /* wait some time before shrinking */
9609 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9611 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9612 6 - MovDelay[x][y]);
9614 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9617 if (!MovDelay[x][y])
9619 Feld[x][y] = EL_EMPTY;
9620 TEST_DrawLevelField(x, y);
9622 /* don't let mole enter this field in this cycle;
9623 (give priority to objects falling to this field from above) */
9629 void AmoebeAbleger(int ax, int ay)
9632 int element = Feld[ax][ay];
9633 int graphic = el2img(element);
9634 int newax = ax, neway = ay;
9635 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9636 static int xy[4][2] =
9644 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9646 Feld[ax][ay] = EL_AMOEBA_DEAD;
9647 TEST_DrawLevelField(ax, ay);
9651 if (IS_ANIMATED(graphic))
9652 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9654 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9655 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9657 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9660 if (MovDelay[ax][ay])
9664 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9667 int x = ax + xy[start][0];
9668 int y = ay + xy[start][1];
9670 if (!IN_LEV_FIELD(x, y))
9673 if (IS_FREE(x, y) ||
9674 CAN_GROW_INTO(Feld[x][y]) ||
9675 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9676 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9682 if (newax == ax && neway == ay)
9685 else /* normal or "filled" (BD style) amoeba */
9688 boolean waiting_for_player = FALSE;
9690 for (i = 0; i < NUM_DIRECTIONS; i++)
9692 int j = (start + i) % 4;
9693 int x = ax + xy[j][0];
9694 int y = ay + xy[j][1];
9696 if (!IN_LEV_FIELD(x, y))
9699 if (IS_FREE(x, y) ||
9700 CAN_GROW_INTO(Feld[x][y]) ||
9701 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9702 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9708 else if (IS_PLAYER(x, y))
9709 waiting_for_player = TRUE;
9712 if (newax == ax && neway == ay) /* amoeba cannot grow */
9714 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9716 Feld[ax][ay] = EL_AMOEBA_DEAD;
9717 TEST_DrawLevelField(ax, ay);
9718 AmoebaCnt[AmoebaNr[ax][ay]]--;
9720 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9722 if (element == EL_AMOEBA_FULL)
9723 AmoebeUmwandeln(ax, ay);
9724 else if (element == EL_BD_AMOEBA)
9725 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9730 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9732 /* amoeba gets larger by growing in some direction */
9734 int new_group_nr = AmoebaNr[ax][ay];
9737 if (new_group_nr == 0)
9739 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9740 printf("AmoebeAbleger(): This should never happen!\n");
9745 AmoebaNr[newax][neway] = new_group_nr;
9746 AmoebaCnt[new_group_nr]++;
9747 AmoebaCnt2[new_group_nr]++;
9749 /* if amoeba touches other amoeba(s) after growing, unify them */
9750 AmoebenVereinigen(newax, neway);
9752 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9754 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9760 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9761 (neway == lev_fieldy - 1 && newax != ax))
9763 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9764 Store[newax][neway] = element;
9766 else if (neway == ay || element == EL_EMC_DRIPPER)
9768 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9770 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9774 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9775 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9776 Store[ax][ay] = EL_AMOEBA_DROP;
9777 ContinueMoving(ax, ay);
9781 TEST_DrawLevelField(newax, neway);
9784 void Life(int ax, int ay)
9788 int element = Feld[ax][ay];
9789 int graphic = el2img(element);
9790 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9792 boolean changed = FALSE;
9794 if (IS_ANIMATED(graphic))
9795 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9800 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9801 MovDelay[ax][ay] = life_time;
9803 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9806 if (MovDelay[ax][ay])
9810 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9812 int xx = ax+x1, yy = ay+y1;
9815 if (!IN_LEV_FIELD(xx, yy))
9818 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9820 int x = xx+x2, y = yy+y2;
9822 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9825 if (((Feld[x][y] == element ||
9826 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9828 (IS_FREE(x, y) && Stop[x][y]))
9832 if (xx == ax && yy == ay) /* field in the middle */
9834 if (nachbarn < life_parameter[0] ||
9835 nachbarn > life_parameter[1])
9837 Feld[xx][yy] = EL_EMPTY;
9839 TEST_DrawLevelField(xx, yy);
9840 Stop[xx][yy] = TRUE;
9844 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9845 { /* free border field */
9846 if (nachbarn >= life_parameter[2] &&
9847 nachbarn <= life_parameter[3])
9849 Feld[xx][yy] = element;
9850 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9852 TEST_DrawLevelField(xx, yy);
9853 Stop[xx][yy] = TRUE;
9860 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9861 SND_GAME_OF_LIFE_GROWING);
9864 static void InitRobotWheel(int x, int y)
9866 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9869 static void RunRobotWheel(int x, int y)
9871 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9874 static void StopRobotWheel(int x, int y)
9876 if (ZX == x && ZY == y)
9880 game.robot_wheel_active = FALSE;
9884 static void InitTimegateWheel(int x, int y)
9886 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9889 static void RunTimegateWheel(int x, int y)
9891 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9894 static void InitMagicBallDelay(int x, int y)
9897 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9899 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9903 static void ActivateMagicBall(int bx, int by)
9907 if (level.ball_random)
9909 int pos_border = RND(8); /* select one of the eight border elements */
9910 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9911 int xx = pos_content % 3;
9912 int yy = pos_content / 3;
9917 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9918 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9922 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9924 int xx = x - bx + 1;
9925 int yy = y - by + 1;
9927 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9928 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9932 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9935 void CheckExit(int x, int y)
9937 if (local_player->gems_still_needed > 0 ||
9938 local_player->sokobanfields_still_needed > 0 ||
9939 local_player->lights_still_needed > 0)
9941 int element = Feld[x][y];
9942 int graphic = el2img(element);
9944 if (IS_ANIMATED(graphic))
9945 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9950 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9953 Feld[x][y] = EL_EXIT_OPENING;
9955 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9958 void CheckExitEM(int x, int y)
9960 if (local_player->gems_still_needed > 0 ||
9961 local_player->sokobanfields_still_needed > 0 ||
9962 local_player->lights_still_needed > 0)
9964 int element = Feld[x][y];
9965 int graphic = el2img(element);
9967 if (IS_ANIMATED(graphic))
9968 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9973 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9976 Feld[x][y] = EL_EM_EXIT_OPENING;
9978 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9981 void CheckExitSteel(int x, int y)
9983 if (local_player->gems_still_needed > 0 ||
9984 local_player->sokobanfields_still_needed > 0 ||
9985 local_player->lights_still_needed > 0)
9987 int element = Feld[x][y];
9988 int graphic = el2img(element);
9990 if (IS_ANIMATED(graphic))
9991 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9996 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9999 Feld[x][y] = EL_STEEL_EXIT_OPENING;
10001 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10004 void CheckExitSteelEM(int x, int y)
10006 if (local_player->gems_still_needed > 0 ||
10007 local_player->sokobanfields_still_needed > 0 ||
10008 local_player->lights_still_needed > 0)
10010 int element = Feld[x][y];
10011 int graphic = el2img(element);
10013 if (IS_ANIMATED(graphic))
10014 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10019 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10022 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10024 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10027 void CheckExitSP(int x, int y)
10029 if (local_player->gems_still_needed > 0)
10031 int element = Feld[x][y];
10032 int graphic = el2img(element);
10034 if (IS_ANIMATED(graphic))
10035 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10040 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10043 Feld[x][y] = EL_SP_EXIT_OPENING;
10045 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10048 static void CloseAllOpenTimegates()
10052 SCAN_PLAYFIELD(x, y)
10054 int element = Feld[x][y];
10056 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10058 Feld[x][y] = EL_TIMEGATE_CLOSING;
10060 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10065 void DrawTwinkleOnField(int x, int y)
10067 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10070 if (Feld[x][y] == EL_BD_DIAMOND)
10073 if (MovDelay[x][y] == 0) /* next animation frame */
10074 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10076 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10080 DrawLevelElementAnimation(x, y, Feld[x][y]);
10082 if (MovDelay[x][y] != 0)
10084 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10085 10 - MovDelay[x][y]);
10087 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10092 void MauerWaechst(int x, int y)
10096 if (!MovDelay[x][y]) /* next animation frame */
10097 MovDelay[x][y] = 3 * delay;
10099 if (MovDelay[x][y]) /* wait some time before next frame */
10103 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10105 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10106 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10108 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10111 if (!MovDelay[x][y])
10113 if (MovDir[x][y] == MV_LEFT)
10115 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10116 TEST_DrawLevelField(x - 1, y);
10118 else if (MovDir[x][y] == MV_RIGHT)
10120 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10121 TEST_DrawLevelField(x + 1, y);
10123 else if (MovDir[x][y] == MV_UP)
10125 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10126 TEST_DrawLevelField(x, y - 1);
10130 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10131 TEST_DrawLevelField(x, y + 1);
10134 Feld[x][y] = Store[x][y];
10136 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10137 TEST_DrawLevelField(x, y);
10142 void MauerAbleger(int ax, int ay)
10144 int element = Feld[ax][ay];
10145 int graphic = el2img(element);
10146 boolean oben_frei = FALSE, unten_frei = FALSE;
10147 boolean links_frei = FALSE, rechts_frei = FALSE;
10148 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10149 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10150 boolean new_wall = FALSE;
10152 if (IS_ANIMATED(graphic))
10153 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10155 if (!MovDelay[ax][ay]) /* start building new wall */
10156 MovDelay[ax][ay] = 6;
10158 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10160 MovDelay[ax][ay]--;
10161 if (MovDelay[ax][ay])
10165 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10167 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10169 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10171 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10172 rechts_frei = TRUE;
10174 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10175 element == EL_EXPANDABLE_WALL_ANY)
10179 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10180 Store[ax][ay-1] = element;
10181 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10182 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10183 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10184 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10189 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10190 Store[ax][ay+1] = element;
10191 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10192 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10193 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10194 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10199 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10200 element == EL_EXPANDABLE_WALL_ANY ||
10201 element == EL_EXPANDABLE_WALL ||
10202 element == EL_BD_EXPANDABLE_WALL)
10206 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10207 Store[ax-1][ay] = element;
10208 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10209 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10210 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10211 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10217 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10218 Store[ax+1][ay] = element;
10219 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10220 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10221 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10222 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10227 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10228 TEST_DrawLevelField(ax, ay);
10230 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10231 oben_massiv = TRUE;
10232 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10233 unten_massiv = TRUE;
10234 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10235 links_massiv = TRUE;
10236 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10237 rechts_massiv = TRUE;
10239 if (((oben_massiv && unten_massiv) ||
10240 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10241 element == EL_EXPANDABLE_WALL) &&
10242 ((links_massiv && rechts_massiv) ||
10243 element == EL_EXPANDABLE_WALL_VERTICAL))
10244 Feld[ax][ay] = EL_WALL;
10247 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10250 void MauerAblegerStahl(int ax, int ay)
10252 int element = Feld[ax][ay];
10253 int graphic = el2img(element);
10254 boolean oben_frei = FALSE, unten_frei = FALSE;
10255 boolean links_frei = FALSE, rechts_frei = FALSE;
10256 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10257 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10258 boolean new_wall = FALSE;
10260 if (IS_ANIMATED(graphic))
10261 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10263 if (!MovDelay[ax][ay]) /* start building new wall */
10264 MovDelay[ax][ay] = 6;
10266 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10268 MovDelay[ax][ay]--;
10269 if (MovDelay[ax][ay])
10273 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10275 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10277 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10279 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10280 rechts_frei = TRUE;
10282 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10283 element == EL_EXPANDABLE_STEELWALL_ANY)
10287 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10288 Store[ax][ay-1] = element;
10289 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10290 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10291 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10292 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10297 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10298 Store[ax][ay+1] = element;
10299 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10300 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10301 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10302 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10307 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10308 element == EL_EXPANDABLE_STEELWALL_ANY)
10312 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10313 Store[ax-1][ay] = element;
10314 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10315 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10316 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10317 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10323 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10324 Store[ax+1][ay] = element;
10325 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10326 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10327 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10328 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10333 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10334 oben_massiv = TRUE;
10335 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10336 unten_massiv = TRUE;
10337 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10338 links_massiv = TRUE;
10339 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10340 rechts_massiv = TRUE;
10342 if (((oben_massiv && unten_massiv) ||
10343 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10344 ((links_massiv && rechts_massiv) ||
10345 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10346 Feld[ax][ay] = EL_STEELWALL;
10349 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10352 void CheckForDragon(int x, int y)
10355 boolean dragon_found = FALSE;
10356 static int xy[4][2] =
10364 for (i = 0; i < NUM_DIRECTIONS; i++)
10366 for (j = 0; j < 4; j++)
10368 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10370 if (IN_LEV_FIELD(xx, yy) &&
10371 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10373 if (Feld[xx][yy] == EL_DRAGON)
10374 dragon_found = TRUE;
10383 for (i = 0; i < NUM_DIRECTIONS; i++)
10385 for (j = 0; j < 3; j++)
10387 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10389 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10391 Feld[xx][yy] = EL_EMPTY;
10392 TEST_DrawLevelField(xx, yy);
10401 static void InitBuggyBase(int x, int y)
10403 int element = Feld[x][y];
10404 int activating_delay = FRAMES_PER_SECOND / 4;
10406 ChangeDelay[x][y] =
10407 (element == EL_SP_BUGGY_BASE ?
10408 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10409 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10411 element == EL_SP_BUGGY_BASE_ACTIVE ?
10412 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10415 static void WarnBuggyBase(int x, int y)
10418 static int xy[4][2] =
10426 for (i = 0; i < NUM_DIRECTIONS; i++)
10428 int xx = x + xy[i][0];
10429 int yy = y + xy[i][1];
10431 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10433 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10440 static void InitTrap(int x, int y)
10442 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10445 static void ActivateTrap(int x, int y)
10447 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10450 static void ChangeActiveTrap(int x, int y)
10452 int graphic = IMG_TRAP_ACTIVE;
10454 /* if new animation frame was drawn, correct crumbled sand border */
10455 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10456 TEST_DrawLevelFieldCrumbled(x, y);
10459 static int getSpecialActionElement(int element, int number, int base_element)
10461 return (element != EL_EMPTY ? element :
10462 number != -1 ? base_element + number - 1 :
10466 static int getModifiedActionNumber(int value_old, int operator, int operand,
10467 int value_min, int value_max)
10469 int value_new = (operator == CA_MODE_SET ? operand :
10470 operator == CA_MODE_ADD ? value_old + operand :
10471 operator == CA_MODE_SUBTRACT ? value_old - operand :
10472 operator == CA_MODE_MULTIPLY ? value_old * operand :
10473 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10474 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10477 return (value_new < value_min ? value_min :
10478 value_new > value_max ? value_max :
10482 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10484 struct ElementInfo *ei = &element_info[element];
10485 struct ElementChangeInfo *change = &ei->change_page[page];
10486 int target_element = change->target_element;
10487 int action_type = change->action_type;
10488 int action_mode = change->action_mode;
10489 int action_arg = change->action_arg;
10490 int action_element = change->action_element;
10493 if (!change->has_action)
10496 /* ---------- determine action paramater values -------------------------- */
10498 int level_time_value =
10499 (level.time > 0 ? TimeLeft :
10502 int action_arg_element_raw =
10503 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10504 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10505 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10506 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10507 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10508 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10509 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10511 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10514 if (action_arg_element_raw == EL_GROUP_START)
10515 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10518 int action_arg_direction =
10519 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10520 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10521 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10522 change->actual_trigger_side :
10523 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10524 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10527 int action_arg_number_min =
10528 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10531 int action_arg_number_max =
10532 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10533 action_type == CA_SET_LEVEL_GEMS ? 999 :
10534 action_type == CA_SET_LEVEL_TIME ? 9999 :
10535 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10536 action_type == CA_SET_CE_VALUE ? 9999 :
10537 action_type == CA_SET_CE_SCORE ? 9999 :
10540 int action_arg_number_reset =
10541 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10542 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10543 action_type == CA_SET_LEVEL_TIME ? level.time :
10544 action_type == CA_SET_LEVEL_SCORE ? 0 :
10545 #if USE_NEW_CUSTOM_VALUE
10546 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10548 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10550 action_type == CA_SET_CE_SCORE ? 0 :
10553 int action_arg_number =
10554 (action_arg <= CA_ARG_MAX ? action_arg :
10555 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10556 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10557 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10558 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10559 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10560 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10561 #if USE_NEW_CUSTOM_VALUE
10562 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10564 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10566 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10567 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10568 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10569 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10570 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10571 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10572 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10573 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10574 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10575 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10576 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10577 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10578 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10579 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10582 int action_arg_number_old =
10583 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10584 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10585 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10586 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10587 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10590 int action_arg_number_new =
10591 getModifiedActionNumber(action_arg_number_old,
10592 action_mode, action_arg_number,
10593 action_arg_number_min, action_arg_number_max);
10596 int trigger_player_bits =
10597 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10598 change->actual_trigger_player_bits : change->trigger_player);
10600 int trigger_player_bits =
10601 (change->actual_trigger_player >= EL_PLAYER_1 &&
10602 change->actual_trigger_player <= EL_PLAYER_4 ?
10603 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10607 int action_arg_player_bits =
10608 (action_arg >= CA_ARG_PLAYER_1 &&
10609 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10610 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10611 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10614 /* ---------- execute action -------------------------------------------- */
10616 switch (action_type)
10623 /* ---------- level actions ------------------------------------------- */
10625 case CA_RESTART_LEVEL:
10627 game.restart_level = TRUE;
10632 case CA_SHOW_ENVELOPE:
10634 int element = getSpecialActionElement(action_arg_element,
10635 action_arg_number, EL_ENVELOPE_1);
10637 if (IS_ENVELOPE(element))
10638 local_player->show_envelope = element;
10643 case CA_SET_LEVEL_TIME:
10645 if (level.time > 0) /* only modify limited time value */
10647 TimeLeft = action_arg_number_new;
10650 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10652 DisplayGameControlValues();
10654 DrawGameValue_Time(TimeLeft);
10657 if (!TimeLeft && setup.time_limit)
10658 for (i = 0; i < MAX_PLAYERS; i++)
10659 KillPlayer(&stored_player[i]);
10665 case CA_SET_LEVEL_SCORE:
10667 local_player->score = action_arg_number_new;
10670 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10672 DisplayGameControlValues();
10674 DrawGameValue_Score(local_player->score);
10680 case CA_SET_LEVEL_GEMS:
10682 local_player->gems_still_needed = action_arg_number_new;
10685 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10687 DisplayGameControlValues();
10689 DrawGameValue_Emeralds(local_player->gems_still_needed);
10695 #if !USE_PLAYER_GRAVITY
10696 case CA_SET_LEVEL_GRAVITY:
10698 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10699 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10700 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10706 case CA_SET_LEVEL_WIND:
10708 game.wind_direction = action_arg_direction;
10713 case CA_SET_LEVEL_RANDOM_SEED:
10716 /* ensure that setting a new random seed while playing is predictable */
10717 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10719 InitRND(action_arg_number_new);
10723 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10731 for (i = 0; i < 9; i++)
10732 printf("%d, ", RND(2));
10740 /* ---------- player actions ------------------------------------------ */
10742 case CA_MOVE_PLAYER:
10744 /* automatically move to the next field in specified direction */
10745 for (i = 0; i < MAX_PLAYERS; i++)
10746 if (trigger_player_bits & (1 << i))
10747 stored_player[i].programmed_action = action_arg_direction;
10752 case CA_EXIT_PLAYER:
10754 for (i = 0; i < MAX_PLAYERS; i++)
10755 if (action_arg_player_bits & (1 << i))
10756 PlayerWins(&stored_player[i]);
10761 case CA_KILL_PLAYER:
10763 for (i = 0; i < MAX_PLAYERS; i++)
10764 if (action_arg_player_bits & (1 << i))
10765 KillPlayer(&stored_player[i]);
10770 case CA_SET_PLAYER_KEYS:
10772 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10773 int element = getSpecialActionElement(action_arg_element,
10774 action_arg_number, EL_KEY_1);
10776 if (IS_KEY(element))
10778 for (i = 0; i < MAX_PLAYERS; i++)
10780 if (trigger_player_bits & (1 << i))
10782 stored_player[i].key[KEY_NR(element)] = key_state;
10784 DrawGameDoorValues();
10792 case CA_SET_PLAYER_SPEED:
10795 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10798 for (i = 0; i < MAX_PLAYERS; i++)
10800 if (trigger_player_bits & (1 << i))
10802 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10804 if (action_arg == CA_ARG_SPEED_FASTER &&
10805 stored_player[i].cannot_move)
10807 action_arg_number = STEPSIZE_VERY_SLOW;
10809 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10810 action_arg == CA_ARG_SPEED_FASTER)
10812 action_arg_number = 2;
10813 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10816 else if (action_arg == CA_ARG_NUMBER_RESET)
10818 action_arg_number = level.initial_player_stepsize[i];
10822 getModifiedActionNumber(move_stepsize,
10825 action_arg_number_min,
10826 action_arg_number_max);
10828 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10835 case CA_SET_PLAYER_SHIELD:
10837 for (i = 0; i < MAX_PLAYERS; i++)
10839 if (trigger_player_bits & (1 << i))
10841 if (action_arg == CA_ARG_SHIELD_OFF)
10843 stored_player[i].shield_normal_time_left = 0;
10844 stored_player[i].shield_deadly_time_left = 0;
10846 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10848 stored_player[i].shield_normal_time_left = 999999;
10850 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10852 stored_player[i].shield_normal_time_left = 999999;
10853 stored_player[i].shield_deadly_time_left = 999999;
10861 #if USE_PLAYER_GRAVITY
10862 case CA_SET_PLAYER_GRAVITY:
10864 for (i = 0; i < MAX_PLAYERS; i++)
10866 if (trigger_player_bits & (1 << i))
10868 stored_player[i].gravity =
10869 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10870 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10871 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10872 stored_player[i].gravity);
10880 case CA_SET_PLAYER_ARTWORK:
10882 for (i = 0; i < MAX_PLAYERS; i++)
10884 if (trigger_player_bits & (1 << i))
10886 int artwork_element = action_arg_element;
10888 if (action_arg == CA_ARG_ELEMENT_RESET)
10890 (level.use_artwork_element[i] ? level.artwork_element[i] :
10891 stored_player[i].element_nr);
10893 #if USE_GFX_RESET_PLAYER_ARTWORK
10894 if (stored_player[i].artwork_element != artwork_element)
10895 stored_player[i].Frame = 0;
10898 stored_player[i].artwork_element = artwork_element;
10900 SetPlayerWaiting(&stored_player[i], FALSE);
10902 /* set number of special actions for bored and sleeping animation */
10903 stored_player[i].num_special_action_bored =
10904 get_num_special_action(artwork_element,
10905 ACTION_BORING_1, ACTION_BORING_LAST);
10906 stored_player[i].num_special_action_sleeping =
10907 get_num_special_action(artwork_element,
10908 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10915 case CA_SET_PLAYER_INVENTORY:
10917 for (i = 0; i < MAX_PLAYERS; i++)
10919 struct PlayerInfo *player = &stored_player[i];
10922 if (trigger_player_bits & (1 << i))
10924 int inventory_element = action_arg_element;
10926 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10927 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10928 action_arg == CA_ARG_ELEMENT_ACTION)
10930 int element = inventory_element;
10931 int collect_count = element_info[element].collect_count_initial;
10933 if (!IS_CUSTOM_ELEMENT(element))
10936 if (collect_count == 0)
10937 player->inventory_infinite_element = element;
10939 for (k = 0; k < collect_count; k++)
10940 if (player->inventory_size < MAX_INVENTORY_SIZE)
10941 player->inventory_element[player->inventory_size++] =
10944 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10945 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10946 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10948 if (player->inventory_infinite_element != EL_UNDEFINED &&
10949 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10950 action_arg_element_raw))
10951 player->inventory_infinite_element = EL_UNDEFINED;
10953 for (k = 0, j = 0; j < player->inventory_size; j++)
10955 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10956 action_arg_element_raw))
10957 player->inventory_element[k++] = player->inventory_element[j];
10960 player->inventory_size = k;
10962 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10964 if (player->inventory_size > 0)
10966 for (j = 0; j < player->inventory_size - 1; j++)
10967 player->inventory_element[j] = player->inventory_element[j + 1];
10969 player->inventory_size--;
10972 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10974 if (player->inventory_size > 0)
10975 player->inventory_size--;
10977 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10979 player->inventory_infinite_element = EL_UNDEFINED;
10980 player->inventory_size = 0;
10982 else if (action_arg == CA_ARG_INVENTORY_RESET)
10984 player->inventory_infinite_element = EL_UNDEFINED;
10985 player->inventory_size = 0;
10987 if (level.use_initial_inventory[i])
10989 for (j = 0; j < level.initial_inventory_size[i]; j++)
10991 int element = level.initial_inventory_content[i][j];
10992 int collect_count = element_info[element].collect_count_initial;
10994 if (!IS_CUSTOM_ELEMENT(element))
10997 if (collect_count == 0)
10998 player->inventory_infinite_element = element;
11000 for (k = 0; k < collect_count; k++)
11001 if (player->inventory_size < MAX_INVENTORY_SIZE)
11002 player->inventory_element[player->inventory_size++] =
11013 /* ---------- CE actions ---------------------------------------------- */
11015 case CA_SET_CE_VALUE:
11017 #if USE_NEW_CUSTOM_VALUE
11018 int last_ce_value = CustomValue[x][y];
11020 CustomValue[x][y] = action_arg_number_new;
11022 if (CustomValue[x][y] != last_ce_value)
11024 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11025 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11027 if (CustomValue[x][y] == 0)
11029 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11030 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11038 case CA_SET_CE_SCORE:
11040 #if USE_NEW_CUSTOM_VALUE
11041 int last_ce_score = ei->collect_score;
11043 ei->collect_score = action_arg_number_new;
11045 if (ei->collect_score != last_ce_score)
11047 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11048 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11050 if (ei->collect_score == 0)
11054 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11055 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11058 This is a very special case that seems to be a mixture between
11059 CheckElementChange() and CheckTriggeredElementChange(): while
11060 the first one only affects single elements that are triggered
11061 directly, the second one affects multiple elements in the playfield
11062 that are triggered indirectly by another element. This is a third
11063 case: Changing the CE score always affects multiple identical CEs,
11064 so every affected CE must be checked, not only the single CE for
11065 which the CE score was changed in the first place (as every instance
11066 of that CE shares the same CE score, and therefore also can change)!
11068 SCAN_PLAYFIELD(xx, yy)
11070 if (Feld[xx][yy] == element)
11071 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11072 CE_SCORE_GETS_ZERO);
11081 case CA_SET_CE_ARTWORK:
11083 int artwork_element = action_arg_element;
11084 boolean reset_frame = FALSE;
11087 if (action_arg == CA_ARG_ELEMENT_RESET)
11088 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11091 if (ei->gfx_element != artwork_element)
11092 reset_frame = TRUE;
11094 ei->gfx_element = artwork_element;
11096 SCAN_PLAYFIELD(xx, yy)
11098 if (Feld[xx][yy] == element)
11102 ResetGfxAnimation(xx, yy);
11103 ResetRandomAnimationValue(xx, yy);
11106 TEST_DrawLevelField(xx, yy);
11113 /* ---------- engine actions ------------------------------------------ */
11115 case CA_SET_ENGINE_SCAN_MODE:
11117 InitPlayfieldScanMode(action_arg);
11127 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11129 int old_element = Feld[x][y];
11130 int new_element = GetElementFromGroupElement(element);
11131 int previous_move_direction = MovDir[x][y];
11132 #if USE_NEW_CUSTOM_VALUE
11133 int last_ce_value = CustomValue[x][y];
11135 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11136 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11137 boolean add_player_onto_element = (new_element_is_player &&
11138 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11139 /* this breaks SnakeBite when a snake is
11140 halfway through a door that closes */
11141 /* NOW FIXED AT LEVEL INIT IN files.c */
11142 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11144 IS_WALKABLE(old_element));
11147 /* check if element under the player changes from accessible to unaccessible
11148 (needed for special case of dropping element which then changes) */
11149 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11150 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11158 if (!add_player_onto_element)
11160 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11161 RemoveMovingField(x, y);
11165 Feld[x][y] = new_element;
11167 #if !USE_GFX_RESET_GFX_ANIMATION
11168 ResetGfxAnimation(x, y);
11169 ResetRandomAnimationValue(x, y);
11172 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11173 MovDir[x][y] = previous_move_direction;
11175 #if USE_NEW_CUSTOM_VALUE
11176 if (element_info[new_element].use_last_ce_value)
11177 CustomValue[x][y] = last_ce_value;
11180 InitField_WithBug1(x, y, FALSE);
11182 new_element = Feld[x][y]; /* element may have changed */
11184 #if USE_GFX_RESET_GFX_ANIMATION
11185 ResetGfxAnimation(x, y);
11186 ResetRandomAnimationValue(x, y);
11189 TEST_DrawLevelField(x, y);
11191 if (GFX_CRUMBLED(new_element))
11192 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11196 /* check if element under the player changes from accessible to unaccessible
11197 (needed for special case of dropping element which then changes) */
11198 /* (must be checked after creating new element for walkable group elements) */
11199 #if USE_FIX_KILLED_BY_NON_WALKABLE
11200 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11201 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11208 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11209 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11218 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11219 if (new_element_is_player)
11220 RelocatePlayer(x, y, new_element);
11223 ChangeCount[x][y]++; /* count number of changes in the same frame */
11225 TestIfBadThingTouchesPlayer(x, y);
11226 TestIfPlayerTouchesCustomElement(x, y);
11227 TestIfElementTouchesCustomElement(x, y);
11230 static void CreateField(int x, int y, int element)
11232 CreateFieldExt(x, y, element, FALSE);
11235 static void CreateElementFromChange(int x, int y, int element)
11237 element = GET_VALID_RUNTIME_ELEMENT(element);
11239 #if USE_STOP_CHANGED_ELEMENTS
11240 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11242 int old_element = Feld[x][y];
11244 /* prevent changed element from moving in same engine frame
11245 unless both old and new element can either fall or move */
11246 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11247 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11252 CreateFieldExt(x, y, element, TRUE);
11255 static boolean ChangeElement(int x, int y, int element, int page)
11257 struct ElementInfo *ei = &element_info[element];
11258 struct ElementChangeInfo *change = &ei->change_page[page];
11259 int ce_value = CustomValue[x][y];
11260 int ce_score = ei->collect_score;
11261 int target_element;
11262 int old_element = Feld[x][y];
11264 /* always use default change event to prevent running into a loop */
11265 if (ChangeEvent[x][y] == -1)
11266 ChangeEvent[x][y] = CE_DELAY;
11268 if (ChangeEvent[x][y] == CE_DELAY)
11270 /* reset actual trigger element, trigger player and action element */
11271 change->actual_trigger_element = EL_EMPTY;
11272 change->actual_trigger_player = EL_EMPTY;
11273 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11274 change->actual_trigger_side = CH_SIDE_NONE;
11275 change->actual_trigger_ce_value = 0;
11276 change->actual_trigger_ce_score = 0;
11279 /* do not change elements more than a specified maximum number of changes */
11280 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11283 ChangeCount[x][y]++; /* count number of changes in the same frame */
11285 if (change->explode)
11292 if (change->use_target_content)
11294 boolean complete_replace = TRUE;
11295 boolean can_replace[3][3];
11298 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11301 boolean is_walkable;
11302 boolean is_diggable;
11303 boolean is_collectible;
11304 boolean is_removable;
11305 boolean is_destructible;
11306 int ex = x + xx - 1;
11307 int ey = y + yy - 1;
11308 int content_element = change->target_content.e[xx][yy];
11311 can_replace[xx][yy] = TRUE;
11313 if (ex == x && ey == y) /* do not check changing element itself */
11316 if (content_element == EL_EMPTY_SPACE)
11318 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11323 if (!IN_LEV_FIELD(ex, ey))
11325 can_replace[xx][yy] = FALSE;
11326 complete_replace = FALSE;
11333 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11334 e = MovingOrBlocked2Element(ex, ey);
11336 is_empty = (IS_FREE(ex, ey) ||
11337 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11339 is_walkable = (is_empty || IS_WALKABLE(e));
11340 is_diggable = (is_empty || IS_DIGGABLE(e));
11341 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11342 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11343 is_removable = (is_diggable || is_collectible);
11345 can_replace[xx][yy] =
11346 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11347 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11348 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11349 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11350 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11351 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11352 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11354 if (!can_replace[xx][yy])
11355 complete_replace = FALSE;
11358 if (!change->only_if_complete || complete_replace)
11360 boolean something_has_changed = FALSE;
11362 if (change->only_if_complete && change->use_random_replace &&
11363 RND(100) < change->random_percentage)
11366 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11368 int ex = x + xx - 1;
11369 int ey = y + yy - 1;
11370 int content_element;
11372 if (can_replace[xx][yy] && (!change->use_random_replace ||
11373 RND(100) < change->random_percentage))
11375 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11376 RemoveMovingField(ex, ey);
11378 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11380 content_element = change->target_content.e[xx][yy];
11381 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11382 ce_value, ce_score);
11384 CreateElementFromChange(ex, ey, target_element);
11386 something_has_changed = TRUE;
11388 /* for symmetry reasons, freeze newly created border elements */
11389 if (ex != x || ey != y)
11390 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11394 if (something_has_changed)
11396 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11397 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11403 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11404 ce_value, ce_score);
11406 if (element == EL_DIAGONAL_GROWING ||
11407 element == EL_DIAGONAL_SHRINKING)
11409 target_element = Store[x][y];
11411 Store[x][y] = EL_EMPTY;
11414 CreateElementFromChange(x, y, target_element);
11416 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11417 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11420 /* this uses direct change before indirect change */
11421 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11426 #if USE_NEW_DELAYED_ACTION
11428 static void HandleElementChange(int x, int y, int page)
11430 int element = MovingOrBlocked2Element(x, y);
11431 struct ElementInfo *ei = &element_info[element];
11432 struct ElementChangeInfo *change = &ei->change_page[page];
11433 boolean handle_action_before_change = FALSE;
11436 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11437 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11440 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11441 x, y, element, element_info[element].token_name);
11442 printf("HandleElementChange(): This should never happen!\n");
11447 /* this can happen with classic bombs on walkable, changing elements */
11448 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11451 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11452 ChangeDelay[x][y] = 0;
11458 if (ChangeDelay[x][y] == 0) /* initialize element change */
11460 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11462 if (change->can_change)
11465 /* !!! not clear why graphic animation should be reset at all here !!! */
11466 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11467 #if USE_GFX_RESET_WHEN_NOT_MOVING
11468 /* when a custom element is about to change (for example by change delay),
11469 do not reset graphic animation when the custom element is moving */
11470 if (!IS_MOVING(x, y))
11473 ResetGfxAnimation(x, y);
11474 ResetRandomAnimationValue(x, y);
11478 if (change->pre_change_function)
11479 change->pre_change_function(x, y);
11483 ChangeDelay[x][y]--;
11485 if (ChangeDelay[x][y] != 0) /* continue element change */
11487 if (change->can_change)
11489 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11491 if (IS_ANIMATED(graphic))
11492 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11494 if (change->change_function)
11495 change->change_function(x, y);
11498 else /* finish element change */
11500 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11502 page = ChangePage[x][y];
11503 ChangePage[x][y] = -1;
11505 change = &ei->change_page[page];
11508 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11510 ChangeDelay[x][y] = 1; /* try change after next move step */
11511 ChangePage[x][y] = page; /* remember page to use for change */
11517 /* special case: set new level random seed before changing element */
11518 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11519 handle_action_before_change = TRUE;
11521 if (change->has_action && handle_action_before_change)
11522 ExecuteCustomElementAction(x, y, element, page);
11525 if (change->can_change)
11527 if (ChangeElement(x, y, element, page))
11529 if (change->post_change_function)
11530 change->post_change_function(x, y);
11534 if (change->has_action && !handle_action_before_change)
11535 ExecuteCustomElementAction(x, y, element, page);
11541 static void HandleElementChange(int x, int y, int page)
11543 int element = MovingOrBlocked2Element(x, y);
11544 struct ElementInfo *ei = &element_info[element];
11545 struct ElementChangeInfo *change = &ei->change_page[page];
11548 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11551 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11552 x, y, element, element_info[element].token_name);
11553 printf("HandleElementChange(): This should never happen!\n");
11558 /* this can happen with classic bombs on walkable, changing elements */
11559 if (!CAN_CHANGE(element))
11562 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11563 ChangeDelay[x][y] = 0;
11569 if (ChangeDelay[x][y] == 0) /* initialize element change */
11571 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11573 ResetGfxAnimation(x, y);
11574 ResetRandomAnimationValue(x, y);
11576 if (change->pre_change_function)
11577 change->pre_change_function(x, y);
11580 ChangeDelay[x][y]--;
11582 if (ChangeDelay[x][y] != 0) /* continue element change */
11584 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11586 if (IS_ANIMATED(graphic))
11587 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11589 if (change->change_function)
11590 change->change_function(x, y);
11592 else /* finish element change */
11594 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11596 page = ChangePage[x][y];
11597 ChangePage[x][y] = -1;
11599 change = &ei->change_page[page];
11602 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11604 ChangeDelay[x][y] = 1; /* try change after next move step */
11605 ChangePage[x][y] = page; /* remember page to use for change */
11610 if (ChangeElement(x, y, element, page))
11612 if (change->post_change_function)
11613 change->post_change_function(x, y);
11620 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11621 int trigger_element,
11623 int trigger_player,
11627 boolean change_done_any = FALSE;
11628 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11631 if (!(trigger_events[trigger_element][trigger_event]))
11635 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11636 trigger_event, recursion_loop_depth, recursion_loop_detected,
11637 recursion_loop_element, EL_NAME(recursion_loop_element));
11640 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11642 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11644 int element = EL_CUSTOM_START + i;
11645 boolean change_done = FALSE;
11648 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11649 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11652 for (p = 0; p < element_info[element].num_change_pages; p++)
11654 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11656 if (change->can_change_or_has_action &&
11657 change->has_event[trigger_event] &&
11658 change->trigger_side & trigger_side &&
11659 change->trigger_player & trigger_player &&
11660 change->trigger_page & trigger_page_bits &&
11661 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11663 change->actual_trigger_element = trigger_element;
11664 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11665 change->actual_trigger_player_bits = trigger_player;
11666 change->actual_trigger_side = trigger_side;
11667 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11668 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11671 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11672 element, EL_NAME(element), p);
11675 if ((change->can_change && !change_done) || change->has_action)
11679 SCAN_PLAYFIELD(x, y)
11681 if (Feld[x][y] == element)
11683 if (change->can_change && !change_done)
11685 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11686 /* if element already changed in this frame, not only prevent
11687 another element change (checked in ChangeElement()), but
11688 also prevent additional element actions for this element */
11690 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11691 !level.use_action_after_change_bug)
11696 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11697 element, EL_NAME(element), p);
11700 ChangeDelay[x][y] = 1;
11701 ChangeEvent[x][y] = trigger_event;
11703 HandleElementChange(x, y, p);
11705 #if USE_NEW_DELAYED_ACTION
11706 else if (change->has_action)
11708 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11709 /* if element already changed in this frame, not only prevent
11710 another element change (checked in ChangeElement()), but
11711 also prevent additional element actions for this element */
11713 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11714 !level.use_action_after_change_bug)
11720 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11721 element, EL_NAME(element), p);
11724 ExecuteCustomElementAction(x, y, element, p);
11725 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11728 if (change->has_action)
11730 ExecuteCustomElementAction(x, y, element, p);
11731 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11737 if (change->can_change)
11739 change_done = TRUE;
11740 change_done_any = TRUE;
11743 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11744 element, EL_NAME(element), p);
11753 RECURSION_LOOP_DETECTION_END();
11755 return change_done_any;
11758 static boolean CheckElementChangeExt(int x, int y,
11760 int trigger_element,
11762 int trigger_player,
11765 boolean change_done = FALSE;
11768 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11769 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11772 if (Feld[x][y] == EL_BLOCKED)
11774 Blocked2Moving(x, y, &x, &y);
11775 element = Feld[x][y];
11779 /* check if element has already changed */
11780 if (Feld[x][y] != element)
11783 /* check if element has already changed or is about to change after moving */
11784 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11785 Feld[x][y] != element) ||
11787 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11788 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11789 ChangePage[x][y] != -1)))
11794 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11795 trigger_event, recursion_loop_depth, recursion_loop_detected,
11796 recursion_loop_element, EL_NAME(recursion_loop_element));
11799 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11802 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11805 for (p = 0; p < element_info[element].num_change_pages; p++)
11807 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11809 /* check trigger element for all events where the element that is checked
11810 for changing interacts with a directly adjacent element -- this is
11811 different to element changes that affect other elements to change on the
11812 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11813 boolean check_trigger_element =
11814 (trigger_event == CE_TOUCHING_X ||
11815 trigger_event == CE_HITTING_X ||
11816 trigger_event == CE_HIT_BY_X ||
11818 /* this one was forgotten until 3.2.3 */
11819 trigger_event == CE_DIGGING_X);
11822 if (change->can_change_or_has_action &&
11823 change->has_event[trigger_event] &&
11824 change->trigger_side & trigger_side &&
11825 change->trigger_player & trigger_player &&
11826 (!check_trigger_element ||
11827 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11829 change->actual_trigger_element = trigger_element;
11830 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11831 change->actual_trigger_player_bits = trigger_player;
11832 change->actual_trigger_side = trigger_side;
11833 change->actual_trigger_ce_value = CustomValue[x][y];
11834 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11836 /* special case: trigger element not at (x,y) position for some events */
11837 if (check_trigger_element)
11849 { 0, 0 }, { 0, 0 }, { 0, 0 },
11853 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11854 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11856 change->actual_trigger_ce_value = CustomValue[xx][yy];
11857 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11860 if (change->can_change && !change_done)
11862 ChangeDelay[x][y] = 1;
11863 ChangeEvent[x][y] = trigger_event;
11865 HandleElementChange(x, y, p);
11867 change_done = TRUE;
11869 #if USE_NEW_DELAYED_ACTION
11870 else if (change->has_action)
11872 ExecuteCustomElementAction(x, y, element, p);
11873 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11876 if (change->has_action)
11878 ExecuteCustomElementAction(x, y, element, p);
11879 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11885 RECURSION_LOOP_DETECTION_END();
11887 return change_done;
11890 static void PlayPlayerSound(struct PlayerInfo *player)
11892 int jx = player->jx, jy = player->jy;
11893 int sound_element = player->artwork_element;
11894 int last_action = player->last_action_waiting;
11895 int action = player->action_waiting;
11897 if (player->is_waiting)
11899 if (action != last_action)
11900 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11902 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11906 if (action != last_action)
11907 StopSound(element_info[sound_element].sound[last_action]);
11909 if (last_action == ACTION_SLEEPING)
11910 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11914 static void PlayAllPlayersSound()
11918 for (i = 0; i < MAX_PLAYERS; i++)
11919 if (stored_player[i].active)
11920 PlayPlayerSound(&stored_player[i]);
11923 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11925 boolean last_waiting = player->is_waiting;
11926 int move_dir = player->MovDir;
11928 player->dir_waiting = move_dir;
11929 player->last_action_waiting = player->action_waiting;
11933 if (!last_waiting) /* not waiting -> waiting */
11935 player->is_waiting = TRUE;
11937 player->frame_counter_bored =
11939 game.player_boring_delay_fixed +
11940 GetSimpleRandom(game.player_boring_delay_random);
11941 player->frame_counter_sleeping =
11943 game.player_sleeping_delay_fixed +
11944 GetSimpleRandom(game.player_sleeping_delay_random);
11946 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11949 if (game.player_sleeping_delay_fixed +
11950 game.player_sleeping_delay_random > 0 &&
11951 player->anim_delay_counter == 0 &&
11952 player->post_delay_counter == 0 &&
11953 FrameCounter >= player->frame_counter_sleeping)
11954 player->is_sleeping = TRUE;
11955 else if (game.player_boring_delay_fixed +
11956 game.player_boring_delay_random > 0 &&
11957 FrameCounter >= player->frame_counter_bored)
11958 player->is_bored = TRUE;
11960 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11961 player->is_bored ? ACTION_BORING :
11964 if (player->is_sleeping && player->use_murphy)
11966 /* special case for sleeping Murphy when leaning against non-free tile */
11968 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11969 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11970 !IS_MOVING(player->jx - 1, player->jy)))
11971 move_dir = MV_LEFT;
11972 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11973 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11974 !IS_MOVING(player->jx + 1, player->jy)))
11975 move_dir = MV_RIGHT;
11977 player->is_sleeping = FALSE;
11979 player->dir_waiting = move_dir;
11982 if (player->is_sleeping)
11984 if (player->num_special_action_sleeping > 0)
11986 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11988 int last_special_action = player->special_action_sleeping;
11989 int num_special_action = player->num_special_action_sleeping;
11990 int special_action =
11991 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11992 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11993 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11994 last_special_action + 1 : ACTION_SLEEPING);
11995 int special_graphic =
11996 el_act_dir2img(player->artwork_element, special_action, move_dir);
11998 player->anim_delay_counter =
11999 graphic_info[special_graphic].anim_delay_fixed +
12000 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12001 player->post_delay_counter =
12002 graphic_info[special_graphic].post_delay_fixed +
12003 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12005 player->special_action_sleeping = special_action;
12008 if (player->anim_delay_counter > 0)
12010 player->action_waiting = player->special_action_sleeping;
12011 player->anim_delay_counter--;
12013 else if (player->post_delay_counter > 0)
12015 player->post_delay_counter--;
12019 else if (player->is_bored)
12021 if (player->num_special_action_bored > 0)
12023 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12025 int special_action =
12026 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12027 int special_graphic =
12028 el_act_dir2img(player->artwork_element, special_action, move_dir);
12030 player->anim_delay_counter =
12031 graphic_info[special_graphic].anim_delay_fixed +
12032 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12033 player->post_delay_counter =
12034 graphic_info[special_graphic].post_delay_fixed +
12035 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12037 player->special_action_bored = special_action;
12040 if (player->anim_delay_counter > 0)
12042 player->action_waiting = player->special_action_bored;
12043 player->anim_delay_counter--;
12045 else if (player->post_delay_counter > 0)
12047 player->post_delay_counter--;
12052 else if (last_waiting) /* waiting -> not waiting */
12054 player->is_waiting = FALSE;
12055 player->is_bored = FALSE;
12056 player->is_sleeping = FALSE;
12058 player->frame_counter_bored = -1;
12059 player->frame_counter_sleeping = -1;
12061 player->anim_delay_counter = 0;
12062 player->post_delay_counter = 0;
12064 player->dir_waiting = player->MovDir;
12065 player->action_waiting = ACTION_DEFAULT;
12067 player->special_action_bored = ACTION_DEFAULT;
12068 player->special_action_sleeping = ACTION_DEFAULT;
12072 static void CheckSingleStepMode(struct PlayerInfo *player)
12074 if (tape.single_step && tape.recording && !tape.pausing)
12076 /* as it is called "single step mode", just return to pause mode when the
12077 player stopped moving after one tile (or never starts moving at all) */
12078 if (!player->is_moving && !player->is_pushing)
12080 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12081 SnapField(player, 0, 0); /* stop snapping */
12086 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12088 int left = player_action & JOY_LEFT;
12089 int right = player_action & JOY_RIGHT;
12090 int up = player_action & JOY_UP;
12091 int down = player_action & JOY_DOWN;
12092 int button1 = player_action & JOY_BUTTON_1;
12093 int button2 = player_action & JOY_BUTTON_2;
12094 int dx = (left ? -1 : right ? 1 : 0);
12095 int dy = (up ? -1 : down ? 1 : 0);
12097 if (!player->active || tape.pausing)
12103 SnapField(player, dx, dy);
12107 DropElement(player);
12109 MovePlayer(player, dx, dy);
12112 CheckSingleStepMode(player);
12114 SetPlayerWaiting(player, FALSE);
12116 return player_action;
12120 /* no actions for this player (no input at player's configured device) */
12122 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12123 SnapField(player, 0, 0);
12124 CheckGravityMovementWhenNotMoving(player);
12126 if (player->MovPos == 0)
12127 SetPlayerWaiting(player, TRUE);
12129 if (player->MovPos == 0) /* needed for tape.playing */
12130 player->is_moving = FALSE;
12132 player->is_dropping = FALSE;
12133 player->is_dropping_pressed = FALSE;
12134 player->drop_pressed_delay = 0;
12136 CheckSingleStepMode(player);
12142 static void CheckLevelTime()
12146 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12147 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12149 if (level.native_em_level->lev->home == 0) /* all players at home */
12151 PlayerWins(local_player);
12153 AllPlayersGone = TRUE;
12155 level.native_em_level->lev->home = -1;
12158 if (level.native_em_level->ply[0]->alive == 0 &&
12159 level.native_em_level->ply[1]->alive == 0 &&
12160 level.native_em_level->ply[2]->alive == 0 &&
12161 level.native_em_level->ply[3]->alive == 0) /* all dead */
12162 AllPlayersGone = TRUE;
12164 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12166 if (game_sp.LevelSolved &&
12167 !game_sp.GameOver) /* game won */
12169 PlayerWins(local_player);
12171 game_sp.GameOver = TRUE;
12173 AllPlayersGone = TRUE;
12176 if (game_sp.GameOver) /* game lost */
12177 AllPlayersGone = TRUE;
12180 if (TimeFrames >= FRAMES_PER_SECOND)
12185 for (i = 0; i < MAX_PLAYERS; i++)
12187 struct PlayerInfo *player = &stored_player[i];
12189 if (SHIELD_ON(player))
12191 player->shield_normal_time_left--;
12193 if (player->shield_deadly_time_left > 0)
12194 player->shield_deadly_time_left--;
12198 if (!local_player->LevelSolved && !level.use_step_counter)
12206 if (TimeLeft <= 10 && setup.time_limit)
12207 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12210 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12211 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12213 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12215 /* (already called by UpdateAndDisplayGameControlValues() below) */
12216 // DisplayGameControlValues();
12218 DrawGameValue_Time(TimeLeft);
12221 if (!TimeLeft && setup.time_limit)
12223 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12224 level.native_em_level->lev->killed_out_of_time = TRUE;
12226 for (i = 0; i < MAX_PLAYERS; i++)
12227 KillPlayer(&stored_player[i]);
12231 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12233 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12235 /* (already called by UpdateAndDisplayGameControlValues() below) */
12236 // DisplayGameControlValues();
12239 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12240 DrawGameValue_Time(TimePlayed);
12243 level.native_em_level->lev->time =
12244 (game.no_time_limit ? TimePlayed : TimeLeft);
12247 if (tape.recording || tape.playing)
12248 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12252 UpdateAndDisplayGameControlValues();
12254 UpdateGameDoorValues();
12255 DrawGameDoorValues();
12259 void AdvanceFrameAndPlayerCounters(int player_nr)
12263 /* advance frame counters (global frame counter and time frame counter) */
12267 /* advance player counters (counters for move delay, move animation etc.) */
12268 for (i = 0; i < MAX_PLAYERS; i++)
12270 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12271 int move_delay_value = stored_player[i].move_delay_value;
12272 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12274 if (!advance_player_counters) /* not all players may be affected */
12277 #if USE_NEW_PLAYER_ANIM
12278 if (move_frames == 0) /* less than one move per game frame */
12280 int stepsize = TILEX / move_delay_value;
12281 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12282 int count = (stored_player[i].is_moving ?
12283 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12285 if (count % delay == 0)
12290 stored_player[i].Frame += move_frames;
12292 if (stored_player[i].MovPos != 0)
12293 stored_player[i].StepFrame += move_frames;
12295 if (stored_player[i].move_delay > 0)
12296 stored_player[i].move_delay--;
12298 /* due to bugs in previous versions, counter must count up, not down */
12299 if (stored_player[i].push_delay != -1)
12300 stored_player[i].push_delay++;
12302 if (stored_player[i].drop_delay > 0)
12303 stored_player[i].drop_delay--;
12305 if (stored_player[i].is_dropping_pressed)
12306 stored_player[i].drop_pressed_delay++;
12310 void StartGameActions(boolean init_network_game, boolean record_tape,
12313 unsigned int new_random_seed = InitRND(random_seed);
12316 TapeStartRecording(new_random_seed);
12318 #if defined(NETWORK_AVALIABLE)
12319 if (init_network_game)
12321 SendToServer_StartPlaying();
12332 static unsigned int game_frame_delay = 0;
12333 unsigned int game_frame_delay_value;
12334 byte *recorded_player_action;
12335 byte summarized_player_action = 0;
12336 byte tape_action[MAX_PLAYERS];
12339 /* detect endless loops, caused by custom element programming */
12340 if (recursion_loop_detected && recursion_loop_depth == 0)
12342 char *message = getStringCat3("Internal Error! Element ",
12343 EL_NAME(recursion_loop_element),
12344 " caused endless loop! Quit the game?");
12346 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12347 EL_NAME(recursion_loop_element));
12349 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12351 recursion_loop_detected = FALSE; /* if game should be continued */
12358 if (game.restart_level)
12359 StartGameActions(options.network, setup.autorecord, level.random_seed);
12361 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12362 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12364 if (level.native_em_level->lev->home == 0) /* all players at home */
12366 PlayerWins(local_player);
12368 AllPlayersGone = TRUE;
12370 level.native_em_level->lev->home = -1;
12373 if (level.native_em_level->ply[0]->alive == 0 &&
12374 level.native_em_level->ply[1]->alive == 0 &&
12375 level.native_em_level->ply[2]->alive == 0 &&
12376 level.native_em_level->ply[3]->alive == 0) /* all dead */
12377 AllPlayersGone = TRUE;
12379 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12381 if (game_sp.LevelSolved &&
12382 !game_sp.GameOver) /* game won */
12384 PlayerWins(local_player);
12386 game_sp.GameOver = TRUE;
12388 AllPlayersGone = TRUE;
12391 if (game_sp.GameOver) /* game lost */
12392 AllPlayersGone = TRUE;
12395 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12398 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12401 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12404 game_frame_delay_value =
12405 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12407 if (tape.playing && tape.warp_forward && !tape.pausing)
12408 game_frame_delay_value = 0;
12410 /* ---------- main game synchronization point ---------- */
12412 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12414 if (network_playing && !network_player_action_received)
12416 /* try to get network player actions in time */
12418 #if defined(NETWORK_AVALIABLE)
12419 /* last chance to get network player actions without main loop delay */
12420 HandleNetworking();
12423 /* game was quit by network peer */
12424 if (game_status != GAME_MODE_PLAYING)
12427 if (!network_player_action_received)
12428 return; /* failed to get network player actions in time */
12430 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12436 /* at this point we know that we really continue executing the game */
12438 network_player_action_received = FALSE;
12440 /* when playing tape, read previously recorded player input from tape data */
12441 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12444 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12449 if (tape.set_centered_player)
12451 game.centered_player_nr_next = tape.centered_player_nr_next;
12452 game.set_centered_player = TRUE;
12455 for (i = 0; i < MAX_PLAYERS; i++)
12457 summarized_player_action |= stored_player[i].action;
12460 if (!network_playing && (game.team_mode || tape.playing))
12461 stored_player[i].effective_action = stored_player[i].action;
12463 if (!network_playing)
12464 stored_player[i].effective_action = stored_player[i].action;
12468 #if defined(NETWORK_AVALIABLE)
12469 if (network_playing)
12470 SendToServer_MovePlayer(summarized_player_action);
12473 if (!options.network && !game.team_mode)
12474 local_player->effective_action = summarized_player_action;
12476 if (tape.recording &&
12478 setup.input_on_focus &&
12479 game.centered_player_nr != -1)
12481 for (i = 0; i < MAX_PLAYERS; i++)
12482 stored_player[i].effective_action =
12483 (i == game.centered_player_nr ? summarized_player_action : 0);
12486 if (recorded_player_action != NULL)
12487 for (i = 0; i < MAX_PLAYERS; i++)
12488 stored_player[i].effective_action = recorded_player_action[i];
12490 for (i = 0; i < MAX_PLAYERS; i++)
12492 tape_action[i] = stored_player[i].effective_action;
12495 /* (this may happen in the RND game engine if a player was not present on
12496 the playfield on level start, but appeared later from a custom element */
12497 if (tape.recording &&
12500 !tape.player_participates[i])
12501 tape.player_participates[i] = TRUE;
12503 /* (this can only happen in the R'n'D game engine) */
12504 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12505 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12509 /* only record actions from input devices, but not programmed actions */
12510 if (tape.recording)
12511 TapeRecordAction(tape_action);
12513 #if USE_NEW_PLAYER_ASSIGNMENTS
12515 if (game.team_mode)
12518 byte mapped_action[MAX_PLAYERS];
12520 #if DEBUG_PLAYER_ACTIONS
12522 for (i = 0; i < MAX_PLAYERS; i++)
12523 printf(" %d, ", stored_player[i].effective_action);
12526 for (i = 0; i < MAX_PLAYERS; i++)
12527 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12529 for (i = 0; i < MAX_PLAYERS; i++)
12530 stored_player[i].effective_action = mapped_action[i];
12532 #if DEBUG_PLAYER_ACTIONS
12534 for (i = 0; i < MAX_PLAYERS; i++)
12535 printf(" %d, ", stored_player[i].effective_action);
12539 #if DEBUG_PLAYER_ACTIONS
12543 for (i = 0; i < MAX_PLAYERS; i++)
12544 printf(" %d, ", stored_player[i].effective_action);
12551 printf("::: summarized_player_action == %d\n",
12552 local_player->effective_action);
12559 #if DEBUG_INIT_PLAYER
12562 printf("Player status (final):\n");
12564 for (i = 0; i < MAX_PLAYERS; i++)
12566 struct PlayerInfo *player = &stored_player[i];
12568 printf("- player %d: present == %d, connected == %d, active == %d",
12574 if (local_player == player)
12575 printf(" (local player)");
12585 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12587 GameActions_EM_Main();
12589 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12591 GameActions_SP_Main();
12599 void GameActions_EM_Main()
12601 byte effective_action[MAX_PLAYERS];
12602 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12605 for (i = 0; i < MAX_PLAYERS; i++)
12606 effective_action[i] = stored_player[i].effective_action;
12608 GameActions_EM(effective_action, warp_mode);
12612 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12615 void GameActions_SP_Main()
12617 byte effective_action[MAX_PLAYERS];
12618 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12621 for (i = 0; i < MAX_PLAYERS; i++)
12622 effective_action[i] = stored_player[i].effective_action;
12624 GameActions_SP(effective_action, warp_mode);
12628 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12631 void GameActions_RND()
12633 int magic_wall_x = 0, magic_wall_y = 0;
12634 int i, x, y, element, graphic;
12636 InitPlayfieldScanModeVars();
12638 #if USE_ONE_MORE_CHANGE_PER_FRAME
12639 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12641 SCAN_PLAYFIELD(x, y)
12643 ChangeCount[x][y] = 0;
12644 ChangeEvent[x][y] = -1;
12649 if (game.set_centered_player)
12651 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12653 /* switching to "all players" only possible if all players fit to screen */
12654 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12656 game.centered_player_nr_next = game.centered_player_nr;
12657 game.set_centered_player = FALSE;
12660 /* do not switch focus to non-existing (or non-active) player */
12661 if (game.centered_player_nr_next >= 0 &&
12662 !stored_player[game.centered_player_nr_next].active)
12664 game.centered_player_nr_next = game.centered_player_nr;
12665 game.set_centered_player = FALSE;
12669 if (game.set_centered_player &&
12670 ScreenMovPos == 0) /* screen currently aligned at tile position */
12674 if (game.centered_player_nr_next == -1)
12676 setScreenCenteredToAllPlayers(&sx, &sy);
12680 sx = stored_player[game.centered_player_nr_next].jx;
12681 sy = stored_player[game.centered_player_nr_next].jy;
12684 game.centered_player_nr = game.centered_player_nr_next;
12685 game.set_centered_player = FALSE;
12687 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12688 DrawGameDoorValues();
12691 for (i = 0; i < MAX_PLAYERS; i++)
12693 int actual_player_action = stored_player[i].effective_action;
12696 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12697 - rnd_equinox_tetrachloride 048
12698 - rnd_equinox_tetrachloride_ii 096
12699 - rnd_emanuel_schmieg 002
12700 - doctor_sloan_ww 001, 020
12702 if (stored_player[i].MovPos == 0)
12703 CheckGravityMovement(&stored_player[i]);
12706 /* overwrite programmed action with tape action */
12707 if (stored_player[i].programmed_action)
12708 actual_player_action = stored_player[i].programmed_action;
12710 PlayerActions(&stored_player[i], actual_player_action);
12712 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12715 ScrollScreen(NULL, SCROLL_GO_ON);
12717 /* for backwards compatibility, the following code emulates a fixed bug that
12718 occured when pushing elements (causing elements that just made their last
12719 pushing step to already (if possible) make their first falling step in the
12720 same game frame, which is bad); this code is also needed to use the famous
12721 "spring push bug" which is used in older levels and might be wanted to be
12722 used also in newer levels, but in this case the buggy pushing code is only
12723 affecting the "spring" element and no other elements */
12725 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12727 for (i = 0; i < MAX_PLAYERS; i++)
12729 struct PlayerInfo *player = &stored_player[i];
12730 int x = player->jx;
12731 int y = player->jy;
12733 if (player->active && player->is_pushing && player->is_moving &&
12735 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12736 Feld[x][y] == EL_SPRING))
12738 ContinueMoving(x, y);
12740 /* continue moving after pushing (this is actually a bug) */
12741 if (!IS_MOVING(x, y))
12742 Stop[x][y] = FALSE;
12748 debug_print_timestamp(0, "start main loop profiling");
12751 SCAN_PLAYFIELD(x, y)
12753 ChangeCount[x][y] = 0;
12754 ChangeEvent[x][y] = -1;
12756 /* this must be handled before main playfield loop */
12757 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12760 if (MovDelay[x][y] <= 0)
12764 #if USE_NEW_SNAP_DELAY
12765 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12768 if (MovDelay[x][y] <= 0)
12771 TEST_DrawLevelField(x, y);
12773 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12779 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12781 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12782 printf("GameActions(): This should never happen!\n");
12784 ChangePage[x][y] = -1;
12788 Stop[x][y] = FALSE;
12789 if (WasJustMoving[x][y] > 0)
12790 WasJustMoving[x][y]--;
12791 if (WasJustFalling[x][y] > 0)
12792 WasJustFalling[x][y]--;
12793 if (CheckCollision[x][y] > 0)
12794 CheckCollision[x][y]--;
12795 if (CheckImpact[x][y] > 0)
12796 CheckImpact[x][y]--;
12800 /* reset finished pushing action (not done in ContinueMoving() to allow
12801 continuous pushing animation for elements with zero push delay) */
12802 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12804 ResetGfxAnimation(x, y);
12805 TEST_DrawLevelField(x, y);
12809 if (IS_BLOCKED(x, y))
12813 Blocked2Moving(x, y, &oldx, &oldy);
12814 if (!IS_MOVING(oldx, oldy))
12816 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12817 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12818 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12819 printf("GameActions(): This should never happen!\n");
12826 debug_print_timestamp(0, "- time for pre-main loop:");
12829 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12830 SCAN_PLAYFIELD(x, y)
12832 element = Feld[x][y];
12833 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12838 int element2 = element;
12839 int graphic2 = graphic;
12841 int element2 = Feld[x][y];
12842 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12844 int last_gfx_frame = GfxFrame[x][y];
12846 if (graphic_info[graphic2].anim_global_sync)
12847 GfxFrame[x][y] = FrameCounter;
12848 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12849 GfxFrame[x][y] = CustomValue[x][y];
12850 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12851 GfxFrame[x][y] = element_info[element2].collect_score;
12852 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12853 GfxFrame[x][y] = ChangeDelay[x][y];
12855 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12856 DrawLevelGraphicAnimation(x, y, graphic2);
12859 ResetGfxFrame(x, y, TRUE);
12863 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12864 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12865 ResetRandomAnimationValue(x, y);
12869 SetRandomAnimationValue(x, y);
12873 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12876 #endif // -------------------- !!! TEST ONLY !!! --------------------
12879 debug_print_timestamp(0, "- time for TEST loop: -->");
12882 SCAN_PLAYFIELD(x, y)
12884 element = Feld[x][y];
12885 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12887 ResetGfxFrame(x, y, TRUE);
12889 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12890 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12891 ResetRandomAnimationValue(x, y);
12893 SetRandomAnimationValue(x, y);
12895 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12897 if (IS_INACTIVE(element))
12899 if (IS_ANIMATED(graphic))
12900 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12905 /* this may take place after moving, so 'element' may have changed */
12906 if (IS_CHANGING(x, y) &&
12907 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12909 int page = element_info[element].event_page_nr[CE_DELAY];
12912 HandleElementChange(x, y, page);
12914 if (CAN_CHANGE(element))
12915 HandleElementChange(x, y, page);
12917 if (HAS_ACTION(element))
12918 ExecuteCustomElementAction(x, y, element, page);
12921 element = Feld[x][y];
12922 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12925 #if 0 // ---------------------------------------------------------------------
12927 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12931 element = Feld[x][y];
12932 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12934 if (IS_ANIMATED(graphic) &&
12935 !IS_MOVING(x, y) &&
12937 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12939 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12940 TEST_DrawTwinkleOnField(x, y);
12942 else if (IS_MOVING(x, y))
12943 ContinueMoving(x, y);
12950 case EL_EM_EXIT_OPEN:
12951 case EL_SP_EXIT_OPEN:
12952 case EL_STEEL_EXIT_OPEN:
12953 case EL_EM_STEEL_EXIT_OPEN:
12954 case EL_SP_TERMINAL:
12955 case EL_SP_TERMINAL_ACTIVE:
12956 case EL_EXTRA_TIME:
12957 case EL_SHIELD_NORMAL:
12958 case EL_SHIELD_DEADLY:
12959 if (IS_ANIMATED(graphic))
12960 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12963 case EL_DYNAMITE_ACTIVE:
12964 case EL_EM_DYNAMITE_ACTIVE:
12965 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12966 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12967 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12968 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12969 case EL_SP_DISK_RED_ACTIVE:
12970 CheckDynamite(x, y);
12973 case EL_AMOEBA_GROWING:
12974 AmoebeWaechst(x, y);
12977 case EL_AMOEBA_SHRINKING:
12978 AmoebaDisappearing(x, y);
12981 #if !USE_NEW_AMOEBA_CODE
12982 case EL_AMOEBA_WET:
12983 case EL_AMOEBA_DRY:
12984 case EL_AMOEBA_FULL:
12986 case EL_EMC_DRIPPER:
12987 AmoebeAbleger(x, y);
12991 case EL_GAME_OF_LIFE:
12996 case EL_EXIT_CLOSED:
13000 case EL_EM_EXIT_CLOSED:
13004 case EL_STEEL_EXIT_CLOSED:
13005 CheckExitSteel(x, y);
13008 case EL_EM_STEEL_EXIT_CLOSED:
13009 CheckExitSteelEM(x, y);
13012 case EL_SP_EXIT_CLOSED:
13016 case EL_EXPANDABLE_WALL_GROWING:
13017 case EL_EXPANDABLE_STEELWALL_GROWING:
13018 MauerWaechst(x, y);
13021 case EL_EXPANDABLE_WALL:
13022 case EL_EXPANDABLE_WALL_HORIZONTAL:
13023 case EL_EXPANDABLE_WALL_VERTICAL:
13024 case EL_EXPANDABLE_WALL_ANY:
13025 case EL_BD_EXPANDABLE_WALL:
13026 MauerAbleger(x, y);
13029 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
13030 case EL_EXPANDABLE_STEELWALL_VERTICAL:
13031 case EL_EXPANDABLE_STEELWALL_ANY:
13032 MauerAblegerStahl(x, y);
13036 CheckForDragon(x, y);
13042 case EL_ELEMENT_SNAPPING:
13043 case EL_DIAGONAL_SHRINKING:
13044 case EL_DIAGONAL_GROWING:
13047 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13049 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13054 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13055 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13060 #else // ---------------------------------------------------------------------
13062 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13066 element = Feld[x][y];
13067 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13069 if (IS_ANIMATED(graphic) &&
13070 !IS_MOVING(x, y) &&
13072 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13074 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13075 TEST_DrawTwinkleOnField(x, y);
13077 else if ((element == EL_ACID ||
13078 element == EL_EXIT_OPEN ||
13079 element == EL_EM_EXIT_OPEN ||
13080 element == EL_SP_EXIT_OPEN ||
13081 element == EL_STEEL_EXIT_OPEN ||
13082 element == EL_EM_STEEL_EXIT_OPEN ||
13083 element == EL_SP_TERMINAL ||
13084 element == EL_SP_TERMINAL_ACTIVE ||
13085 element == EL_EXTRA_TIME ||
13086 element == EL_SHIELD_NORMAL ||
13087 element == EL_SHIELD_DEADLY) &&
13088 IS_ANIMATED(graphic))
13089 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13090 else if (IS_MOVING(x, y))
13091 ContinueMoving(x, y);
13092 else if (IS_ACTIVE_BOMB(element))
13093 CheckDynamite(x, y);
13094 else if (element == EL_AMOEBA_GROWING)
13095 AmoebeWaechst(x, y);
13096 else if (element == EL_AMOEBA_SHRINKING)
13097 AmoebaDisappearing(x, y);
13099 #if !USE_NEW_AMOEBA_CODE
13100 else if (IS_AMOEBALIVE(element))
13101 AmoebeAbleger(x, y);
13104 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13106 else if (element == EL_EXIT_CLOSED)
13108 else if (element == EL_EM_EXIT_CLOSED)
13110 else if (element == EL_STEEL_EXIT_CLOSED)
13111 CheckExitSteel(x, y);
13112 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13113 CheckExitSteelEM(x, y);
13114 else if (element == EL_SP_EXIT_CLOSED)
13116 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13117 element == EL_EXPANDABLE_STEELWALL_GROWING)
13118 MauerWaechst(x, y);
13119 else if (element == EL_EXPANDABLE_WALL ||
13120 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13121 element == EL_EXPANDABLE_WALL_VERTICAL ||
13122 element == EL_EXPANDABLE_WALL_ANY ||
13123 element == EL_BD_EXPANDABLE_WALL)
13124 MauerAbleger(x, y);
13125 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13126 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13127 element == EL_EXPANDABLE_STEELWALL_ANY)
13128 MauerAblegerStahl(x, y);
13129 else if (element == EL_FLAMES)
13130 CheckForDragon(x, y);
13131 else if (element == EL_EXPLOSION)
13132 ; /* drawing of correct explosion animation is handled separately */
13133 else if (element == EL_ELEMENT_SNAPPING ||
13134 element == EL_DIAGONAL_SHRINKING ||
13135 element == EL_DIAGONAL_GROWING)
13137 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13139 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13141 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13142 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13144 #endif // ---------------------------------------------------------------------
13146 if (IS_BELT_ACTIVE(element))
13147 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13149 if (game.magic_wall_active)
13151 int jx = local_player->jx, jy = local_player->jy;
13153 /* play the element sound at the position nearest to the player */
13154 if ((element == EL_MAGIC_WALL_FULL ||
13155 element == EL_MAGIC_WALL_ACTIVE ||
13156 element == EL_MAGIC_WALL_EMPTYING ||
13157 element == EL_BD_MAGIC_WALL_FULL ||
13158 element == EL_BD_MAGIC_WALL_ACTIVE ||
13159 element == EL_BD_MAGIC_WALL_EMPTYING ||
13160 element == EL_DC_MAGIC_WALL_FULL ||
13161 element == EL_DC_MAGIC_WALL_ACTIVE ||
13162 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13163 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13172 debug_print_timestamp(0, "- time for MAIN loop: -->");
13175 #if USE_NEW_AMOEBA_CODE
13176 /* new experimental amoeba growth stuff */
13177 if (!(FrameCounter % 8))
13179 static unsigned int random = 1684108901;
13181 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13183 x = RND(lev_fieldx);
13184 y = RND(lev_fieldy);
13185 element = Feld[x][y];
13187 if (!IS_PLAYER(x,y) &&
13188 (element == EL_EMPTY ||
13189 CAN_GROW_INTO(element) ||
13190 element == EL_QUICKSAND_EMPTY ||
13191 element == EL_QUICKSAND_FAST_EMPTY ||
13192 element == EL_ACID_SPLASH_LEFT ||
13193 element == EL_ACID_SPLASH_RIGHT))
13195 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13196 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13197 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13198 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13199 Feld[x][y] = EL_AMOEBA_DROP;
13202 random = random * 129 + 1;
13208 if (game.explosions_delayed)
13211 game.explosions_delayed = FALSE;
13213 SCAN_PLAYFIELD(x, y)
13215 element = Feld[x][y];
13217 if (ExplodeField[x][y])
13218 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13219 else if (element == EL_EXPLOSION)
13220 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13222 ExplodeField[x][y] = EX_TYPE_NONE;
13225 game.explosions_delayed = TRUE;
13228 if (game.magic_wall_active)
13230 if (!(game.magic_wall_time_left % 4))
13232 int element = Feld[magic_wall_x][magic_wall_y];
13234 if (element == EL_BD_MAGIC_WALL_FULL ||
13235 element == EL_BD_MAGIC_WALL_ACTIVE ||
13236 element == EL_BD_MAGIC_WALL_EMPTYING)
13237 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13238 else if (element == EL_DC_MAGIC_WALL_FULL ||
13239 element == EL_DC_MAGIC_WALL_ACTIVE ||
13240 element == EL_DC_MAGIC_WALL_EMPTYING)
13241 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13243 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13246 if (game.magic_wall_time_left > 0)
13248 game.magic_wall_time_left--;
13250 if (!game.magic_wall_time_left)
13252 SCAN_PLAYFIELD(x, y)
13254 element = Feld[x][y];
13256 if (element == EL_MAGIC_WALL_ACTIVE ||
13257 element == EL_MAGIC_WALL_FULL)
13259 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13260 TEST_DrawLevelField(x, y);
13262 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13263 element == EL_BD_MAGIC_WALL_FULL)
13265 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13266 TEST_DrawLevelField(x, y);
13268 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13269 element == EL_DC_MAGIC_WALL_FULL)
13271 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13272 TEST_DrawLevelField(x, y);
13276 game.magic_wall_active = FALSE;
13281 if (game.light_time_left > 0)
13283 game.light_time_left--;
13285 if (game.light_time_left == 0)
13286 RedrawAllLightSwitchesAndInvisibleElements();
13289 if (game.timegate_time_left > 0)
13291 game.timegate_time_left--;
13293 if (game.timegate_time_left == 0)
13294 CloseAllOpenTimegates();
13297 if (game.lenses_time_left > 0)
13299 game.lenses_time_left--;
13301 if (game.lenses_time_left == 0)
13302 RedrawAllInvisibleElementsForLenses();
13305 if (game.magnify_time_left > 0)
13307 game.magnify_time_left--;
13309 if (game.magnify_time_left == 0)
13310 RedrawAllInvisibleElementsForMagnifier();
13313 for (i = 0; i < MAX_PLAYERS; i++)
13315 struct PlayerInfo *player = &stored_player[i];
13317 if (SHIELD_ON(player))
13319 if (player->shield_deadly_time_left)
13320 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13321 else if (player->shield_normal_time_left)
13322 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13326 #if USE_DELAYED_GFX_REDRAW
13327 SCAN_PLAYFIELD(x, y)
13330 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13332 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13333 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13336 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13337 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13339 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13340 DrawLevelField(x, y);
13342 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13343 DrawLevelFieldCrumbled(x, y);
13345 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13346 DrawLevelFieldCrumbledNeighbours(x, y);
13348 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13349 DrawTwinkleOnField(x, y);
13352 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13359 PlayAllPlayersSound();
13361 if (options.debug) /* calculate frames per second */
13363 static unsigned int fps_counter = 0;
13364 static int fps_frames = 0;
13365 unsigned int fps_delay_ms = Counter() - fps_counter;
13369 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13371 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13374 fps_counter = Counter();
13377 redraw_mask |= REDRAW_FPS;
13380 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13382 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13384 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13386 local_player->show_envelope = 0;
13390 debug_print_timestamp(0, "stop main loop profiling ");
13391 printf("----------------------------------------------------------\n");
13394 /* use random number generator in every frame to make it less predictable */
13395 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13399 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13401 int min_x = x, min_y = y, max_x = x, max_y = y;
13404 for (i = 0; i < MAX_PLAYERS; i++)
13406 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13408 if (!stored_player[i].active || &stored_player[i] == player)
13411 min_x = MIN(min_x, jx);
13412 min_y = MIN(min_y, jy);
13413 max_x = MAX(max_x, jx);
13414 max_y = MAX(max_y, jy);
13417 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13420 static boolean AllPlayersInVisibleScreen()
13424 for (i = 0; i < MAX_PLAYERS; i++)
13426 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13428 if (!stored_player[i].active)
13431 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13438 void ScrollLevel(int dx, int dy)
13441 /* (directly solved in BlitBitmap() now) */
13442 static Bitmap *bitmap_db_field2 = NULL;
13443 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13450 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13451 /* only horizontal XOR vertical scroll direction allowed */
13452 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13457 /* (directly solved in BlitBitmap() now) */
13458 if (bitmap_db_field2 == NULL)
13459 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13461 /* needed when blitting directly to same bitmap -- should not be needed with
13462 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13463 BlitBitmap(drawto_field, bitmap_db_field2,
13464 FX + TILEX * (dx == -1) - softscroll_offset,
13465 FY + TILEY * (dy == -1) - softscroll_offset,
13466 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13467 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13468 FX + TILEX * (dx == 1) - softscroll_offset,
13469 FY + TILEY * (dy == 1) - softscroll_offset);
13470 BlitBitmap(bitmap_db_field2, drawto_field,
13471 FX + TILEX * (dx == 1) - softscroll_offset,
13472 FY + TILEY * (dy == 1) - softscroll_offset,
13473 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13474 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13475 FX + TILEX * (dx == 1) - softscroll_offset,
13476 FY + TILEY * (dy == 1) - softscroll_offset);
13481 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13482 int xsize = (BX2 - BX1 + 1);
13483 int ysize = (BY2 - BY1 + 1);
13484 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13485 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13486 int step = (start < end ? +1 : -1);
13488 for (i = start; i != end; i += step)
13490 BlitBitmap(drawto_field, drawto_field,
13491 FX + TILEX * (dx != 0 ? i + step : 0),
13492 FY + TILEY * (dy != 0 ? i + step : 0),
13493 TILEX * (dx != 0 ? 1 : xsize),
13494 TILEY * (dy != 0 ? 1 : ysize),
13495 FX + TILEX * (dx != 0 ? i : 0),
13496 FY + TILEY * (dy != 0 ? i : 0));
13503 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13505 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13509 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13511 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13516 BlitBitmap(drawto_field, drawto_field,
13517 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13518 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13519 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13520 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13521 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13522 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13524 BlitBitmap(drawto_field, drawto_field,
13525 FX + TILEX * (dx == -1) - softscroll_offset,
13526 FY + TILEY * (dy == -1) - softscroll_offset,
13527 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13528 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13529 FX + TILEX * (dx == 1) - softscroll_offset,
13530 FY + TILEY * (dy == 1) - softscroll_offset);
13538 x = (dx == 1 ? BX1 : BX2);
13539 for (y = BY1; y <= BY2; y++)
13540 DrawScreenField(x, y);
13545 y = (dy == 1 ? BY1 : BY2);
13546 for (x = BX1; x <= BX2; x++)
13547 DrawScreenField(x, y);
13550 redraw_mask |= REDRAW_FIELD;
13553 static boolean canFallDown(struct PlayerInfo *player)
13555 int jx = player->jx, jy = player->jy;
13557 return (IN_LEV_FIELD(jx, jy + 1) &&
13558 (IS_FREE(jx, jy + 1) ||
13559 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13560 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13561 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13564 static boolean canPassField(int x, int y, int move_dir)
13566 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13567 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13568 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13569 int nextx = x + dx;
13570 int nexty = y + dy;
13571 int element = Feld[x][y];
13573 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13574 !CAN_MOVE(element) &&
13575 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13576 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13577 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13580 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13582 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13583 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13584 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13588 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13589 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13590 (IS_DIGGABLE(Feld[newx][newy]) ||
13591 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13592 canPassField(newx, newy, move_dir)));
13595 static void CheckGravityMovement(struct PlayerInfo *player)
13597 #if USE_PLAYER_GRAVITY
13598 if (player->gravity && !player->programmed_action)
13600 if (game.gravity && !player->programmed_action)
13603 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13604 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13605 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13606 int jx = player->jx, jy = player->jy;
13607 boolean player_is_moving_to_valid_field =
13608 (!player_is_snapping &&
13609 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13610 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13611 boolean player_can_fall_down = canFallDown(player);
13613 if (player_can_fall_down &&
13614 !player_is_moving_to_valid_field)
13615 player->programmed_action = MV_DOWN;
13619 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13621 return CheckGravityMovement(player);
13623 #if USE_PLAYER_GRAVITY
13624 if (player->gravity && !player->programmed_action)
13626 if (game.gravity && !player->programmed_action)
13629 int jx = player->jx, jy = player->jy;
13630 boolean field_under_player_is_free =
13631 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13632 boolean player_is_standing_on_valid_field =
13633 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13634 (IS_WALKABLE(Feld[jx][jy]) &&
13635 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13637 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13638 player->programmed_action = MV_DOWN;
13643 MovePlayerOneStep()
13644 -----------------------------------------------------------------------------
13645 dx, dy: direction (non-diagonal) to try to move the player to
13646 real_dx, real_dy: direction as read from input device (can be diagonal)
13649 boolean MovePlayerOneStep(struct PlayerInfo *player,
13650 int dx, int dy, int real_dx, int real_dy)
13652 int jx = player->jx, jy = player->jy;
13653 int new_jx = jx + dx, new_jy = jy + dy;
13654 #if !USE_FIXED_DONT_RUN_INTO
13658 boolean player_can_move = !player->cannot_move;
13660 if (!player->active || (!dx && !dy))
13661 return MP_NO_ACTION;
13663 player->MovDir = (dx < 0 ? MV_LEFT :
13664 dx > 0 ? MV_RIGHT :
13666 dy > 0 ? MV_DOWN : MV_NONE);
13668 if (!IN_LEV_FIELD(new_jx, new_jy))
13669 return MP_NO_ACTION;
13671 if (!player_can_move)
13673 if (player->MovPos == 0)
13675 player->is_moving = FALSE;
13676 player->is_digging = FALSE;
13677 player->is_collecting = FALSE;
13678 player->is_snapping = FALSE;
13679 player->is_pushing = FALSE;
13684 if (!options.network && game.centered_player_nr == -1 &&
13685 !AllPlayersInSight(player, new_jx, new_jy))
13686 return MP_NO_ACTION;
13688 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13689 return MP_NO_ACTION;
13692 #if !USE_FIXED_DONT_RUN_INTO
13693 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13695 /* (moved to DigField()) */
13696 if (player_can_move && DONT_RUN_INTO(element))
13698 if (element == EL_ACID && dx == 0 && dy == 1)
13700 SplashAcid(new_jx, new_jy);
13701 Feld[jx][jy] = EL_PLAYER_1;
13702 InitMovingField(jx, jy, MV_DOWN);
13703 Store[jx][jy] = EL_ACID;
13704 ContinueMoving(jx, jy);
13705 BuryPlayer(player);
13708 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13714 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13715 if (can_move != MP_MOVING)
13718 /* check if DigField() has caused relocation of the player */
13719 if (player->jx != jx || player->jy != jy)
13720 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13722 StorePlayer[jx][jy] = 0;
13723 player->last_jx = jx;
13724 player->last_jy = jy;
13725 player->jx = new_jx;
13726 player->jy = new_jy;
13727 StorePlayer[new_jx][new_jy] = player->element_nr;
13729 if (player->move_delay_value_next != -1)
13731 player->move_delay_value = player->move_delay_value_next;
13732 player->move_delay_value_next = -1;
13736 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13738 player->step_counter++;
13740 PlayerVisit[jx][jy] = FrameCounter;
13742 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13743 player->is_moving = TRUE;
13747 /* should better be called in MovePlayer(), but this breaks some tapes */
13748 ScrollPlayer(player, SCROLL_INIT);
13754 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13756 int jx = player->jx, jy = player->jy;
13757 int old_jx = jx, old_jy = jy;
13758 int moved = MP_NO_ACTION;
13760 if (!player->active)
13765 if (player->MovPos == 0)
13767 player->is_moving = FALSE;
13768 player->is_digging = FALSE;
13769 player->is_collecting = FALSE;
13770 player->is_snapping = FALSE;
13771 player->is_pushing = FALSE;
13777 if (player->move_delay > 0)
13780 player->move_delay = -1; /* set to "uninitialized" value */
13782 /* store if player is automatically moved to next field */
13783 player->is_auto_moving = (player->programmed_action != MV_NONE);
13785 /* remove the last programmed player action */
13786 player->programmed_action = 0;
13788 if (player->MovPos)
13790 /* should only happen if pre-1.2 tape recordings are played */
13791 /* this is only for backward compatibility */
13793 int original_move_delay_value = player->move_delay_value;
13796 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13800 /* scroll remaining steps with finest movement resolution */
13801 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13803 while (player->MovPos)
13805 ScrollPlayer(player, SCROLL_GO_ON);
13806 ScrollScreen(NULL, SCROLL_GO_ON);
13808 AdvanceFrameAndPlayerCounters(player->index_nr);
13814 player->move_delay_value = original_move_delay_value;
13817 player->is_active = FALSE;
13819 if (player->last_move_dir & MV_HORIZONTAL)
13821 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13822 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13826 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13827 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13830 #if USE_FIXED_BORDER_RUNNING_GFX
13831 if (!moved && !player->is_active)
13833 player->is_moving = FALSE;
13834 player->is_digging = FALSE;
13835 player->is_collecting = FALSE;
13836 player->is_snapping = FALSE;
13837 player->is_pushing = FALSE;
13845 if (moved & MP_MOVING && !ScreenMovPos &&
13846 (player->index_nr == game.centered_player_nr ||
13847 game.centered_player_nr == -1))
13849 if (moved & MP_MOVING && !ScreenMovPos &&
13850 (player == local_player || !options.network))
13853 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13854 int offset = game.scroll_delay_value;
13856 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13858 /* actual player has left the screen -- scroll in that direction */
13859 if (jx != old_jx) /* player has moved horizontally */
13860 scroll_x += (jx - old_jx);
13861 else /* player has moved vertically */
13862 scroll_y += (jy - old_jy);
13866 if (jx != old_jx) /* player has moved horizontally */
13868 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13869 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13870 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13872 /* don't scroll over playfield boundaries */
13873 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13874 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13876 /* don't scroll more than one field at a time */
13877 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13879 /* don't scroll against the player's moving direction */
13880 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13881 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13882 scroll_x = old_scroll_x;
13884 else /* player has moved vertically */
13886 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13887 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13888 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13890 /* don't scroll over playfield boundaries */
13891 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13892 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13894 /* don't scroll more than one field at a time */
13895 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13897 /* don't scroll against the player's moving direction */
13898 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13899 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13900 scroll_y = old_scroll_y;
13904 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13907 if (!options.network && game.centered_player_nr == -1 &&
13908 !AllPlayersInVisibleScreen())
13910 scroll_x = old_scroll_x;
13911 scroll_y = old_scroll_y;
13915 if (!options.network && !AllPlayersInVisibleScreen())
13917 scroll_x = old_scroll_x;
13918 scroll_y = old_scroll_y;
13923 ScrollScreen(player, SCROLL_INIT);
13924 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13929 player->StepFrame = 0;
13931 if (moved & MP_MOVING)
13933 if (old_jx != jx && old_jy == jy)
13934 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13935 else if (old_jx == jx && old_jy != jy)
13936 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13938 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13940 player->last_move_dir = player->MovDir;
13941 player->is_moving = TRUE;
13942 player->is_snapping = FALSE;
13943 player->is_switching = FALSE;
13944 player->is_dropping = FALSE;
13945 player->is_dropping_pressed = FALSE;
13946 player->drop_pressed_delay = 0;
13949 /* should better be called here than above, but this breaks some tapes */
13950 ScrollPlayer(player, SCROLL_INIT);
13955 CheckGravityMovementWhenNotMoving(player);
13957 player->is_moving = FALSE;
13959 /* at this point, the player is allowed to move, but cannot move right now
13960 (e.g. because of something blocking the way) -- ensure that the player
13961 is also allowed to move in the next frame (in old versions before 3.1.1,
13962 the player was forced to wait again for eight frames before next try) */
13964 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13965 player->move_delay = 0; /* allow direct movement in the next frame */
13968 if (player->move_delay == -1) /* not yet initialized by DigField() */
13969 player->move_delay = player->move_delay_value;
13971 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13973 TestIfPlayerTouchesBadThing(jx, jy);
13974 TestIfPlayerTouchesCustomElement(jx, jy);
13977 if (!player->active)
13978 RemovePlayer(player);
13983 void ScrollPlayer(struct PlayerInfo *player, int mode)
13985 int jx = player->jx, jy = player->jy;
13986 int last_jx = player->last_jx, last_jy = player->last_jy;
13987 int move_stepsize = TILEX / player->move_delay_value;
13989 #if USE_NEW_PLAYER_SPEED
13990 if (!player->active)
13993 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13996 if (!player->active || player->MovPos == 0)
14000 if (mode == SCROLL_INIT)
14002 player->actual_frame_counter = FrameCounter;
14003 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14005 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
14006 Feld[last_jx][last_jy] == EL_EMPTY)
14008 int last_field_block_delay = 0; /* start with no blocking at all */
14009 int block_delay_adjustment = player->block_delay_adjustment;
14011 /* if player blocks last field, add delay for exactly one move */
14012 if (player->block_last_field)
14014 last_field_block_delay += player->move_delay_value;
14016 /* when blocking enabled, prevent moving up despite gravity */
14017 #if USE_PLAYER_GRAVITY
14018 if (player->gravity && player->MovDir == MV_UP)
14019 block_delay_adjustment = -1;
14021 if (game.gravity && player->MovDir == MV_UP)
14022 block_delay_adjustment = -1;
14026 /* add block delay adjustment (also possible when not blocking) */
14027 last_field_block_delay += block_delay_adjustment;
14029 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
14030 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14033 #if USE_NEW_PLAYER_SPEED
14034 if (player->MovPos != 0) /* player has not yet reached destination */
14040 else if (!FrameReached(&player->actual_frame_counter, 1))
14043 #if USE_NEW_PLAYER_SPEED
14044 if (player->MovPos != 0)
14046 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14047 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14049 /* before DrawPlayer() to draw correct player graphic for this case */
14050 if (player->MovPos == 0)
14051 CheckGravityMovement(player);
14054 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14055 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14057 /* before DrawPlayer() to draw correct player graphic for this case */
14058 if (player->MovPos == 0)
14059 CheckGravityMovement(player);
14062 if (player->MovPos == 0) /* player reached destination field */
14064 if (player->move_delay_reset_counter > 0)
14066 player->move_delay_reset_counter--;
14068 if (player->move_delay_reset_counter == 0)
14070 /* continue with normal speed after quickly moving through gate */
14071 HALVE_PLAYER_SPEED(player);
14073 /* be able to make the next move without delay */
14074 player->move_delay = 0;
14078 player->last_jx = jx;
14079 player->last_jy = jy;
14081 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14082 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14084 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14086 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14087 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14089 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14091 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14092 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14094 DrawPlayer(player); /* needed here only to cleanup last field */
14095 RemovePlayer(player);
14097 if (local_player->friends_still_needed == 0 ||
14098 IS_SP_ELEMENT(Feld[jx][jy]))
14099 PlayerWins(player);
14102 /* this breaks one level: "machine", level 000 */
14104 int move_direction = player->MovDir;
14105 int enter_side = MV_DIR_OPPOSITE(move_direction);
14106 int leave_side = move_direction;
14107 int old_jx = last_jx;
14108 int old_jy = last_jy;
14109 int old_element = Feld[old_jx][old_jy];
14110 int new_element = Feld[jx][jy];
14112 if (IS_CUSTOM_ELEMENT(old_element))
14113 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14115 player->index_bit, leave_side);
14117 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14118 CE_PLAYER_LEAVES_X,
14119 player->index_bit, leave_side);
14121 if (IS_CUSTOM_ELEMENT(new_element))
14122 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14123 player->index_bit, enter_side);
14125 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14126 CE_PLAYER_ENTERS_X,
14127 player->index_bit, enter_side);
14129 #if USE_FIX_CE_ACTION_WITH_PLAYER
14130 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14131 CE_MOVE_OF_X, move_direction);
14133 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14134 CE_MOVE_OF_X, move_direction);
14138 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14140 TestIfPlayerTouchesBadThing(jx, jy);
14141 TestIfPlayerTouchesCustomElement(jx, jy);
14143 /* needed because pushed element has not yet reached its destination,
14144 so it would trigger a change event at its previous field location */
14145 if (!player->is_pushing)
14146 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14148 if (!player->active)
14149 RemovePlayer(player);
14152 if (!local_player->LevelSolved && level.use_step_counter)
14162 if (TimeLeft <= 10 && setup.time_limit)
14163 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14166 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14168 DisplayGameControlValues();
14170 DrawGameValue_Time(TimeLeft);
14173 if (!TimeLeft && setup.time_limit)
14174 for (i = 0; i < MAX_PLAYERS; i++)
14175 KillPlayer(&stored_player[i]);
14178 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14180 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14182 DisplayGameControlValues();
14185 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14186 DrawGameValue_Time(TimePlayed);
14190 if (tape.single_step && tape.recording && !tape.pausing &&
14191 !player->programmed_action)
14192 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14196 void ScrollScreen(struct PlayerInfo *player, int mode)
14198 static unsigned int screen_frame_counter = 0;
14200 if (mode == SCROLL_INIT)
14202 /* set scrolling step size according to actual player's moving speed */
14203 ScrollStepSize = TILEX / player->move_delay_value;
14205 screen_frame_counter = FrameCounter;
14206 ScreenMovDir = player->MovDir;
14207 ScreenMovPos = player->MovPos;
14208 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14211 else if (!FrameReached(&screen_frame_counter, 1))
14216 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14217 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14218 redraw_mask |= REDRAW_FIELD;
14221 ScreenMovDir = MV_NONE;
14224 void TestIfPlayerTouchesCustomElement(int x, int y)
14226 static int xy[4][2] =
14233 static int trigger_sides[4][2] =
14235 /* center side border side */
14236 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14237 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14238 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14239 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14241 static int touch_dir[4] =
14243 MV_LEFT | MV_RIGHT,
14248 int center_element = Feld[x][y]; /* should always be non-moving! */
14251 for (i = 0; i < NUM_DIRECTIONS; i++)
14253 int xx = x + xy[i][0];
14254 int yy = y + xy[i][1];
14255 int center_side = trigger_sides[i][0];
14256 int border_side = trigger_sides[i][1];
14257 int border_element;
14259 if (!IN_LEV_FIELD(xx, yy))
14262 if (IS_PLAYER(x, y)) /* player found at center element */
14264 struct PlayerInfo *player = PLAYERINFO(x, y);
14266 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14267 border_element = Feld[xx][yy]; /* may be moving! */
14268 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14269 border_element = Feld[xx][yy];
14270 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14271 border_element = MovingOrBlocked2Element(xx, yy);
14273 continue; /* center and border element do not touch */
14275 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14276 player->index_bit, border_side);
14277 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14278 CE_PLAYER_TOUCHES_X,
14279 player->index_bit, border_side);
14281 #if USE_FIX_CE_ACTION_WITH_PLAYER
14283 /* use player element that is initially defined in the level playfield,
14284 not the player element that corresponds to the runtime player number
14285 (example: a level that contains EL_PLAYER_3 as the only player would
14286 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14287 int player_element = PLAYERINFO(x, y)->initial_element;
14289 CheckElementChangeBySide(xx, yy, border_element, player_element,
14290 CE_TOUCHING_X, border_side);
14294 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14296 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14298 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14300 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14301 continue; /* center and border element do not touch */
14304 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14305 player->index_bit, center_side);
14306 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14307 CE_PLAYER_TOUCHES_X,
14308 player->index_bit, center_side);
14310 #if USE_FIX_CE_ACTION_WITH_PLAYER
14312 /* use player element that is initially defined in the level playfield,
14313 not the player element that corresponds to the runtime player number
14314 (example: a level that contains EL_PLAYER_3 as the only player would
14315 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14316 int player_element = PLAYERINFO(xx, yy)->initial_element;
14318 CheckElementChangeBySide(x, y, center_element, player_element,
14319 CE_TOUCHING_X, center_side);
14328 #if USE_ELEMENT_TOUCHING_BUGFIX
14330 void TestIfElementTouchesCustomElement(int x, int y)
14332 static int xy[4][2] =
14339 static int trigger_sides[4][2] =
14341 /* center side border side */
14342 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14343 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14344 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14345 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14347 static int touch_dir[4] =
14349 MV_LEFT | MV_RIGHT,
14354 boolean change_center_element = FALSE;
14355 int center_element = Feld[x][y]; /* should always be non-moving! */
14356 int border_element_old[NUM_DIRECTIONS];
14359 for (i = 0; i < NUM_DIRECTIONS; i++)
14361 int xx = x + xy[i][0];
14362 int yy = y + xy[i][1];
14363 int border_element;
14365 border_element_old[i] = -1;
14367 if (!IN_LEV_FIELD(xx, yy))
14370 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14371 border_element = Feld[xx][yy]; /* may be moving! */
14372 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14373 border_element = Feld[xx][yy];
14374 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14375 border_element = MovingOrBlocked2Element(xx, yy);
14377 continue; /* center and border element do not touch */
14379 border_element_old[i] = border_element;
14382 for (i = 0; i < NUM_DIRECTIONS; i++)
14384 int xx = x + xy[i][0];
14385 int yy = y + xy[i][1];
14386 int center_side = trigger_sides[i][0];
14387 int border_element = border_element_old[i];
14389 if (border_element == -1)
14392 /* check for change of border element */
14393 CheckElementChangeBySide(xx, yy, border_element, center_element,
14394 CE_TOUCHING_X, center_side);
14396 /* (center element cannot be player, so we dont have to check this here) */
14399 for (i = 0; i < NUM_DIRECTIONS; i++)
14401 int xx = x + xy[i][0];
14402 int yy = y + xy[i][1];
14403 int border_side = trigger_sides[i][1];
14404 int border_element = border_element_old[i];
14406 if (border_element == -1)
14409 /* check for change of center element (but change it only once) */
14410 if (!change_center_element)
14411 change_center_element =
14412 CheckElementChangeBySide(x, y, center_element, border_element,
14413 CE_TOUCHING_X, border_side);
14415 #if USE_FIX_CE_ACTION_WITH_PLAYER
14416 if (IS_PLAYER(xx, yy))
14418 /* use player element that is initially defined in the level playfield,
14419 not the player element that corresponds to the runtime player number
14420 (example: a level that contains EL_PLAYER_3 as the only player would
14421 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14422 int player_element = PLAYERINFO(xx, yy)->initial_element;
14424 CheckElementChangeBySide(x, y, center_element, player_element,
14425 CE_TOUCHING_X, border_side);
14433 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14435 static int xy[4][2] =
14442 static int trigger_sides[4][2] =
14444 /* center side border side */
14445 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14446 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14447 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14448 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14450 static int touch_dir[4] =
14452 MV_LEFT | MV_RIGHT,
14457 boolean change_center_element = FALSE;
14458 int center_element = Feld[x][y]; /* should always be non-moving! */
14461 for (i = 0; i < NUM_DIRECTIONS; i++)
14463 int xx = x + xy[i][0];
14464 int yy = y + xy[i][1];
14465 int center_side = trigger_sides[i][0];
14466 int border_side = trigger_sides[i][1];
14467 int border_element;
14469 if (!IN_LEV_FIELD(xx, yy))
14472 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14473 border_element = Feld[xx][yy]; /* may be moving! */
14474 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14475 border_element = Feld[xx][yy];
14476 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14477 border_element = MovingOrBlocked2Element(xx, yy);
14479 continue; /* center and border element do not touch */
14481 /* check for change of center element (but change it only once) */
14482 if (!change_center_element)
14483 change_center_element =
14484 CheckElementChangeBySide(x, y, center_element, border_element,
14485 CE_TOUCHING_X, border_side);
14487 /* check for change of border element */
14488 CheckElementChangeBySide(xx, yy, border_element, center_element,
14489 CE_TOUCHING_X, center_side);
14495 void TestIfElementHitsCustomElement(int x, int y, int direction)
14497 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14498 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14499 int hitx = x + dx, hity = y + dy;
14500 int hitting_element = Feld[x][y];
14501 int touched_element;
14503 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14506 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14507 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14509 if (IN_LEV_FIELD(hitx, hity))
14511 int opposite_direction = MV_DIR_OPPOSITE(direction);
14512 int hitting_side = direction;
14513 int touched_side = opposite_direction;
14514 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14515 MovDir[hitx][hity] != direction ||
14516 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14522 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14523 CE_HITTING_X, touched_side);
14525 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14526 CE_HIT_BY_X, hitting_side);
14528 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14529 CE_HIT_BY_SOMETHING, opposite_direction);
14531 #if USE_FIX_CE_ACTION_WITH_PLAYER
14532 if (IS_PLAYER(hitx, hity))
14534 /* use player element that is initially defined in the level playfield,
14535 not the player element that corresponds to the runtime player number
14536 (example: a level that contains EL_PLAYER_3 as the only player would
14537 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14538 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14540 CheckElementChangeBySide(x, y, hitting_element, player_element,
14541 CE_HITTING_X, touched_side);
14547 /* "hitting something" is also true when hitting the playfield border */
14548 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14549 CE_HITTING_SOMETHING, direction);
14553 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14555 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14556 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14557 int hitx = x + dx, hity = y + dy;
14558 int hitting_element = Feld[x][y];
14559 int touched_element;
14561 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14562 !IS_FREE(hitx, hity) &&
14563 (!IS_MOVING(hitx, hity) ||
14564 MovDir[hitx][hity] != direction ||
14565 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14568 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14572 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14576 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14577 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14579 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14580 EP_CAN_SMASH_EVERYTHING, direction);
14582 if (IN_LEV_FIELD(hitx, hity))
14584 int opposite_direction = MV_DIR_OPPOSITE(direction);
14585 int hitting_side = direction;
14586 int touched_side = opposite_direction;
14588 int touched_element = MovingOrBlocked2Element(hitx, hity);
14591 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14592 MovDir[hitx][hity] != direction ||
14593 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14602 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14603 CE_SMASHED_BY_SOMETHING, opposite_direction);
14605 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14606 CE_OTHER_IS_SMASHING, touched_side);
14608 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14609 CE_OTHER_GETS_SMASHED, hitting_side);
14615 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14617 int i, kill_x = -1, kill_y = -1;
14619 int bad_element = -1;
14620 static int test_xy[4][2] =
14627 static int test_dir[4] =
14635 for (i = 0; i < NUM_DIRECTIONS; i++)
14637 int test_x, test_y, test_move_dir, test_element;
14639 test_x = good_x + test_xy[i][0];
14640 test_y = good_y + test_xy[i][1];
14642 if (!IN_LEV_FIELD(test_x, test_y))
14646 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14648 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14650 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14651 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14653 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14654 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14658 bad_element = test_element;
14664 if (kill_x != -1 || kill_y != -1)
14666 if (IS_PLAYER(good_x, good_y))
14668 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14670 if (player->shield_deadly_time_left > 0 &&
14671 !IS_INDESTRUCTIBLE(bad_element))
14672 Bang(kill_x, kill_y);
14673 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14674 KillPlayer(player);
14677 Bang(good_x, good_y);
14681 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14683 int i, kill_x = -1, kill_y = -1;
14684 int bad_element = Feld[bad_x][bad_y];
14685 static int test_xy[4][2] =
14692 static int touch_dir[4] =
14694 MV_LEFT | MV_RIGHT,
14699 static int test_dir[4] =
14707 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14710 for (i = 0; i < NUM_DIRECTIONS; i++)
14712 int test_x, test_y, test_move_dir, test_element;
14714 test_x = bad_x + test_xy[i][0];
14715 test_y = bad_y + test_xy[i][1];
14717 if (!IN_LEV_FIELD(test_x, test_y))
14721 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14723 test_element = Feld[test_x][test_y];
14725 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14726 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14728 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14729 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14731 /* good thing is player or penguin that does not move away */
14732 if (IS_PLAYER(test_x, test_y))
14734 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14736 if (bad_element == EL_ROBOT && player->is_moving)
14737 continue; /* robot does not kill player if he is moving */
14739 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14741 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14742 continue; /* center and border element do not touch */
14750 else if (test_element == EL_PENGUIN)
14760 if (kill_x != -1 || kill_y != -1)
14762 if (IS_PLAYER(kill_x, kill_y))
14764 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14766 if (player->shield_deadly_time_left > 0 &&
14767 !IS_INDESTRUCTIBLE(bad_element))
14768 Bang(bad_x, bad_y);
14769 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14770 KillPlayer(player);
14773 Bang(kill_x, kill_y);
14777 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14779 int bad_element = Feld[bad_x][bad_y];
14780 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14781 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14782 int test_x = bad_x + dx, test_y = bad_y + dy;
14783 int test_move_dir, test_element;
14784 int kill_x = -1, kill_y = -1;
14786 if (!IN_LEV_FIELD(test_x, test_y))
14790 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14792 test_element = Feld[test_x][test_y];
14794 if (test_move_dir != bad_move_dir)
14796 /* good thing can be player or penguin that does not move away */
14797 if (IS_PLAYER(test_x, test_y))
14799 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14801 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14802 player as being hit when he is moving towards the bad thing, because
14803 the "get hit by" condition would be lost after the player stops) */
14804 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14805 return; /* player moves away from bad thing */
14810 else if (test_element == EL_PENGUIN)
14817 if (kill_x != -1 || kill_y != -1)
14819 if (IS_PLAYER(kill_x, kill_y))
14821 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14823 if (player->shield_deadly_time_left > 0 &&
14824 !IS_INDESTRUCTIBLE(bad_element))
14825 Bang(bad_x, bad_y);
14826 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14827 KillPlayer(player);
14830 Bang(kill_x, kill_y);
14834 void TestIfPlayerTouchesBadThing(int x, int y)
14836 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14839 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14841 TestIfGoodThingHitsBadThing(x, y, move_dir);
14844 void TestIfBadThingTouchesPlayer(int x, int y)
14846 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14849 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14851 TestIfBadThingHitsGoodThing(x, y, move_dir);
14854 void TestIfFriendTouchesBadThing(int x, int y)
14856 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14859 void TestIfBadThingTouchesFriend(int x, int y)
14861 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14864 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14866 int i, kill_x = bad_x, kill_y = bad_y;
14867 static int xy[4][2] =
14875 for (i = 0; i < NUM_DIRECTIONS; i++)
14879 x = bad_x + xy[i][0];
14880 y = bad_y + xy[i][1];
14881 if (!IN_LEV_FIELD(x, y))
14884 element = Feld[x][y];
14885 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14886 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14894 if (kill_x != bad_x || kill_y != bad_y)
14895 Bang(bad_x, bad_y);
14898 void KillPlayer(struct PlayerInfo *player)
14900 int jx = player->jx, jy = player->jy;
14902 if (!player->active)
14906 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14907 player->killed, player->active, player->reanimated);
14910 /* the following code was introduced to prevent an infinite loop when calling
14912 -> CheckTriggeredElementChangeExt()
14913 -> ExecuteCustomElementAction()
14915 -> (infinitely repeating the above sequence of function calls)
14916 which occurs when killing the player while having a CE with the setting
14917 "kill player X when explosion of <player X>"; the solution using a new
14918 field "player->killed" was chosen for backwards compatibility, although
14919 clever use of the fields "player->active" etc. would probably also work */
14921 if (player->killed)
14925 player->killed = TRUE;
14927 /* remove accessible field at the player's position */
14928 Feld[jx][jy] = EL_EMPTY;
14930 /* deactivate shield (else Bang()/Explode() would not work right) */
14931 player->shield_normal_time_left = 0;
14932 player->shield_deadly_time_left = 0;
14935 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14936 player->killed, player->active, player->reanimated);
14942 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14943 player->killed, player->active, player->reanimated);
14946 #if USE_PLAYER_REANIMATION
14948 if (player->reanimated) /* killed player may have been reanimated */
14949 player->killed = player->reanimated = FALSE;
14951 BuryPlayer(player);
14953 if (player->killed) /* player may have been reanimated */
14954 BuryPlayer(player);
14957 BuryPlayer(player);
14961 static void KillPlayerUnlessEnemyProtected(int x, int y)
14963 if (!PLAYER_ENEMY_PROTECTED(x, y))
14964 KillPlayer(PLAYERINFO(x, y));
14967 static void KillPlayerUnlessExplosionProtected(int x, int y)
14969 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14970 KillPlayer(PLAYERINFO(x, y));
14973 void BuryPlayer(struct PlayerInfo *player)
14975 int jx = player->jx, jy = player->jy;
14977 if (!player->active)
14980 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14981 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14983 player->GameOver = TRUE;
14984 RemovePlayer(player);
14987 void RemovePlayer(struct PlayerInfo *player)
14989 int jx = player->jx, jy = player->jy;
14990 int i, found = FALSE;
14992 player->present = FALSE;
14993 player->active = FALSE;
14995 if (!ExplodeField[jx][jy])
14996 StorePlayer[jx][jy] = 0;
14998 if (player->is_moving)
14999 TEST_DrawLevelField(player->last_jx, player->last_jy);
15001 for (i = 0; i < MAX_PLAYERS; i++)
15002 if (stored_player[i].active)
15006 AllPlayersGone = TRUE;
15012 #if USE_NEW_SNAP_DELAY
15013 static void setFieldForSnapping(int x, int y, int element, int direction)
15015 struct ElementInfo *ei = &element_info[element];
15016 int direction_bit = MV_DIR_TO_BIT(direction);
15017 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
15018 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
15019 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
15021 Feld[x][y] = EL_ELEMENT_SNAPPING;
15022 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
15024 ResetGfxAnimation(x, y);
15026 GfxElement[x][y] = element;
15027 GfxAction[x][y] = action;
15028 GfxDir[x][y] = direction;
15029 GfxFrame[x][y] = -1;
15034 =============================================================================
15035 checkDiagonalPushing()
15036 -----------------------------------------------------------------------------
15037 check if diagonal input device direction results in pushing of object
15038 (by checking if the alternative direction is walkable, diggable, ...)
15039 =============================================================================
15042 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15043 int x, int y, int real_dx, int real_dy)
15045 int jx, jy, dx, dy, xx, yy;
15047 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
15050 /* diagonal direction: check alternative direction */
15055 xx = jx + (dx == 0 ? real_dx : 0);
15056 yy = jy + (dy == 0 ? real_dy : 0);
15058 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15062 =============================================================================
15064 -----------------------------------------------------------------------------
15065 x, y: field next to player (non-diagonal) to try to dig to
15066 real_dx, real_dy: direction as read from input device (can be diagonal)
15067 =============================================================================
15070 static int DigField(struct PlayerInfo *player,
15071 int oldx, int oldy, int x, int y,
15072 int real_dx, int real_dy, int mode)
15074 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15075 boolean player_was_pushing = player->is_pushing;
15076 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15077 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15078 int jx = oldx, jy = oldy;
15079 int dx = x - jx, dy = y - jy;
15080 int nextx = x + dx, nexty = y + dy;
15081 int move_direction = (dx == -1 ? MV_LEFT :
15082 dx == +1 ? MV_RIGHT :
15084 dy == +1 ? MV_DOWN : MV_NONE);
15085 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15086 int dig_side = MV_DIR_OPPOSITE(move_direction);
15087 int old_element = Feld[jx][jy];
15088 #if USE_FIXED_DONT_RUN_INTO
15089 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15095 if (is_player) /* function can also be called by EL_PENGUIN */
15097 if (player->MovPos == 0)
15099 player->is_digging = FALSE;
15100 player->is_collecting = FALSE;
15103 if (player->MovPos == 0) /* last pushing move finished */
15104 player->is_pushing = FALSE;
15106 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15108 player->is_switching = FALSE;
15109 player->push_delay = -1;
15111 return MP_NO_ACTION;
15115 #if !USE_FIXED_DONT_RUN_INTO
15116 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15117 return MP_NO_ACTION;
15120 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15121 old_element = Back[jx][jy];
15123 /* in case of element dropped at player position, check background */
15124 else if (Back[jx][jy] != EL_EMPTY &&
15125 game.engine_version >= VERSION_IDENT(2,2,0,0))
15126 old_element = Back[jx][jy];
15128 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15129 return MP_NO_ACTION; /* field has no opening in this direction */
15131 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15132 return MP_NO_ACTION; /* field has no opening in this direction */
15134 #if USE_FIXED_DONT_RUN_INTO
15135 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15139 Feld[jx][jy] = player->artwork_element;
15140 InitMovingField(jx, jy, MV_DOWN);
15141 Store[jx][jy] = EL_ACID;
15142 ContinueMoving(jx, jy);
15143 BuryPlayer(player);
15145 return MP_DONT_RUN_INTO;
15149 #if USE_FIXED_DONT_RUN_INTO
15150 if (player_can_move && DONT_RUN_INTO(element))
15152 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15154 return MP_DONT_RUN_INTO;
15158 #if USE_FIXED_DONT_RUN_INTO
15159 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15160 return MP_NO_ACTION;
15163 #if !USE_FIXED_DONT_RUN_INTO
15164 element = Feld[x][y];
15167 collect_count = element_info[element].collect_count_initial;
15169 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15170 return MP_NO_ACTION;
15172 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15173 player_can_move = player_can_move_or_snap;
15175 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15176 game.engine_version >= VERSION_IDENT(2,2,0,0))
15178 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15179 player->index_bit, dig_side);
15180 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15181 player->index_bit, dig_side);
15183 if (element == EL_DC_LANDMINE)
15186 if (Feld[x][y] != element) /* field changed by snapping */
15189 return MP_NO_ACTION;
15192 #if USE_PLAYER_GRAVITY
15193 if (player->gravity && is_player && !player->is_auto_moving &&
15194 canFallDown(player) && move_direction != MV_DOWN &&
15195 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15196 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15198 if (game.gravity && is_player && !player->is_auto_moving &&
15199 canFallDown(player) && move_direction != MV_DOWN &&
15200 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15201 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15204 if (player_can_move &&
15205 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15207 int sound_element = SND_ELEMENT(element);
15208 int sound_action = ACTION_WALKING;
15210 if (IS_RND_GATE(element))
15212 if (!player->key[RND_GATE_NR(element)])
15213 return MP_NO_ACTION;
15215 else if (IS_RND_GATE_GRAY(element))
15217 if (!player->key[RND_GATE_GRAY_NR(element)])
15218 return MP_NO_ACTION;
15220 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15222 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15223 return MP_NO_ACTION;
15225 else if (element == EL_EXIT_OPEN ||
15226 element == EL_EM_EXIT_OPEN ||
15228 element == EL_EM_EXIT_OPENING ||
15230 element == EL_STEEL_EXIT_OPEN ||
15231 element == EL_EM_STEEL_EXIT_OPEN ||
15233 element == EL_EM_STEEL_EXIT_OPENING ||
15235 element == EL_SP_EXIT_OPEN ||
15236 element == EL_SP_EXIT_OPENING)
15238 sound_action = ACTION_PASSING; /* player is passing exit */
15240 else if (element == EL_EMPTY)
15242 sound_action = ACTION_MOVING; /* nothing to walk on */
15245 /* play sound from background or player, whatever is available */
15246 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15247 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15249 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15251 else if (player_can_move &&
15252 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15254 if (!ACCESS_FROM(element, opposite_direction))
15255 return MP_NO_ACTION; /* field not accessible from this direction */
15257 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15258 return MP_NO_ACTION;
15260 if (IS_EM_GATE(element))
15262 if (!player->key[EM_GATE_NR(element)])
15263 return MP_NO_ACTION;
15265 else if (IS_EM_GATE_GRAY(element))
15267 if (!player->key[EM_GATE_GRAY_NR(element)])
15268 return MP_NO_ACTION;
15270 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15272 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15273 return MP_NO_ACTION;
15275 else if (IS_EMC_GATE(element))
15277 if (!player->key[EMC_GATE_NR(element)])
15278 return MP_NO_ACTION;
15280 else if (IS_EMC_GATE_GRAY(element))
15282 if (!player->key[EMC_GATE_GRAY_NR(element)])
15283 return MP_NO_ACTION;
15285 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15287 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15288 return MP_NO_ACTION;
15290 else if (element == EL_DC_GATE_WHITE ||
15291 element == EL_DC_GATE_WHITE_GRAY ||
15292 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15294 if (player->num_white_keys == 0)
15295 return MP_NO_ACTION;
15297 player->num_white_keys--;
15299 else if (IS_SP_PORT(element))
15301 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15302 element == EL_SP_GRAVITY_PORT_RIGHT ||
15303 element == EL_SP_GRAVITY_PORT_UP ||
15304 element == EL_SP_GRAVITY_PORT_DOWN)
15305 #if USE_PLAYER_GRAVITY
15306 player->gravity = !player->gravity;
15308 game.gravity = !game.gravity;
15310 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15311 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15312 element == EL_SP_GRAVITY_ON_PORT_UP ||
15313 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15314 #if USE_PLAYER_GRAVITY
15315 player->gravity = TRUE;
15317 game.gravity = TRUE;
15319 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15320 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15321 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15322 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15323 #if USE_PLAYER_GRAVITY
15324 player->gravity = FALSE;
15326 game.gravity = FALSE;
15330 /* automatically move to the next field with double speed */
15331 player->programmed_action = move_direction;
15333 if (player->move_delay_reset_counter == 0)
15335 player->move_delay_reset_counter = 2; /* two double speed steps */
15337 DOUBLE_PLAYER_SPEED(player);
15340 PlayLevelSoundAction(x, y, ACTION_PASSING);
15342 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15346 if (mode != DF_SNAP)
15348 GfxElement[x][y] = GFX_ELEMENT(element);
15349 player->is_digging = TRUE;
15352 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15354 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15355 player->index_bit, dig_side);
15357 if (mode == DF_SNAP)
15359 #if USE_NEW_SNAP_DELAY
15360 if (level.block_snap_field)
15361 setFieldForSnapping(x, y, element, move_direction);
15363 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15365 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15368 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15369 player->index_bit, dig_side);
15372 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15376 if (is_player && mode != DF_SNAP)
15378 GfxElement[x][y] = element;
15379 player->is_collecting = TRUE;
15382 if (element == EL_SPEED_PILL)
15384 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15386 else if (element == EL_EXTRA_TIME && level.time > 0)
15388 TimeLeft += level.extra_time;
15391 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15393 DisplayGameControlValues();
15395 DrawGameValue_Time(TimeLeft);
15398 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15400 player->shield_normal_time_left += level.shield_normal_time;
15401 if (element == EL_SHIELD_DEADLY)
15402 player->shield_deadly_time_left += level.shield_deadly_time;
15404 else if (element == EL_DYNAMITE ||
15405 element == EL_EM_DYNAMITE ||
15406 element == EL_SP_DISK_RED)
15408 if (player->inventory_size < MAX_INVENTORY_SIZE)
15409 player->inventory_element[player->inventory_size++] = element;
15411 DrawGameDoorValues();
15413 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15415 player->dynabomb_count++;
15416 player->dynabombs_left++;
15418 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15420 player->dynabomb_size++;
15422 else if (element == EL_DYNABOMB_INCREASE_POWER)
15424 player->dynabomb_xl = TRUE;
15426 else if (IS_KEY(element))
15428 player->key[KEY_NR(element)] = TRUE;
15430 DrawGameDoorValues();
15432 else if (element == EL_DC_KEY_WHITE)
15434 player->num_white_keys++;
15436 /* display white keys? */
15437 /* DrawGameDoorValues(); */
15439 else if (IS_ENVELOPE(element))
15441 player->show_envelope = element;
15443 else if (element == EL_EMC_LENSES)
15445 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15447 RedrawAllInvisibleElementsForLenses();
15449 else if (element == EL_EMC_MAGNIFIER)
15451 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15453 RedrawAllInvisibleElementsForMagnifier();
15455 else if (IS_DROPPABLE(element) ||
15456 IS_THROWABLE(element)) /* can be collected and dropped */
15460 if (collect_count == 0)
15461 player->inventory_infinite_element = element;
15463 for (i = 0; i < collect_count; i++)
15464 if (player->inventory_size < MAX_INVENTORY_SIZE)
15465 player->inventory_element[player->inventory_size++] = element;
15467 DrawGameDoorValues();
15469 else if (collect_count > 0)
15471 local_player->gems_still_needed -= collect_count;
15472 if (local_player->gems_still_needed < 0)
15473 local_player->gems_still_needed = 0;
15476 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15478 DisplayGameControlValues();
15480 DrawGameValue_Emeralds(local_player->gems_still_needed);
15484 RaiseScoreElement(element);
15485 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15488 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15489 player->index_bit, dig_side);
15491 if (mode == DF_SNAP)
15493 #if USE_NEW_SNAP_DELAY
15494 if (level.block_snap_field)
15495 setFieldForSnapping(x, y, element, move_direction);
15497 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15499 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15502 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15503 player->index_bit, dig_side);
15506 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15508 if (mode == DF_SNAP && element != EL_BD_ROCK)
15509 return MP_NO_ACTION;
15511 if (CAN_FALL(element) && dy)
15512 return MP_NO_ACTION;
15514 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15515 !(element == EL_SPRING && level.use_spring_bug))
15516 return MP_NO_ACTION;
15518 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15519 ((move_direction & MV_VERTICAL &&
15520 ((element_info[element].move_pattern & MV_LEFT &&
15521 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15522 (element_info[element].move_pattern & MV_RIGHT &&
15523 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15524 (move_direction & MV_HORIZONTAL &&
15525 ((element_info[element].move_pattern & MV_UP &&
15526 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15527 (element_info[element].move_pattern & MV_DOWN &&
15528 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15529 return MP_NO_ACTION;
15531 /* do not push elements already moving away faster than player */
15532 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15533 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15534 return MP_NO_ACTION;
15536 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15538 if (player->push_delay_value == -1 || !player_was_pushing)
15539 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15541 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15543 if (player->push_delay_value == -1)
15544 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15546 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15548 if (!player->is_pushing)
15549 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15552 player->is_pushing = TRUE;
15553 player->is_active = TRUE;
15555 if (!(IN_LEV_FIELD(nextx, nexty) &&
15556 (IS_FREE(nextx, nexty) ||
15557 (IS_SB_ELEMENT(element) &&
15558 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15559 (IS_CUSTOM_ELEMENT(element) &&
15560 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15561 return MP_NO_ACTION;
15563 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15564 return MP_NO_ACTION;
15566 if (player->push_delay == -1) /* new pushing; restart delay */
15567 player->push_delay = 0;
15569 if (player->push_delay < player->push_delay_value &&
15570 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15571 element != EL_SPRING && element != EL_BALLOON)
15573 /* make sure that there is no move delay before next try to push */
15574 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15575 player->move_delay = 0;
15577 return MP_NO_ACTION;
15580 if (IS_CUSTOM_ELEMENT(element) &&
15581 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15583 if (!DigFieldByCE(nextx, nexty, element))
15584 return MP_NO_ACTION;
15587 if (IS_SB_ELEMENT(element))
15589 if (element == EL_SOKOBAN_FIELD_FULL)
15591 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15592 local_player->sokobanfields_still_needed++;
15595 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15597 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15598 local_player->sokobanfields_still_needed--;
15601 Feld[x][y] = EL_SOKOBAN_OBJECT;
15603 if (Back[x][y] == Back[nextx][nexty])
15604 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15605 else if (Back[x][y] != 0)
15606 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15609 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15613 if (local_player->sokobanfields_still_needed == 0 &&
15614 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15616 if (local_player->sokobanfields_still_needed == 0 &&
15617 game.emulation == EMU_SOKOBAN)
15620 PlayerWins(player);
15622 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15626 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15628 InitMovingField(x, y, move_direction);
15629 GfxAction[x][y] = ACTION_PUSHING;
15631 if (mode == DF_SNAP)
15632 ContinueMoving(x, y);
15634 MovPos[x][y] = (dx != 0 ? dx : dy);
15636 Pushed[x][y] = TRUE;
15637 Pushed[nextx][nexty] = TRUE;
15639 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15640 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15642 player->push_delay_value = -1; /* get new value later */
15644 /* check for element change _after_ element has been pushed */
15645 if (game.use_change_when_pushing_bug)
15647 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15648 player->index_bit, dig_side);
15649 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15650 player->index_bit, dig_side);
15653 else if (IS_SWITCHABLE(element))
15655 if (PLAYER_SWITCHING(player, x, y))
15657 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15658 player->index_bit, dig_side);
15663 player->is_switching = TRUE;
15664 player->switch_x = x;
15665 player->switch_y = y;
15667 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15669 if (element == EL_ROBOT_WHEEL)
15671 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15675 game.robot_wheel_active = TRUE;
15677 TEST_DrawLevelField(x, y);
15679 else if (element == EL_SP_TERMINAL)
15683 SCAN_PLAYFIELD(xx, yy)
15685 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15687 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15688 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15691 else if (IS_BELT_SWITCH(element))
15693 ToggleBeltSwitch(x, y);
15695 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15696 element == EL_SWITCHGATE_SWITCH_DOWN ||
15697 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15698 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15700 ToggleSwitchgateSwitch(x, y);
15702 else if (element == EL_LIGHT_SWITCH ||
15703 element == EL_LIGHT_SWITCH_ACTIVE)
15705 ToggleLightSwitch(x, y);
15707 else if (element == EL_TIMEGATE_SWITCH ||
15708 element == EL_DC_TIMEGATE_SWITCH)
15710 ActivateTimegateSwitch(x, y);
15712 else if (element == EL_BALLOON_SWITCH_LEFT ||
15713 element == EL_BALLOON_SWITCH_RIGHT ||
15714 element == EL_BALLOON_SWITCH_UP ||
15715 element == EL_BALLOON_SWITCH_DOWN ||
15716 element == EL_BALLOON_SWITCH_NONE ||
15717 element == EL_BALLOON_SWITCH_ANY)
15719 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15720 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15721 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15722 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15723 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15726 else if (element == EL_LAMP)
15728 Feld[x][y] = EL_LAMP_ACTIVE;
15729 local_player->lights_still_needed--;
15731 ResetGfxAnimation(x, y);
15732 TEST_DrawLevelField(x, y);
15734 else if (element == EL_TIME_ORB_FULL)
15736 Feld[x][y] = EL_TIME_ORB_EMPTY;
15738 if (level.time > 0 || level.use_time_orb_bug)
15740 TimeLeft += level.time_orb_time;
15741 game.no_time_limit = FALSE;
15744 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15746 DisplayGameControlValues();
15748 DrawGameValue_Time(TimeLeft);
15752 ResetGfxAnimation(x, y);
15753 TEST_DrawLevelField(x, y);
15755 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15756 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15760 game.ball_state = !game.ball_state;
15762 SCAN_PLAYFIELD(xx, yy)
15764 int e = Feld[xx][yy];
15766 if (game.ball_state)
15768 if (e == EL_EMC_MAGIC_BALL)
15769 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15770 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15771 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15775 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15776 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15777 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15778 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15783 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15784 player->index_bit, dig_side);
15786 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15787 player->index_bit, dig_side);
15789 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15790 player->index_bit, dig_side);
15796 if (!PLAYER_SWITCHING(player, x, y))
15798 player->is_switching = TRUE;
15799 player->switch_x = x;
15800 player->switch_y = y;
15802 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15803 player->index_bit, dig_side);
15804 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15805 player->index_bit, dig_side);
15807 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15808 player->index_bit, dig_side);
15809 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15810 player->index_bit, dig_side);
15813 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15814 player->index_bit, dig_side);
15815 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15816 player->index_bit, dig_side);
15818 return MP_NO_ACTION;
15821 player->push_delay = -1;
15823 if (is_player) /* function can also be called by EL_PENGUIN */
15825 if (Feld[x][y] != element) /* really digged/collected something */
15827 player->is_collecting = !player->is_digging;
15828 player->is_active = TRUE;
15835 static boolean DigFieldByCE(int x, int y, int digging_element)
15837 int element = Feld[x][y];
15839 if (!IS_FREE(x, y))
15841 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15842 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15845 /* no element can dig solid indestructible elements */
15846 if (IS_INDESTRUCTIBLE(element) &&
15847 !IS_DIGGABLE(element) &&
15848 !IS_COLLECTIBLE(element))
15851 if (AmoebaNr[x][y] &&
15852 (element == EL_AMOEBA_FULL ||
15853 element == EL_BD_AMOEBA ||
15854 element == EL_AMOEBA_GROWING))
15856 AmoebaCnt[AmoebaNr[x][y]]--;
15857 AmoebaCnt2[AmoebaNr[x][y]]--;
15860 if (IS_MOVING(x, y))
15861 RemoveMovingField(x, y);
15865 TEST_DrawLevelField(x, y);
15868 /* if digged element was about to explode, prevent the explosion */
15869 ExplodeField[x][y] = EX_TYPE_NONE;
15871 PlayLevelSoundAction(x, y, action);
15874 Store[x][y] = EL_EMPTY;
15877 /* this makes it possible to leave the removed element again */
15878 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15879 Store[x][y] = element;
15881 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15883 int move_leave_element = element_info[digging_element].move_leave_element;
15885 /* this makes it possible to leave the removed element again */
15886 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15887 element : move_leave_element);
15894 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15896 int jx = player->jx, jy = player->jy;
15897 int x = jx + dx, y = jy + dy;
15898 int snap_direction = (dx == -1 ? MV_LEFT :
15899 dx == +1 ? MV_RIGHT :
15901 dy == +1 ? MV_DOWN : MV_NONE);
15902 boolean can_continue_snapping = (level.continuous_snapping &&
15903 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15905 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15908 if (!player->active || !IN_LEV_FIELD(x, y))
15916 if (player->MovPos == 0)
15917 player->is_pushing = FALSE;
15919 player->is_snapping = FALSE;
15921 if (player->MovPos == 0)
15923 player->is_moving = FALSE;
15924 player->is_digging = FALSE;
15925 player->is_collecting = FALSE;
15931 #if USE_NEW_CONTINUOUS_SNAPPING
15932 /* prevent snapping with already pressed snap key when not allowed */
15933 if (player->is_snapping && !can_continue_snapping)
15936 if (player->is_snapping)
15940 player->MovDir = snap_direction;
15942 if (player->MovPos == 0)
15944 player->is_moving = FALSE;
15945 player->is_digging = FALSE;
15946 player->is_collecting = FALSE;
15949 player->is_dropping = FALSE;
15950 player->is_dropping_pressed = FALSE;
15951 player->drop_pressed_delay = 0;
15953 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15956 player->is_snapping = TRUE;
15957 player->is_active = TRUE;
15959 if (player->MovPos == 0)
15961 player->is_moving = FALSE;
15962 player->is_digging = FALSE;
15963 player->is_collecting = FALSE;
15966 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15967 TEST_DrawLevelField(player->last_jx, player->last_jy);
15969 TEST_DrawLevelField(x, y);
15974 static boolean DropElement(struct PlayerInfo *player)
15976 int old_element, new_element;
15977 int dropx = player->jx, dropy = player->jy;
15978 int drop_direction = player->MovDir;
15979 int drop_side = drop_direction;
15981 int drop_element = get_next_dropped_element(player);
15983 int drop_element = (player->inventory_size > 0 ?
15984 player->inventory_element[player->inventory_size - 1] :
15985 player->inventory_infinite_element != EL_UNDEFINED ?
15986 player->inventory_infinite_element :
15987 player->dynabombs_left > 0 ?
15988 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15992 player->is_dropping_pressed = TRUE;
15994 /* do not drop an element on top of another element; when holding drop key
15995 pressed without moving, dropped element must move away before the next
15996 element can be dropped (this is especially important if the next element
15997 is dynamite, which can be placed on background for historical reasons) */
15998 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
16001 if (IS_THROWABLE(drop_element))
16003 dropx += GET_DX_FROM_DIR(drop_direction);
16004 dropy += GET_DY_FROM_DIR(drop_direction);
16006 if (!IN_LEV_FIELD(dropx, dropy))
16010 old_element = Feld[dropx][dropy]; /* old element at dropping position */
16011 new_element = drop_element; /* default: no change when dropping */
16013 /* check if player is active, not moving and ready to drop */
16014 if (!player->active || player->MovPos || player->drop_delay > 0)
16017 /* check if player has anything that can be dropped */
16018 if (new_element == EL_UNDEFINED)
16021 /* check if drop key was pressed long enough for EM style dynamite */
16022 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
16025 /* check if anything can be dropped at the current position */
16026 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
16029 /* collected custom elements can only be dropped on empty fields */
16030 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16033 if (old_element != EL_EMPTY)
16034 Back[dropx][dropy] = old_element; /* store old element on this field */
16036 ResetGfxAnimation(dropx, dropy);
16037 ResetRandomAnimationValue(dropx, dropy);
16039 if (player->inventory_size > 0 ||
16040 player->inventory_infinite_element != EL_UNDEFINED)
16042 if (player->inventory_size > 0)
16044 player->inventory_size--;
16046 DrawGameDoorValues();
16048 if (new_element == EL_DYNAMITE)
16049 new_element = EL_DYNAMITE_ACTIVE;
16050 else if (new_element == EL_EM_DYNAMITE)
16051 new_element = EL_EM_DYNAMITE_ACTIVE;
16052 else if (new_element == EL_SP_DISK_RED)
16053 new_element = EL_SP_DISK_RED_ACTIVE;
16056 Feld[dropx][dropy] = new_element;
16058 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16059 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16060 el2img(Feld[dropx][dropy]), 0);
16062 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16064 /* needed if previous element just changed to "empty" in the last frame */
16065 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16067 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16068 player->index_bit, drop_side);
16069 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16071 player->index_bit, drop_side);
16073 TestIfElementTouchesCustomElement(dropx, dropy);
16075 else /* player is dropping a dyna bomb */
16077 player->dynabombs_left--;
16079 Feld[dropx][dropy] = new_element;
16081 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16082 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16083 el2img(Feld[dropx][dropy]), 0);
16085 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16088 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16089 InitField_WithBug1(dropx, dropy, FALSE);
16091 new_element = Feld[dropx][dropy]; /* element might have changed */
16093 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16094 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16097 int move_direction;
16101 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16102 MovDir[dropx][dropy] = drop_direction;
16105 move_direction = MovDir[dropx][dropy];
16106 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16107 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16110 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16112 #if USE_FIX_IMPACT_COLLISION
16113 /* do not cause impact style collision by dropping elements that can fall */
16114 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16116 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16120 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16121 player->is_dropping = TRUE;
16123 player->drop_pressed_delay = 0;
16124 player->is_dropping_pressed = FALSE;
16126 player->drop_x = dropx;
16127 player->drop_y = dropy;
16132 /* ------------------------------------------------------------------------- */
16133 /* game sound playing functions */
16134 /* ------------------------------------------------------------------------- */
16136 static int *loop_sound_frame = NULL;
16137 static int *loop_sound_volume = NULL;
16139 void InitPlayLevelSound()
16141 int num_sounds = getSoundListSize();
16143 checked_free(loop_sound_frame);
16144 checked_free(loop_sound_volume);
16146 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16147 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16150 static void PlayLevelSound(int x, int y, int nr)
16152 int sx = SCREENX(x), sy = SCREENY(y);
16153 int volume, stereo_position;
16154 int max_distance = 8;
16155 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16157 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16158 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16161 if (!IN_LEV_FIELD(x, y) ||
16162 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16163 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16166 volume = SOUND_MAX_VOLUME;
16168 if (!IN_SCR_FIELD(sx, sy))
16170 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16171 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16173 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16176 stereo_position = (SOUND_MAX_LEFT +
16177 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16178 (SCR_FIELDX + 2 * max_distance));
16180 if (IS_LOOP_SOUND(nr))
16182 /* This assures that quieter loop sounds do not overwrite louder ones,
16183 while restarting sound volume comparison with each new game frame. */
16185 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16188 loop_sound_volume[nr] = volume;
16189 loop_sound_frame[nr] = FrameCounter;
16192 PlaySoundExt(nr, volume, stereo_position, type);
16195 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16197 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16198 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16199 y < LEVELY(BY1) ? LEVELY(BY1) :
16200 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16204 static void PlayLevelSoundAction(int x, int y, int action)
16206 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16209 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16211 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16213 if (sound_effect != SND_UNDEFINED)
16214 PlayLevelSound(x, y, sound_effect);
16217 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16220 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16222 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16223 PlayLevelSound(x, y, sound_effect);
16226 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16228 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16230 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16231 PlayLevelSound(x, y, sound_effect);
16234 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16236 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16238 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16239 StopSound(sound_effect);
16242 static void PlayLevelMusic()
16244 if (levelset.music[level_nr] != MUS_UNDEFINED)
16245 PlayMusic(levelset.music[level_nr]); /* from config file */
16247 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16250 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16252 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16253 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16254 int x = xx - 1 - offset;
16255 int y = yy - 1 - offset;
16260 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16264 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16268 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16272 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16276 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16280 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16284 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16287 case SAMPLE_android_clone:
16288 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16291 case SAMPLE_android_move:
16292 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16295 case SAMPLE_spring:
16296 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16300 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16304 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16307 case SAMPLE_eater_eat:
16308 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16312 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16315 case SAMPLE_collect:
16316 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16319 case SAMPLE_diamond:
16320 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16323 case SAMPLE_squash:
16324 /* !!! CHECK THIS !!! */
16326 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16328 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16332 case SAMPLE_wonderfall:
16333 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16337 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16341 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16345 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16349 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16353 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16357 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16360 case SAMPLE_wonder:
16361 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16365 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16368 case SAMPLE_exit_open:
16369 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16372 case SAMPLE_exit_leave:
16373 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16376 case SAMPLE_dynamite:
16377 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16381 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16385 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16389 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16393 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16397 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16401 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16405 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16410 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16412 int element = map_element_SP_to_RND(element_sp);
16413 int action = map_action_SP_to_RND(action_sp);
16414 int offset = (setup.sp_show_border_elements ? 0 : 1);
16415 int x = xx - offset;
16416 int y = yy - offset;
16419 printf("::: %d -> %d\n", element_sp, action_sp);
16422 PlayLevelSoundElementAction(x, y, element, action);
16425 void RaiseScore(int value)
16427 local_player->score += value;
16430 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16432 DisplayGameControlValues();
16434 DrawGameValue_Score(local_player->score);
16438 void RaiseScoreElement(int element)
16443 case EL_BD_DIAMOND:
16444 case EL_EMERALD_YELLOW:
16445 case EL_EMERALD_RED:
16446 case EL_EMERALD_PURPLE:
16447 case EL_SP_INFOTRON:
16448 RaiseScore(level.score[SC_EMERALD]);
16451 RaiseScore(level.score[SC_DIAMOND]);
16454 RaiseScore(level.score[SC_CRYSTAL]);
16457 RaiseScore(level.score[SC_PEARL]);
16460 case EL_BD_BUTTERFLY:
16461 case EL_SP_ELECTRON:
16462 RaiseScore(level.score[SC_BUG]);
16465 case EL_BD_FIREFLY:
16466 case EL_SP_SNIKSNAK:
16467 RaiseScore(level.score[SC_SPACESHIP]);
16470 case EL_DARK_YAMYAM:
16471 RaiseScore(level.score[SC_YAMYAM]);
16474 RaiseScore(level.score[SC_ROBOT]);
16477 RaiseScore(level.score[SC_PACMAN]);
16480 RaiseScore(level.score[SC_NUT]);
16483 case EL_EM_DYNAMITE:
16484 case EL_SP_DISK_RED:
16485 case EL_DYNABOMB_INCREASE_NUMBER:
16486 case EL_DYNABOMB_INCREASE_SIZE:
16487 case EL_DYNABOMB_INCREASE_POWER:
16488 RaiseScore(level.score[SC_DYNAMITE]);
16490 case EL_SHIELD_NORMAL:
16491 case EL_SHIELD_DEADLY:
16492 RaiseScore(level.score[SC_SHIELD]);
16494 case EL_EXTRA_TIME:
16495 RaiseScore(level.extra_time_score);
16509 case EL_DC_KEY_WHITE:
16510 RaiseScore(level.score[SC_KEY]);
16513 RaiseScore(element_info[element].collect_score);
16518 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16520 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16523 /* closing door required in case of envelope style request dialogs */
16525 CloseDoor(DOOR_CLOSE_1);
16528 #if defined(NETWORK_AVALIABLE)
16529 if (options.network)
16530 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16539 FadeSkipNextFadeIn();
16541 fading = fading_none;
16545 OpenDoor(DOOR_CLOSE_1);
16548 game_status = GAME_MODE_MAIN;
16551 DrawAndFadeInMainMenu(REDRAW_FIELD);
16559 FadeOut(REDRAW_FIELD);
16562 game_status = GAME_MODE_MAIN;
16564 DrawAndFadeInMainMenu(REDRAW_FIELD);
16568 else /* continue playing the game */
16570 if (tape.playing && tape.deactivate_display)
16571 TapeDeactivateDisplayOff(TRUE);
16573 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16575 if (tape.playing && tape.deactivate_display)
16576 TapeDeactivateDisplayOn();
16580 void RequestQuitGame(boolean ask_if_really_quit)
16582 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16583 boolean skip_request = AllPlayersGone || quick_quit;
16585 RequestQuitGameExt(skip_request, quick_quit,
16586 "Do you really want to quit the game?");
16590 /* ------------------------------------------------------------------------- */
16591 /* random generator functions */
16592 /* ------------------------------------------------------------------------- */
16594 unsigned int InitEngineRandom_RND(int seed)
16596 game.num_random_calls = 0;
16599 unsigned int rnd_seed = InitEngineRandom(seed);
16601 printf("::: START RND: %d\n", rnd_seed);
16606 return InitEngineRandom(seed);
16612 unsigned int RND(int max)
16616 game.num_random_calls++;
16618 return GetEngineRandom(max);
16625 /* ------------------------------------------------------------------------- */
16626 /* game engine snapshot handling functions */
16627 /* ------------------------------------------------------------------------- */
16629 struct EngineSnapshotInfo
16631 /* runtime values for custom element collect score */
16632 int collect_score[NUM_CUSTOM_ELEMENTS];
16634 /* runtime values for group element choice position */
16635 int choice_pos[NUM_GROUP_ELEMENTS];
16637 /* runtime values for belt position animations */
16638 int belt_graphic[4][NUM_BELT_PARTS];
16639 int belt_anim_mode[4][NUM_BELT_PARTS];
16642 static struct EngineSnapshotInfo engine_snapshot_rnd;
16643 static char *snapshot_level_identifier = NULL;
16644 static int snapshot_level_nr = -1;
16646 static void SaveEngineSnapshotValues_RND()
16648 static int belt_base_active_element[4] =
16650 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16651 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16652 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16653 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16657 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16659 int element = EL_CUSTOM_START + i;
16661 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16664 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16666 int element = EL_GROUP_START + i;
16668 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16671 for (i = 0; i < 4; i++)
16673 for (j = 0; j < NUM_BELT_PARTS; j++)
16675 int element = belt_base_active_element[i] + j;
16676 int graphic = el2img(element);
16677 int anim_mode = graphic_info[graphic].anim_mode;
16679 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16680 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16685 static void LoadEngineSnapshotValues_RND()
16687 unsigned int num_random_calls = game.num_random_calls;
16690 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16692 int element = EL_CUSTOM_START + i;
16694 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16697 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16699 int element = EL_GROUP_START + i;
16701 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16704 for (i = 0; i < 4; i++)
16706 for (j = 0; j < NUM_BELT_PARTS; j++)
16708 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16709 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16711 graphic_info[graphic].anim_mode = anim_mode;
16715 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16717 InitRND(tape.random_seed);
16718 for (i = 0; i < num_random_calls; i++)
16722 if (game.num_random_calls != num_random_calls)
16724 Error(ERR_INFO, "number of random calls out of sync");
16725 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16726 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16727 Error(ERR_EXIT, "this should not happen -- please debug");
16731 void FreeEngineSnapshot()
16733 FreeEngineSnapshotBuffers();
16735 setString(&snapshot_level_identifier, NULL);
16736 snapshot_level_nr = -1;
16739 void SaveEngineSnapshot()
16741 /* do not save snapshots from editor */
16742 if (level_editor_test_game)
16745 /* free previous snapshot buffers, if needed */
16746 FreeEngineSnapshotBuffers();
16749 /* copy some special values to a structure better suited for the snapshot */
16751 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16752 SaveEngineSnapshotValues_RND();
16753 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16754 SaveEngineSnapshotValues_EM();
16755 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16756 SaveEngineSnapshotValues_SP();
16758 /* save values stored in special snapshot structure */
16760 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16761 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16762 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16764 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16765 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16767 /* copy some special values to a structure better suited for the snapshot */
16769 SaveEngineSnapshotValues_RND();
16770 SaveEngineSnapshotValues_EM();
16771 SaveEngineSnapshotValues_SP();
16773 /* save values stored in special snapshot structure */
16775 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16776 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16780 /* save further RND engine values */
16782 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16783 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16784 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16786 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16788 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16791 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16792 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16793 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16794 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16795 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16797 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16798 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16799 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16801 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16805 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16806 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16808 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16810 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16811 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16812 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16813 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16814 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16815 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16816 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16817 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16818 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16819 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16820 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16821 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16822 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16823 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16824 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16825 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16827 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16828 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16830 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16831 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16832 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16834 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16835 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16837 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16838 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16839 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16840 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16841 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16843 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16844 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16846 /* save level identification information */
16848 setString(&snapshot_level_identifier, leveldir_current->identifier);
16849 snapshot_level_nr = level_nr;
16852 ListNode *node = engine_snapshot_list_rnd;
16855 while (node != NULL)
16857 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16862 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16866 void LoadEngineSnapshot()
16868 /* restore generically stored snapshot buffers */
16870 LoadEngineSnapshotBuffers();
16872 /* restore special values from snapshot structure */
16875 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16876 LoadEngineSnapshotValues_RND();
16877 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16878 LoadEngineSnapshotValues_EM();
16879 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16880 LoadEngineSnapshotValues_SP();
16882 LoadEngineSnapshotValues_RND();
16883 LoadEngineSnapshotValues_EM();
16884 LoadEngineSnapshotValues_SP();
16888 printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16892 // needed if tile size was different when saving and loading engine snapshot
16893 if (local_player->present)
16895 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
16896 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
16897 local_player->jx - MIDPOSX);
16899 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
16900 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
16901 local_player->jy - MIDPOSY);
16906 printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16910 boolean CheckEngineSnapshot()
16912 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16913 snapshot_level_nr == level_nr);
16917 /* ---------- new game button stuff ---------------------------------------- */
16925 } gamebutton_info[NUM_GAME_BUTTONS] =
16928 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16929 GAME_CTRL_ID_STOP, "stop game"
16932 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16933 GAME_CTRL_ID_PAUSE, "pause game"
16936 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16937 GAME_CTRL_ID_PLAY, "play game"
16940 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16941 SOUND_CTRL_ID_MUSIC, "background music on/off"
16944 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16945 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16948 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16949 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16952 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
16953 GAME_CTRL_ID_SAVE, "save game"
16956 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
16957 GAME_CTRL_ID_LOAD, "load game"
16961 void CreateGameButtons()
16965 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16967 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16968 struct Rect *pos = gamebutton_info[i].pos;
16969 struct GadgetInfo *gi;
16972 unsigned int event_mask;
16973 int base_x = (tape.show_game_buttons ? VX : DX);
16974 int base_y = (tape.show_game_buttons ? VY : DY);
16975 int gd_x = gfx->src_x;
16976 int gd_y = gfx->src_y;
16977 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16978 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16979 int gd_xa = gfx->src_x + gfx->active_xoffset;
16980 int gd_ya = gfx->src_y + gfx->active_yoffset;
16981 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16982 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16985 if (gfx->bitmap == NULL)
16987 game_gadget[id] = NULL;
16992 if (id == GAME_CTRL_ID_STOP ||
16993 id == GAME_CTRL_ID_PAUSE ||
16994 id == GAME_CTRL_ID_PLAY ||
16995 id == GAME_CTRL_ID_SAVE ||
16996 id == GAME_CTRL_ID_LOAD)
16998 button_type = GD_TYPE_NORMAL_BUTTON;
17000 event_mask = GD_EVENT_RELEASED;
17004 button_type = GD_TYPE_CHECK_BUTTON;
17006 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
17007 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
17008 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
17009 event_mask = GD_EVENT_PRESSED;
17012 gi = CreateGadget(GDI_CUSTOM_ID, id,
17013 GDI_INFO_TEXT, gamebutton_info[i].infotext,
17014 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
17015 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
17016 GDI_WIDTH, gfx->width,
17017 GDI_HEIGHT, gfx->height,
17018 GDI_TYPE, button_type,
17019 GDI_STATE, GD_BUTTON_UNPRESSED,
17020 GDI_CHECKED, checked,
17021 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
17022 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
17023 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
17024 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
17025 GDI_DIRECT_DRAW, FALSE,
17026 GDI_EVENT_MASK, event_mask,
17027 GDI_CALLBACK_ACTION, HandleGameButtons,
17031 Error(ERR_EXIT, "cannot create gadget");
17033 game_gadget[id] = gi;
17037 void FreeGameButtons()
17041 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17042 FreeGadget(game_gadget[i]);
17045 void MapGameButtons()
17049 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17050 MapGadget(game_gadget[i]);
17053 void UnmapGameButtons()
17057 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17058 UnmapGadget(game_gadget[i]);
17061 void RedrawGameButtons()
17065 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17066 RedrawGadget(game_gadget[i]);
17069 static void HandleGameButtonsExt(int id)
17071 boolean handle_game_buttons =
17072 (game_status == GAME_MODE_PLAYING ||
17073 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17075 if (!handle_game_buttons)
17080 case GAME_CTRL_ID_STOP:
17081 if (game_status == GAME_MODE_MAIN)
17087 RequestQuitGame(TRUE);
17091 case GAME_CTRL_ID_PAUSE:
17092 if (options.network && game_status == GAME_MODE_PLAYING)
17094 #if defined(NETWORK_AVALIABLE)
17096 SendToServer_ContinuePlaying();
17098 SendToServer_PausePlaying();
17102 TapeTogglePause(TAPE_TOGGLE_MANUAL);
17105 case GAME_CTRL_ID_PLAY:
17106 if (game_status == GAME_MODE_MAIN)
17108 StartGameActions(options.network, setup.autorecord, level.random_seed);
17110 else if (tape.pausing)
17112 #if defined(NETWORK_AVALIABLE)
17113 if (options.network)
17114 SendToServer_ContinuePlaying();
17118 tape.pausing = FALSE;
17119 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17124 case SOUND_CTRL_ID_MUSIC:
17125 if (setup.sound_music)
17127 setup.sound_music = FALSE;
17131 else if (audio.music_available)
17133 setup.sound = setup.sound_music = TRUE;
17135 SetAudioMode(setup.sound);
17141 case SOUND_CTRL_ID_LOOPS:
17142 if (setup.sound_loops)
17143 setup.sound_loops = FALSE;
17144 else if (audio.loops_available)
17146 setup.sound = setup.sound_loops = TRUE;
17148 SetAudioMode(setup.sound);
17152 case SOUND_CTRL_ID_SIMPLE:
17153 if (setup.sound_simple)
17154 setup.sound_simple = FALSE;
17155 else if (audio.sound_available)
17157 setup.sound = setup.sound_simple = TRUE;
17159 SetAudioMode(setup.sound);
17163 case GAME_CTRL_ID_SAVE:
17167 case GAME_CTRL_ID_LOAD:
17176 static void HandleGameButtons(struct GadgetInfo *gi)
17178 HandleGameButtonsExt(gi->custom_id);
17181 void HandleSoundButtonKeys(Key key)
17184 if (key == setup.shortcut.sound_simple)
17185 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17186 else if (key == setup.shortcut.sound_loops)
17187 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17188 else if (key == setup.shortcut.sound_music)
17189 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17191 if (key == setup.shortcut.sound_simple)
17192 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17193 else if (key == setup.shortcut.sound_loops)
17194 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17195 else if (key == setup.shortcut.sound_music)
17196 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);