1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
24 #define DEBUG_INIT_PLAYER 1
25 #define DEBUG_PLAYER_ACTIONS 0
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE FALSE
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF ( 1)
33 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
35 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
36 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
37 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
38 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
39 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
40 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
41 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
42 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
43 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
44 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
45 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
46 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
47 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
48 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
49 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
50 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
51 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
53 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
55 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
57 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
59 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
60 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
62 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
63 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
64 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
65 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
67 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
69 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
71 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
73 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
75 #if USE_DELAYED_GFX_REDRAW
76 #define TEST_DrawLevelField(x, y) \
77 GfxRedraw[x][y] |= GFX_REDRAW_TILE
78 #define TEST_DrawLevelFieldCrumbled(x, y) \
79 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
80 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
81 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
82 #define TEST_DrawTwinkleOnField(x, y) \
83 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
85 #define TEST_DrawLevelField(x, y) \
87 #define TEST_DrawLevelFieldCrumbled(x, y) \
88 DrawLevelFieldCrumbled(x, y)
89 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
90 DrawLevelFieldCrumbledNeighbours(x, y)
91 #define TEST_DrawTwinkleOnField(x, y) \
92 DrawTwinkleOnField(x, y)
101 /* for MovePlayer() */
102 #define MP_NO_ACTION 0
105 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
107 /* for ScrollPlayer() */
108 #define SCROLL_INIT 0
109 #define SCROLL_GO_ON 1
111 /* for Bang()/Explode() */
112 #define EX_PHASE_START 0
113 #define EX_TYPE_NONE 0
114 #define EX_TYPE_NORMAL (1 << 0)
115 #define EX_TYPE_CENTER (1 << 1)
116 #define EX_TYPE_BORDER (1 << 2)
117 #define EX_TYPE_CROSS (1 << 3)
118 #define EX_TYPE_DYNA (1 << 4)
119 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
121 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
122 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
123 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
124 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
126 /* game panel display and control definitions */
127 #define GAME_PANEL_LEVEL_NUMBER 0
128 #define GAME_PANEL_GEMS 1
129 #define GAME_PANEL_INVENTORY_COUNT 2
130 #define GAME_PANEL_INVENTORY_FIRST_1 3
131 #define GAME_PANEL_INVENTORY_FIRST_2 4
132 #define GAME_PANEL_INVENTORY_FIRST_3 5
133 #define GAME_PANEL_INVENTORY_FIRST_4 6
134 #define GAME_PANEL_INVENTORY_FIRST_5 7
135 #define GAME_PANEL_INVENTORY_FIRST_6 8
136 #define GAME_PANEL_INVENTORY_FIRST_7 9
137 #define GAME_PANEL_INVENTORY_FIRST_8 10
138 #define GAME_PANEL_INVENTORY_LAST_1 11
139 #define GAME_PANEL_INVENTORY_LAST_2 12
140 #define GAME_PANEL_INVENTORY_LAST_3 13
141 #define GAME_PANEL_INVENTORY_LAST_4 14
142 #define GAME_PANEL_INVENTORY_LAST_5 15
143 #define GAME_PANEL_INVENTORY_LAST_6 16
144 #define GAME_PANEL_INVENTORY_LAST_7 17
145 #define GAME_PANEL_INVENTORY_LAST_8 18
146 #define GAME_PANEL_KEY_1 19
147 #define GAME_PANEL_KEY_2 20
148 #define GAME_PANEL_KEY_3 21
149 #define GAME_PANEL_KEY_4 22
150 #define GAME_PANEL_KEY_5 23
151 #define GAME_PANEL_KEY_6 24
152 #define GAME_PANEL_KEY_7 25
153 #define GAME_PANEL_KEY_8 26
154 #define GAME_PANEL_KEY_WHITE 27
155 #define GAME_PANEL_KEY_WHITE_COUNT 28
156 #define GAME_PANEL_SCORE 29
157 #define GAME_PANEL_HIGHSCORE 30
158 #define GAME_PANEL_TIME 31
159 #define GAME_PANEL_TIME_HH 32
160 #define GAME_PANEL_TIME_MM 33
161 #define GAME_PANEL_TIME_SS 34
162 #define GAME_PANEL_FRAME 35
163 #define GAME_PANEL_SHIELD_NORMAL 36
164 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
165 #define GAME_PANEL_SHIELD_DEADLY 38
166 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
167 #define GAME_PANEL_EXIT 40
168 #define GAME_PANEL_EMC_MAGIC_BALL 41
169 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
170 #define GAME_PANEL_LIGHT_SWITCH 43
171 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
172 #define GAME_PANEL_TIMEGATE_SWITCH 45
173 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
174 #define GAME_PANEL_SWITCHGATE_SWITCH 47
175 #define GAME_PANEL_EMC_LENSES 48
176 #define GAME_PANEL_EMC_LENSES_TIME 49
177 #define GAME_PANEL_EMC_MAGNIFIER 50
178 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
179 #define GAME_PANEL_BALLOON_SWITCH 52
180 #define GAME_PANEL_DYNABOMB_NUMBER 53
181 #define GAME_PANEL_DYNABOMB_SIZE 54
182 #define GAME_PANEL_DYNABOMB_POWER 55
183 #define GAME_PANEL_PENGUINS 56
184 #define GAME_PANEL_SOKOBAN_OBJECTS 57
185 #define GAME_PANEL_SOKOBAN_FIELDS 58
186 #define GAME_PANEL_ROBOT_WHEEL 59
187 #define GAME_PANEL_CONVEYOR_BELT_1 60
188 #define GAME_PANEL_CONVEYOR_BELT_2 61
189 #define GAME_PANEL_CONVEYOR_BELT_3 62
190 #define GAME_PANEL_CONVEYOR_BELT_4 63
191 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
192 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
193 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
194 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
195 #define GAME_PANEL_MAGIC_WALL 68
196 #define GAME_PANEL_MAGIC_WALL_TIME 69
197 #define GAME_PANEL_GRAVITY_STATE 70
198 #define GAME_PANEL_GRAPHIC_1 71
199 #define GAME_PANEL_GRAPHIC_2 72
200 #define GAME_PANEL_GRAPHIC_3 73
201 #define GAME_PANEL_GRAPHIC_4 74
202 #define GAME_PANEL_GRAPHIC_5 75
203 #define GAME_PANEL_GRAPHIC_6 76
204 #define GAME_PANEL_GRAPHIC_7 77
205 #define GAME_PANEL_GRAPHIC_8 78
206 #define GAME_PANEL_ELEMENT_1 79
207 #define GAME_PANEL_ELEMENT_2 80
208 #define GAME_PANEL_ELEMENT_3 81
209 #define GAME_PANEL_ELEMENT_4 82
210 #define GAME_PANEL_ELEMENT_5 83
211 #define GAME_PANEL_ELEMENT_6 84
212 #define GAME_PANEL_ELEMENT_7 85
213 #define GAME_PANEL_ELEMENT_8 86
214 #define GAME_PANEL_ELEMENT_COUNT_1 87
215 #define GAME_PANEL_ELEMENT_COUNT_2 88
216 #define GAME_PANEL_ELEMENT_COUNT_3 89
217 #define GAME_PANEL_ELEMENT_COUNT_4 90
218 #define GAME_PANEL_ELEMENT_COUNT_5 91
219 #define GAME_PANEL_ELEMENT_COUNT_6 92
220 #define GAME_PANEL_ELEMENT_COUNT_7 93
221 #define GAME_PANEL_ELEMENT_COUNT_8 94
222 #define GAME_PANEL_CE_SCORE_1 95
223 #define GAME_PANEL_CE_SCORE_2 96
224 #define GAME_PANEL_CE_SCORE_3 97
225 #define GAME_PANEL_CE_SCORE_4 98
226 #define GAME_PANEL_CE_SCORE_5 99
227 #define GAME_PANEL_CE_SCORE_6 100
228 #define GAME_PANEL_CE_SCORE_7 101
229 #define GAME_PANEL_CE_SCORE_8 102
230 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
231 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
232 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
233 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
234 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
235 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
236 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
237 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
238 #define GAME_PANEL_PLAYER_NAME 111
239 #define GAME_PANEL_LEVEL_NAME 112
240 #define GAME_PANEL_LEVEL_AUTHOR 113
242 #define NUM_GAME_PANEL_CONTROLS 114
244 struct GamePanelOrderInfo
250 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
252 struct GamePanelControlInfo
256 struct TextPosInfo *pos;
259 int value, last_value;
260 int frame, last_frame;
265 static struct GamePanelControlInfo game_panel_controls[] =
268 GAME_PANEL_LEVEL_NUMBER,
269 &game.panel.level_number,
278 GAME_PANEL_INVENTORY_COUNT,
279 &game.panel.inventory_count,
283 GAME_PANEL_INVENTORY_FIRST_1,
284 &game.panel.inventory_first[0],
288 GAME_PANEL_INVENTORY_FIRST_2,
289 &game.panel.inventory_first[1],
293 GAME_PANEL_INVENTORY_FIRST_3,
294 &game.panel.inventory_first[2],
298 GAME_PANEL_INVENTORY_FIRST_4,
299 &game.panel.inventory_first[3],
303 GAME_PANEL_INVENTORY_FIRST_5,
304 &game.panel.inventory_first[4],
308 GAME_PANEL_INVENTORY_FIRST_6,
309 &game.panel.inventory_first[5],
313 GAME_PANEL_INVENTORY_FIRST_7,
314 &game.panel.inventory_first[6],
318 GAME_PANEL_INVENTORY_FIRST_8,
319 &game.panel.inventory_first[7],
323 GAME_PANEL_INVENTORY_LAST_1,
324 &game.panel.inventory_last[0],
328 GAME_PANEL_INVENTORY_LAST_2,
329 &game.panel.inventory_last[1],
333 GAME_PANEL_INVENTORY_LAST_3,
334 &game.panel.inventory_last[2],
338 GAME_PANEL_INVENTORY_LAST_4,
339 &game.panel.inventory_last[3],
343 GAME_PANEL_INVENTORY_LAST_5,
344 &game.panel.inventory_last[4],
348 GAME_PANEL_INVENTORY_LAST_6,
349 &game.panel.inventory_last[5],
353 GAME_PANEL_INVENTORY_LAST_7,
354 &game.panel.inventory_last[6],
358 GAME_PANEL_INVENTORY_LAST_8,
359 &game.panel.inventory_last[7],
403 GAME_PANEL_KEY_WHITE,
404 &game.panel.key_white,
408 GAME_PANEL_KEY_WHITE_COUNT,
409 &game.panel.key_white_count,
418 GAME_PANEL_HIGHSCORE,
419 &game.panel.highscore,
448 GAME_PANEL_SHIELD_NORMAL,
449 &game.panel.shield_normal,
453 GAME_PANEL_SHIELD_NORMAL_TIME,
454 &game.panel.shield_normal_time,
458 GAME_PANEL_SHIELD_DEADLY,
459 &game.panel.shield_deadly,
463 GAME_PANEL_SHIELD_DEADLY_TIME,
464 &game.panel.shield_deadly_time,
473 GAME_PANEL_EMC_MAGIC_BALL,
474 &game.panel.emc_magic_ball,
478 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
479 &game.panel.emc_magic_ball_switch,
483 GAME_PANEL_LIGHT_SWITCH,
484 &game.panel.light_switch,
488 GAME_PANEL_LIGHT_SWITCH_TIME,
489 &game.panel.light_switch_time,
493 GAME_PANEL_TIMEGATE_SWITCH,
494 &game.panel.timegate_switch,
498 GAME_PANEL_TIMEGATE_SWITCH_TIME,
499 &game.panel.timegate_switch_time,
503 GAME_PANEL_SWITCHGATE_SWITCH,
504 &game.panel.switchgate_switch,
508 GAME_PANEL_EMC_LENSES,
509 &game.panel.emc_lenses,
513 GAME_PANEL_EMC_LENSES_TIME,
514 &game.panel.emc_lenses_time,
518 GAME_PANEL_EMC_MAGNIFIER,
519 &game.panel.emc_magnifier,
523 GAME_PANEL_EMC_MAGNIFIER_TIME,
524 &game.panel.emc_magnifier_time,
528 GAME_PANEL_BALLOON_SWITCH,
529 &game.panel.balloon_switch,
533 GAME_PANEL_DYNABOMB_NUMBER,
534 &game.panel.dynabomb_number,
538 GAME_PANEL_DYNABOMB_SIZE,
539 &game.panel.dynabomb_size,
543 GAME_PANEL_DYNABOMB_POWER,
544 &game.panel.dynabomb_power,
549 &game.panel.penguins,
553 GAME_PANEL_SOKOBAN_OBJECTS,
554 &game.panel.sokoban_objects,
558 GAME_PANEL_SOKOBAN_FIELDS,
559 &game.panel.sokoban_fields,
563 GAME_PANEL_ROBOT_WHEEL,
564 &game.panel.robot_wheel,
568 GAME_PANEL_CONVEYOR_BELT_1,
569 &game.panel.conveyor_belt[0],
573 GAME_PANEL_CONVEYOR_BELT_2,
574 &game.panel.conveyor_belt[1],
578 GAME_PANEL_CONVEYOR_BELT_3,
579 &game.panel.conveyor_belt[2],
583 GAME_PANEL_CONVEYOR_BELT_4,
584 &game.panel.conveyor_belt[3],
588 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
589 &game.panel.conveyor_belt_switch[0],
593 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
594 &game.panel.conveyor_belt_switch[1],
598 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
599 &game.panel.conveyor_belt_switch[2],
603 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
604 &game.panel.conveyor_belt_switch[3],
608 GAME_PANEL_MAGIC_WALL,
609 &game.panel.magic_wall,
613 GAME_PANEL_MAGIC_WALL_TIME,
614 &game.panel.magic_wall_time,
618 GAME_PANEL_GRAVITY_STATE,
619 &game.panel.gravity_state,
623 GAME_PANEL_GRAPHIC_1,
624 &game.panel.graphic[0],
628 GAME_PANEL_GRAPHIC_2,
629 &game.panel.graphic[1],
633 GAME_PANEL_GRAPHIC_3,
634 &game.panel.graphic[2],
638 GAME_PANEL_GRAPHIC_4,
639 &game.panel.graphic[3],
643 GAME_PANEL_GRAPHIC_5,
644 &game.panel.graphic[4],
648 GAME_PANEL_GRAPHIC_6,
649 &game.panel.graphic[5],
653 GAME_PANEL_GRAPHIC_7,
654 &game.panel.graphic[6],
658 GAME_PANEL_GRAPHIC_8,
659 &game.panel.graphic[7],
663 GAME_PANEL_ELEMENT_1,
664 &game.panel.element[0],
668 GAME_PANEL_ELEMENT_2,
669 &game.panel.element[1],
673 GAME_PANEL_ELEMENT_3,
674 &game.panel.element[2],
678 GAME_PANEL_ELEMENT_4,
679 &game.panel.element[3],
683 GAME_PANEL_ELEMENT_5,
684 &game.panel.element[4],
688 GAME_PANEL_ELEMENT_6,
689 &game.panel.element[5],
693 GAME_PANEL_ELEMENT_7,
694 &game.panel.element[6],
698 GAME_PANEL_ELEMENT_8,
699 &game.panel.element[7],
703 GAME_PANEL_ELEMENT_COUNT_1,
704 &game.panel.element_count[0],
708 GAME_PANEL_ELEMENT_COUNT_2,
709 &game.panel.element_count[1],
713 GAME_PANEL_ELEMENT_COUNT_3,
714 &game.panel.element_count[2],
718 GAME_PANEL_ELEMENT_COUNT_4,
719 &game.panel.element_count[3],
723 GAME_PANEL_ELEMENT_COUNT_5,
724 &game.panel.element_count[4],
728 GAME_PANEL_ELEMENT_COUNT_6,
729 &game.panel.element_count[5],
733 GAME_PANEL_ELEMENT_COUNT_7,
734 &game.panel.element_count[6],
738 GAME_PANEL_ELEMENT_COUNT_8,
739 &game.panel.element_count[7],
743 GAME_PANEL_CE_SCORE_1,
744 &game.panel.ce_score[0],
748 GAME_PANEL_CE_SCORE_2,
749 &game.panel.ce_score[1],
753 GAME_PANEL_CE_SCORE_3,
754 &game.panel.ce_score[2],
758 GAME_PANEL_CE_SCORE_4,
759 &game.panel.ce_score[3],
763 GAME_PANEL_CE_SCORE_5,
764 &game.panel.ce_score[4],
768 GAME_PANEL_CE_SCORE_6,
769 &game.panel.ce_score[5],
773 GAME_PANEL_CE_SCORE_7,
774 &game.panel.ce_score[6],
778 GAME_PANEL_CE_SCORE_8,
779 &game.panel.ce_score[7],
783 GAME_PANEL_CE_SCORE_1_ELEMENT,
784 &game.panel.ce_score_element[0],
788 GAME_PANEL_CE_SCORE_2_ELEMENT,
789 &game.panel.ce_score_element[1],
793 GAME_PANEL_CE_SCORE_3_ELEMENT,
794 &game.panel.ce_score_element[2],
798 GAME_PANEL_CE_SCORE_4_ELEMENT,
799 &game.panel.ce_score_element[3],
803 GAME_PANEL_CE_SCORE_5_ELEMENT,
804 &game.panel.ce_score_element[4],
808 GAME_PANEL_CE_SCORE_6_ELEMENT,
809 &game.panel.ce_score_element[5],
813 GAME_PANEL_CE_SCORE_7_ELEMENT,
814 &game.panel.ce_score_element[6],
818 GAME_PANEL_CE_SCORE_8_ELEMENT,
819 &game.panel.ce_score_element[7],
823 GAME_PANEL_PLAYER_NAME,
824 &game.panel.player_name,
828 GAME_PANEL_LEVEL_NAME,
829 &game.panel.level_name,
833 GAME_PANEL_LEVEL_AUTHOR,
834 &game.panel.level_author,
845 /* values for delayed check of falling and moving elements and for collision */
846 #define CHECK_DELAY_MOVING 3
847 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
848 #define CHECK_DELAY_COLLISION 2
849 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
851 /* values for initial player move delay (initial delay counter value) */
852 #define INITIAL_MOVE_DELAY_OFF -1
853 #define INITIAL_MOVE_DELAY_ON 0
855 /* values for player movement speed (which is in fact a delay value) */
856 #define MOVE_DELAY_MIN_SPEED 32
857 #define MOVE_DELAY_NORMAL_SPEED 8
858 #define MOVE_DELAY_HIGH_SPEED 4
859 #define MOVE_DELAY_MAX_SPEED 1
861 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
862 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
864 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
865 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
867 /* values for other actions */
868 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
869 #define MOVE_STEPSIZE_MIN (1)
870 #define MOVE_STEPSIZE_MAX (TILEX)
872 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
873 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
875 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
877 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
878 RND(element_info[e].push_delay_random))
879 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
880 RND(element_info[e].drop_delay_random))
881 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
882 RND(element_info[e].move_delay_random))
883 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
884 (element_info[e].move_delay_random))
885 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
886 RND(element_info[e].ce_value_random_initial))
887 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
888 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
889 RND((c)->delay_random * (c)->delay_frames))
890 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
891 RND((c)->delay_random))
894 #define GET_VALID_RUNTIME_ELEMENT(e) \
895 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
898 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
899 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
900 (be) + (e) - EL_SELF)
902 #define GET_PLAYER_FROM_BITS(p) \
903 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
906 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
907 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
908 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
909 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
910 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
911 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
912 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
913 RESOLVED_REFERENCE_ELEMENT(be, e) : \
916 #define CAN_GROW_INTO(e) \
917 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
920 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
923 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
924 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
925 (CAN_MOVE_INTO_ACID(e) && \
926 Feld[x][y] == EL_ACID) || \
929 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
930 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
931 (CAN_MOVE_INTO_ACID(e) && \
932 Feld[x][y] == EL_ACID) || \
935 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
936 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
938 (CAN_MOVE_INTO_ACID(e) && \
939 Feld[x][y] == EL_ACID) || \
940 (DONT_COLLIDE_WITH(e) && \
942 !PLAYER_ENEMY_PROTECTED(x, y))))
944 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
951 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
953 #define ANDROID_CAN_CLONE_FIELD(x, y) \
954 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
955 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
963 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
966 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
969 #define PIG_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
972 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
973 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
974 Feld[x][y] == EL_EM_EXIT_OPEN || \
975 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
976 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
977 IS_FOOD_PENGUIN(Feld[x][y])))
978 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
988 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
989 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
993 #define CE_ENTER_FIELD_COND(e, x, y) \
994 (!IS_PLAYER(x, y) && \
995 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
997 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1001 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1004 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1005 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1006 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008 /* game button identifiers */
1009 #define GAME_CTRL_ID_STOP 0
1010 #define GAME_CTRL_ID_PAUSE 1
1011 #define GAME_CTRL_ID_PLAY 2
1012 #define SOUND_CTRL_ID_MUSIC 3
1013 #define SOUND_CTRL_ID_LOOPS 4
1014 #define SOUND_CTRL_ID_SIMPLE 5
1015 #define GAME_CTRL_ID_SAVE 6
1016 #define GAME_CTRL_ID_LOAD 7
1018 #define NUM_GAME_BUTTONS 8
1021 /* forward declaration for internal use */
1023 static void CreateField(int, int, int);
1025 static void ResetGfxAnimation(int, int);
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1051 static void TestIfElementSmashesCustomElement(int, int, int);
1054 static void HandleElementChange(int, int, int);
1055 static void ExecuteCustomElementAction(int, int, int, int);
1056 static boolean ChangeElement(int, int, int, int);
1058 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1059 #define CheckTriggeredElementChange(x, y, e, ev) \
1060 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1061 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1062 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1063 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1064 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1065 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1066 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1068 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1069 #define CheckElementChange(x, y, e, te, ev) \
1070 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1071 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1072 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1073 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1074 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1076 static void PlayLevelSound(int, int, int);
1077 static void PlayLevelSoundNearest(int, int, int);
1078 static void PlayLevelSoundAction(int, int, int);
1079 static void PlayLevelSoundElementAction(int, int, int, int);
1080 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1081 static void PlayLevelSoundActionIfLoop(int, int, int);
1082 static void StopLevelSoundActionIfLoop(int, int, int);
1083 static void PlayLevelMusic();
1085 static void HandleGameButtons(struct GadgetInfo *);
1087 int AmoebeNachbarNr(int, int);
1088 void AmoebeUmwandeln(int, int);
1089 void ContinueMoving(int, int);
1090 void Bang(int, int);
1091 void InitMovDir(int, int);
1092 void InitAmoebaNr(int, int);
1093 int NewHiScore(void);
1095 void TestIfGoodThingHitsBadThing(int, int, int);
1096 void TestIfBadThingHitsGoodThing(int, int, int);
1097 void TestIfPlayerTouchesBadThing(int, int);
1098 void TestIfPlayerRunsIntoBadThing(int, int, int);
1099 void TestIfBadThingTouchesPlayer(int, int);
1100 void TestIfBadThingRunsIntoPlayer(int, int, int);
1101 void TestIfFriendTouchesBadThing(int, int);
1102 void TestIfBadThingTouchesFriend(int, int);
1103 void TestIfBadThingTouchesOtherBadThing(int, int);
1104 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1106 void KillPlayer(struct PlayerInfo *);
1107 void BuryPlayer(struct PlayerInfo *);
1108 void RemovePlayer(struct PlayerInfo *);
1110 static int getInvisibleActiveFromInvisibleElement(int);
1111 static int getInvisibleFromInvisibleActiveElement(int);
1113 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115 /* for detection of endless loops, caused by custom element programming */
1116 /* (using maximal playfield width x 10 is just a rough approximation) */
1117 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1119 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1121 if (recursion_loop_detected) \
1124 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1126 recursion_loop_detected = TRUE; \
1127 recursion_loop_element = (e); \
1130 recursion_loop_depth++; \
1133 #define RECURSION_LOOP_DETECTION_END() \
1135 recursion_loop_depth--; \
1138 static int recursion_loop_depth;
1139 static boolean recursion_loop_detected;
1140 static boolean recursion_loop_element;
1142 static int map_player_action[MAX_PLAYERS];
1145 /* ------------------------------------------------------------------------- */
1146 /* definition of elements that automatically change to other elements after */
1147 /* a specified time, eventually calling a function when changing */
1148 /* ------------------------------------------------------------------------- */
1150 /* forward declaration for changer functions */
1151 static void InitBuggyBase(int, int);
1152 static void WarnBuggyBase(int, int);
1154 static void InitTrap(int, int);
1155 static void ActivateTrap(int, int);
1156 static void ChangeActiveTrap(int, int);
1158 static void InitRobotWheel(int, int);
1159 static void RunRobotWheel(int, int);
1160 static void StopRobotWheel(int, int);
1162 static void InitTimegateWheel(int, int);
1163 static void RunTimegateWheel(int, int);
1165 static void InitMagicBallDelay(int, int);
1166 static void ActivateMagicBall(int, int);
1168 struct ChangingElementInfo
1173 void (*pre_change_function)(int x, int y);
1174 void (*change_function)(int x, int y);
1175 void (*post_change_function)(int x, int y);
1178 static struct ChangingElementInfo change_delay_list[] =
1213 EL_STEEL_EXIT_OPENING,
1221 EL_STEEL_EXIT_CLOSING,
1222 EL_STEEL_EXIT_CLOSED,
1249 EL_EM_STEEL_EXIT_OPENING,
1250 EL_EM_STEEL_EXIT_OPEN,
1257 EL_EM_STEEL_EXIT_CLOSING,
1261 EL_EM_STEEL_EXIT_CLOSED,
1285 EL_SWITCHGATE_OPENING,
1293 EL_SWITCHGATE_CLOSING,
1294 EL_SWITCHGATE_CLOSED,
1301 EL_TIMEGATE_OPENING,
1309 EL_TIMEGATE_CLOSING,
1318 EL_ACID_SPLASH_LEFT,
1326 EL_ACID_SPLASH_RIGHT,
1335 EL_SP_BUGGY_BASE_ACTIVATING,
1342 EL_SP_BUGGY_BASE_ACTIVATING,
1343 EL_SP_BUGGY_BASE_ACTIVE,
1350 EL_SP_BUGGY_BASE_ACTIVE,
1374 EL_ROBOT_WHEEL_ACTIVE,
1382 EL_TIMEGATE_SWITCH_ACTIVE,
1390 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1391 EL_DC_TIMEGATE_SWITCH,
1398 EL_EMC_MAGIC_BALL_ACTIVE,
1399 EL_EMC_MAGIC_BALL_ACTIVE,
1406 EL_EMC_SPRING_BUMPER_ACTIVE,
1407 EL_EMC_SPRING_BUMPER,
1414 EL_DIAGONAL_SHRINKING,
1422 EL_DIAGONAL_GROWING,
1443 int push_delay_fixed, push_delay_random;
1447 { EL_SPRING, 0, 0 },
1448 { EL_BALLOON, 0, 0 },
1450 { EL_SOKOBAN_OBJECT, 2, 0 },
1451 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1452 { EL_SATELLITE, 2, 0 },
1453 { EL_SP_DISK_YELLOW, 2, 0 },
1455 { EL_UNDEFINED, 0, 0 },
1463 move_stepsize_list[] =
1465 { EL_AMOEBA_DROP, 2 },
1466 { EL_AMOEBA_DROPPING, 2 },
1467 { EL_QUICKSAND_FILLING, 1 },
1468 { EL_QUICKSAND_EMPTYING, 1 },
1469 { EL_QUICKSAND_FAST_FILLING, 2 },
1470 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1471 { EL_MAGIC_WALL_FILLING, 2 },
1472 { EL_MAGIC_WALL_EMPTYING, 2 },
1473 { EL_BD_MAGIC_WALL_FILLING, 2 },
1474 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1475 { EL_DC_MAGIC_WALL_FILLING, 2 },
1476 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1478 { EL_UNDEFINED, 0 },
1486 collect_count_list[] =
1489 { EL_BD_DIAMOND, 1 },
1490 { EL_EMERALD_YELLOW, 1 },
1491 { EL_EMERALD_RED, 1 },
1492 { EL_EMERALD_PURPLE, 1 },
1494 { EL_SP_INFOTRON, 1 },
1498 { EL_UNDEFINED, 0 },
1506 access_direction_list[] =
1508 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1509 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1510 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1511 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1512 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1513 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1514 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1515 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1516 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1517 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1518 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1520 { EL_SP_PORT_LEFT, MV_RIGHT },
1521 { EL_SP_PORT_RIGHT, MV_LEFT },
1522 { EL_SP_PORT_UP, MV_DOWN },
1523 { EL_SP_PORT_DOWN, MV_UP },
1524 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1525 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1526 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1527 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1528 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1529 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1530 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1531 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1532 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1533 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1534 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1535 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1536 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1537 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1538 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1540 { EL_UNDEFINED, MV_NONE }
1543 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1546 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1547 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1548 IS_JUST_CHANGING(x, y))
1550 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1552 /* static variables for playfield scan mode (scanning forward or backward) */
1553 static int playfield_scan_start_x = 0;
1554 static int playfield_scan_start_y = 0;
1555 static int playfield_scan_delta_x = 1;
1556 static int playfield_scan_delta_y = 1;
1558 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1559 (y) >= 0 && (y) <= lev_fieldy - 1; \
1560 (y) += playfield_scan_delta_y) \
1561 for ((x) = playfield_scan_start_x; \
1562 (x) >= 0 && (x) <= lev_fieldx - 1; \
1563 (x) += playfield_scan_delta_x)
1566 void DEBUG_SetMaximumDynamite()
1570 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1571 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1572 local_player->inventory_element[local_player->inventory_size++] =
1577 static void InitPlayfieldScanModeVars()
1579 if (game.use_reverse_scan_direction)
1581 playfield_scan_start_x = lev_fieldx - 1;
1582 playfield_scan_start_y = lev_fieldy - 1;
1584 playfield_scan_delta_x = -1;
1585 playfield_scan_delta_y = -1;
1589 playfield_scan_start_x = 0;
1590 playfield_scan_start_y = 0;
1592 playfield_scan_delta_x = 1;
1593 playfield_scan_delta_y = 1;
1597 static void InitPlayfieldScanMode(int mode)
1599 game.use_reverse_scan_direction =
1600 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602 InitPlayfieldScanModeVars();
1605 static int get_move_delay_from_stepsize(int move_stepsize)
1608 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610 /* make sure that stepsize value is always a power of 2 */
1611 move_stepsize = (1 << log_2(move_stepsize));
1613 return TILEX / move_stepsize;
1616 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1619 int player_nr = player->index_nr;
1620 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1621 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623 /* do no immediately change move delay -- the player might just be moving */
1624 player->move_delay_value_next = move_delay;
1626 /* information if player can move must be set separately */
1627 player->cannot_move = cannot_move;
1631 player->move_delay = game.initial_move_delay[player_nr];
1632 player->move_delay_value = game.initial_move_delay_value[player_nr];
1634 player->move_delay_value_next = -1;
1636 player->move_delay_reset_counter = 0;
1640 void GetPlayerConfig()
1642 GameFrameDelay = setup.game_frame_delay;
1644 if (!audio.sound_available)
1645 setup.sound_simple = FALSE;
1647 if (!audio.loops_available)
1648 setup.sound_loops = FALSE;
1650 if (!audio.music_available)
1651 setup.sound_music = FALSE;
1653 if (!video.fullscreen_available)
1654 setup.fullscreen = FALSE;
1656 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658 SetAudioMode(setup.sound);
1662 int GetElementFromGroupElement(int element)
1664 if (IS_GROUP_ELEMENT(element))
1666 struct ElementGroupInfo *group = element_info[element].group;
1667 int last_anim_random_frame = gfx.anim_random_frame;
1670 if (group->choice_mode == ANIM_RANDOM)
1671 gfx.anim_random_frame = RND(group->num_elements_resolved);
1673 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674 group->choice_mode, 0,
1677 if (group->choice_mode == ANIM_RANDOM)
1678 gfx.anim_random_frame = last_anim_random_frame;
1680 group->choice_pos++;
1682 element = group->element_resolved[element_pos];
1688 static void InitPlayerField(int x, int y, int element, boolean init_game)
1690 if (element == EL_SP_MURPHY)
1694 if (stored_player[0].present)
1696 Feld[x][y] = EL_SP_MURPHY_CLONE;
1702 stored_player[0].initial_element = element;
1703 stored_player[0].use_murphy = TRUE;
1705 if (!level.use_artwork_element[0])
1706 stored_player[0].artwork_element = EL_SP_MURPHY;
1709 Feld[x][y] = EL_PLAYER_1;
1715 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1716 int jx = player->jx, jy = player->jy;
1718 player->present = TRUE;
1720 player->block_last_field = (element == EL_SP_MURPHY ?
1721 level.sp_block_last_field :
1722 level.block_last_field);
1724 /* ---------- initialize player's last field block delay --------------- */
1726 /* always start with reliable default value (no adjustment needed) */
1727 player->block_delay_adjustment = 0;
1729 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1730 if (player->block_last_field && element == EL_SP_MURPHY)
1731 player->block_delay_adjustment = 1;
1733 /* special case 2: in game engines before 3.1.1, blocking was different */
1734 if (game.use_block_last_field_bug)
1735 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1737 if (!options.network || player->connected)
1739 player->active = TRUE;
1741 /* remove potentially duplicate players */
1742 if (StorePlayer[jx][jy] == Feld[x][y])
1743 StorePlayer[jx][jy] = 0;
1745 StorePlayer[x][y] = Feld[x][y];
1747 #if DEBUG_INIT_PLAYER
1750 printf("- player element %d activated", player->element_nr);
1751 printf(" (local player is %d and currently %s)\n",
1752 local_player->element_nr,
1753 local_player->active ? "active" : "not active");
1758 Feld[x][y] = EL_EMPTY;
1760 player->jx = player->last_jx = x;
1761 player->jy = player->last_jy = y;
1764 #if USE_PLAYER_REANIMATION
1767 int player_nr = GET_PLAYER_NR(element);
1768 struct PlayerInfo *player = &stored_player[player_nr];
1770 if (player->active && player->killed)
1771 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1776 static void InitField(int x, int y, boolean init_game)
1778 int element = Feld[x][y];
1787 InitPlayerField(x, y, element, init_game);
1790 case EL_SOKOBAN_FIELD_PLAYER:
1791 element = Feld[x][y] = EL_PLAYER_1;
1792 InitField(x, y, init_game);
1794 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1795 InitField(x, y, init_game);
1798 case EL_SOKOBAN_FIELD_EMPTY:
1799 local_player->sokobanfields_still_needed++;
1803 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1804 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1805 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1806 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1807 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1808 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1809 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1810 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1811 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1812 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1821 case EL_SPACESHIP_RIGHT:
1822 case EL_SPACESHIP_UP:
1823 case EL_SPACESHIP_LEFT:
1824 case EL_SPACESHIP_DOWN:
1825 case EL_BD_BUTTERFLY:
1826 case EL_BD_BUTTERFLY_RIGHT:
1827 case EL_BD_BUTTERFLY_UP:
1828 case EL_BD_BUTTERFLY_LEFT:
1829 case EL_BD_BUTTERFLY_DOWN:
1831 case EL_BD_FIREFLY_RIGHT:
1832 case EL_BD_FIREFLY_UP:
1833 case EL_BD_FIREFLY_LEFT:
1834 case EL_BD_FIREFLY_DOWN:
1835 case EL_PACMAN_RIGHT:
1837 case EL_PACMAN_LEFT:
1838 case EL_PACMAN_DOWN:
1840 case EL_YAMYAM_LEFT:
1841 case EL_YAMYAM_RIGHT:
1843 case EL_YAMYAM_DOWN:
1844 case EL_DARK_YAMYAM:
1847 case EL_SP_SNIKSNAK:
1848 case EL_SP_ELECTRON:
1857 case EL_AMOEBA_FULL:
1862 case EL_AMOEBA_DROP:
1863 if (y == lev_fieldy - 1)
1865 Feld[x][y] = EL_AMOEBA_GROWING;
1866 Store[x][y] = EL_AMOEBA_WET;
1870 case EL_DYNAMITE_ACTIVE:
1871 case EL_SP_DISK_RED_ACTIVE:
1872 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1873 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1874 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1875 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1876 MovDelay[x][y] = 96;
1879 case EL_EM_DYNAMITE_ACTIVE:
1880 MovDelay[x][y] = 32;
1884 local_player->lights_still_needed++;
1888 local_player->friends_still_needed++;
1893 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1896 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1897 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1898 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1899 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1900 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1901 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1902 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1903 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1904 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1905 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1906 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1907 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1910 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1911 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1912 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1914 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1916 game.belt_dir[belt_nr] = belt_dir;
1917 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1919 else /* more than one switch -- set it like the first switch */
1921 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1926 #if !USE_BOTH_SWITCHGATE_SWITCHES
1927 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1929 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1932 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1934 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1938 case EL_LIGHT_SWITCH_ACTIVE:
1940 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1943 case EL_INVISIBLE_STEELWALL:
1944 case EL_INVISIBLE_WALL:
1945 case EL_INVISIBLE_SAND:
1946 if (game.light_time_left > 0 ||
1947 game.lenses_time_left > 0)
1948 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1951 case EL_EMC_MAGIC_BALL:
1952 if (game.ball_state)
1953 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1956 case EL_EMC_MAGIC_BALL_SWITCH:
1957 if (game.ball_state)
1958 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1961 case EL_TRIGGER_PLAYER:
1962 case EL_TRIGGER_ELEMENT:
1963 case EL_TRIGGER_CE_VALUE:
1964 case EL_TRIGGER_CE_SCORE:
1966 case EL_ANY_ELEMENT:
1967 case EL_CURRENT_CE_VALUE:
1968 case EL_CURRENT_CE_SCORE:
1985 /* reference elements should not be used on the playfield */
1986 Feld[x][y] = EL_EMPTY;
1990 if (IS_CUSTOM_ELEMENT(element))
1992 if (CAN_MOVE(element))
1995 #if USE_NEW_CUSTOM_VALUE
1996 if (!element_info[element].use_last_ce_value || init_game)
1997 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2000 else if (IS_GROUP_ELEMENT(element))
2002 Feld[x][y] = GetElementFromGroupElement(element);
2004 InitField(x, y, init_game);
2011 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2014 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2016 InitField(x, y, init_game);
2018 /* not needed to call InitMovDir() -- already done by InitField()! */
2019 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2020 CAN_MOVE(Feld[x][y]))
2024 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2026 int old_element = Feld[x][y];
2028 InitField(x, y, init_game);
2030 /* not needed to call InitMovDir() -- already done by InitField()! */
2031 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032 CAN_MOVE(old_element) &&
2033 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2036 /* this case is in fact a combination of not less than three bugs:
2037 first, it calls InitMovDir() for elements that can move, although this is
2038 already done by InitField(); then, it checks the element that was at this
2039 field _before_ the call to InitField() (which can change it); lastly, it
2040 was not called for "mole with direction" elements, which were treated as
2041 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2045 static int get_key_element_from_nr(int key_nr)
2047 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2048 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2049 EL_EM_KEY_1 : EL_KEY_1);
2051 return key_base_element + key_nr;
2054 static int get_next_dropped_element(struct PlayerInfo *player)
2056 return (player->inventory_size > 0 ?
2057 player->inventory_element[player->inventory_size - 1] :
2058 player->inventory_infinite_element != EL_UNDEFINED ?
2059 player->inventory_infinite_element :
2060 player->dynabombs_left > 0 ?
2061 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2065 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2067 /* pos >= 0: get element from bottom of the stack;
2068 pos < 0: get element from top of the stack */
2072 int min_inventory_size = -pos;
2073 int inventory_pos = player->inventory_size - min_inventory_size;
2074 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2076 return (player->inventory_size >= min_inventory_size ?
2077 player->inventory_element[inventory_pos] :
2078 player->inventory_infinite_element != EL_UNDEFINED ?
2079 player->inventory_infinite_element :
2080 player->dynabombs_left >= min_dynabombs_left ?
2081 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2086 int min_dynabombs_left = pos + 1;
2087 int min_inventory_size = pos + 1 - player->dynabombs_left;
2088 int inventory_pos = pos - player->dynabombs_left;
2090 return (player->inventory_infinite_element != EL_UNDEFINED ?
2091 player->inventory_infinite_element :
2092 player->dynabombs_left >= min_dynabombs_left ?
2093 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094 player->inventory_size >= min_inventory_size ?
2095 player->inventory_element[inventory_pos] :
2100 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2102 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2103 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2106 if (gpo1->sort_priority != gpo2->sort_priority)
2107 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2109 compare_result = gpo1->nr - gpo2->nr;
2111 return compare_result;
2114 void InitGameControlValues()
2118 for (i = 0; game_panel_controls[i].nr != -1; i++)
2120 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2121 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2122 struct TextPosInfo *pos = gpc->pos;
2124 int type = gpc->type;
2128 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2129 Error(ERR_EXIT, "this should not happen -- please debug");
2132 /* force update of game controls after initialization */
2133 gpc->value = gpc->last_value = -1;
2134 gpc->frame = gpc->last_frame = -1;
2135 gpc->gfx_frame = -1;
2137 /* determine panel value width for later calculation of alignment */
2138 if (type == TYPE_INTEGER || type == TYPE_STRING)
2140 pos->width = pos->size * getFontWidth(pos->font);
2141 pos->height = getFontHeight(pos->font);
2143 else if (type == TYPE_ELEMENT)
2145 pos->width = pos->size;
2146 pos->height = pos->size;
2149 /* fill structure for game panel draw order */
2151 gpo->sort_priority = pos->sort_priority;
2154 /* sort game panel controls according to sort_priority and control number */
2155 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2156 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2159 void UpdatePlayfieldElementCount()
2161 boolean use_element_count = FALSE;
2164 /* first check if it is needed at all to calculate playfield element count */
2165 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2166 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2167 use_element_count = TRUE;
2169 if (!use_element_count)
2172 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2173 element_info[i].element_count = 0;
2175 SCAN_PLAYFIELD(x, y)
2177 element_info[Feld[x][y]].element_count++;
2180 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2181 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2182 if (IS_IN_GROUP(j, i))
2183 element_info[EL_GROUP_START + i].element_count +=
2184 element_info[j].element_count;
2187 void UpdateGameControlValues()
2190 int time = (local_player->LevelSolved ?
2191 local_player->LevelSolved_CountingTime :
2192 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193 level.native_em_level->lev->time :
2194 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195 level.native_sp_level->game_sp->time_played :
2196 game.no_time_limit ? TimePlayed : TimeLeft);
2197 int score = (local_player->LevelSolved ?
2198 local_player->LevelSolved_CountingScore :
2199 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200 level.native_em_level->lev->score :
2201 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202 level.native_sp_level->game_sp->score :
2203 local_player->score);
2204 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2205 level.native_em_level->lev->required :
2206 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2207 level.native_sp_level->game_sp->infotrons_still_needed :
2208 local_player->gems_still_needed);
2209 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2210 level.native_em_level->lev->required > 0 :
2211 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2212 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2213 local_player->gems_still_needed > 0 ||
2214 local_player->sokobanfields_still_needed > 0 ||
2215 local_player->lights_still_needed > 0);
2217 UpdatePlayfieldElementCount();
2219 /* update game panel control values */
2221 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2222 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2224 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2225 for (i = 0; i < MAX_NUM_KEYS; i++)
2226 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2227 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2228 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2230 if (game.centered_player_nr == -1)
2232 for (i = 0; i < MAX_PLAYERS; i++)
2234 /* only one player in Supaplex game engine */
2235 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2238 for (k = 0; k < MAX_NUM_KEYS; k++)
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2242 if (level.native_em_level->ply[i]->keys & (1 << k))
2243 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2244 get_key_element_from_nr(k);
2246 else if (stored_player[i].key[k])
2247 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2248 get_key_element_from_nr(k);
2251 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2252 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253 level.native_em_level->ply[i]->dynamite;
2254 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2255 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2256 level.native_sp_level->game_sp->red_disk_count;
2258 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259 stored_player[i].inventory_size;
2261 if (stored_player[i].num_white_keys > 0)
2262 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2265 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266 stored_player[i].num_white_keys;
2271 int player_nr = game.centered_player_nr;
2273 for (k = 0; k < MAX_NUM_KEYS; k++)
2275 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279 get_key_element_from_nr(k);
2281 else if (stored_player[player_nr].key[k])
2282 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283 get_key_element_from_nr(k);
2286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288 level.native_em_level->ply[player_nr]->dynamite;
2289 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2290 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2291 level.native_sp_level->game_sp->red_disk_count;
2293 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2294 stored_player[player_nr].inventory_size;
2296 if (stored_player[player_nr].num_white_keys > 0)
2297 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2299 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2300 stored_player[player_nr].num_white_keys;
2303 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2305 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2306 get_inventory_element_from_pos(local_player, i);
2307 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2308 get_inventory_element_from_pos(local_player, -i - 1);
2311 game_panel_controls[GAME_PANEL_SCORE].value = score;
2312 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2314 game_panel_controls[GAME_PANEL_TIME].value = time;
2316 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2317 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2318 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2320 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2323 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2326 local_player->shield_normal_time_left;
2327 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2328 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2331 local_player->shield_deadly_time_left;
2333 game_panel_controls[GAME_PANEL_EXIT].value =
2334 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2337 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2338 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2339 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2340 EL_EMC_MAGIC_BALL_SWITCH);
2342 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2343 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2344 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2345 game.light_time_left;
2347 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2348 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2349 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2350 game.timegate_time_left;
2352 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2353 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2356 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2357 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2358 game.lenses_time_left;
2360 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2361 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2362 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2363 game.magnify_time_left;
2365 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2366 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2367 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2368 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2369 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2370 EL_BALLOON_SWITCH_NONE);
2372 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2373 local_player->dynabomb_count;
2374 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2375 local_player->dynabomb_size;
2376 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2377 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379 game_panel_controls[GAME_PANEL_PENGUINS].value =
2380 local_player->friends_still_needed;
2382 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2383 local_player->sokobanfields_still_needed;
2384 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2385 local_player->sokobanfields_still_needed;
2387 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2388 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390 for (i = 0; i < NUM_BELTS; i++)
2392 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2393 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2394 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2395 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2396 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2399 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2400 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2401 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2402 game.magic_wall_time_left;
2404 #if USE_PLAYER_GRAVITY
2405 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406 local_player->gravity;
2408 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2411 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2412 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2414 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2416 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2417 game.panel.element[i].id : EL_UNDEFINED);
2419 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2420 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2421 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2422 element_info[game.panel.element_count[i].id].element_count : 0);
2424 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2426 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2427 element_info[game.panel.ce_score[i].id].collect_score : 0);
2429 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2430 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2431 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2432 element_info[game.panel.ce_score_element[i].id].collect_score :
2435 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2436 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2437 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2439 /* update game panel control frames */
2441 for (i = 0; game_panel_controls[i].nr != -1; i++)
2443 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2445 if (gpc->type == TYPE_ELEMENT)
2447 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2449 int last_anim_random_frame = gfx.anim_random_frame;
2450 int element = gpc->value;
2451 int graphic = el2panelimg(element);
2453 if (gpc->value != gpc->last_value)
2456 gpc->gfx_random = INIT_GFX_RANDOM();
2462 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2463 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2464 gpc->gfx_random = INIT_GFX_RANDOM();
2467 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2468 gfx.anim_random_frame = gpc->gfx_random;
2470 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2471 gpc->gfx_frame = element_info[element].collect_score;
2473 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2476 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2477 gfx.anim_random_frame = last_anim_random_frame;
2483 void DisplayGameControlValues()
2485 boolean redraw_panel = FALSE;
2488 for (i = 0; game_panel_controls[i].nr != -1; i++)
2490 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2492 if (PANEL_DEACTIVATED(gpc->pos))
2495 if (gpc->value == gpc->last_value &&
2496 gpc->frame == gpc->last_frame)
2499 redraw_panel = TRUE;
2505 /* copy default game door content to main double buffer */
2507 /* !!! CHECK AGAIN !!! */
2508 SetPanelBackground();
2509 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2510 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2512 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2513 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2516 /* redraw game control buttons */
2518 RedrawGameButtons();
2524 game_status = GAME_MODE_PSEUDO_PANEL;
2527 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2529 for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 int nr = game_panel_order[i].nr;
2534 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2536 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2539 struct TextPosInfo *pos = gpc->pos;
2540 int type = gpc->type;
2541 int value = gpc->value;
2542 int frame = gpc->frame;
2544 int last_value = gpc->last_value;
2545 int last_frame = gpc->last_frame;
2547 int size = pos->size;
2548 int font = pos->font;
2549 boolean draw_masked = pos->draw_masked;
2550 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2552 if (PANEL_DEACTIVATED(pos))
2556 if (value == last_value && frame == last_frame)
2560 gpc->last_value = value;
2561 gpc->last_frame = frame;
2564 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2567 if (type == TYPE_INTEGER)
2569 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2570 nr == GAME_PANEL_TIME)
2572 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2574 if (use_dynamic_size) /* use dynamic number of digits */
2576 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2577 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2578 int size2 = size1 + 1;
2579 int font1 = pos->font;
2580 int font2 = pos->font_alt;
2582 size = (value < value_change ? size1 : size2);
2583 font = (value < value_change ? font1 : font2);
2586 /* clear background if value just changed its size (dynamic digits) */
2587 if ((last_value < value_change) != (value < value_change))
2589 int width1 = size1 * getFontWidth(font1);
2590 int width2 = size2 * getFontWidth(font2);
2591 int max_width = MAX(width1, width2);
2592 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2594 pos->width = max_width;
2596 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2597 max_width, max_height);
2604 /* correct text size if "digits" is zero or less */
2606 size = strlen(int2str(value, size));
2608 /* dynamically correct text alignment */
2609 pos->width = size * getFontWidth(font);
2612 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2613 int2str(value, size), font, mask_mode);
2615 else if (type == TYPE_ELEMENT)
2617 int element, graphic;
2621 int dst_x = PANEL_XPOS(pos);
2622 int dst_y = PANEL_YPOS(pos);
2625 if (value != EL_UNDEFINED && value != EL_EMPTY)
2628 graphic = el2panelimg(value);
2630 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2633 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2640 width = graphic_info[graphic].width * size / TILESIZE;
2641 height = graphic_info[graphic].height * size / TILESIZE;
2645 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2646 dst_x - src_x, dst_y - src_y);
2647 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2652 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2657 if (value == EL_UNDEFINED || value == EL_EMPTY)
2659 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2660 graphic = el2panelimg(element);
2662 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2663 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2664 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2669 graphic = el2panelimg(value);
2671 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2674 width = graphic_info[graphic].width * size / TILESIZE;
2675 height = graphic_info[graphic].height * size / TILESIZE;
2677 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2680 else if (type == TYPE_STRING)
2682 boolean active = (value != 0);
2683 char *state_normal = "off";
2684 char *state_active = "on";
2685 char *state = (active ? state_active : state_normal);
2686 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2687 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2688 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2689 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2691 if (nr == GAME_PANEL_GRAVITY_STATE)
2693 int font1 = pos->font; /* (used for normal state) */
2694 int font2 = pos->font_alt; /* (used for active state) */
2696 int size1 = strlen(state_normal);
2697 int size2 = strlen(state_active);
2698 int width1 = size1 * getFontWidth(font1);
2699 int width2 = size2 * getFontWidth(font2);
2700 int max_width = MAX(width1, width2);
2701 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2703 pos->width = max_width;
2705 /* clear background for values that may have changed its size */
2706 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2707 max_width, max_height);
2710 font = (active ? font2 : font1);
2720 /* don't truncate output if "chars" is zero or less */
2723 /* dynamically correct text alignment */
2724 pos->width = size * getFontWidth(font);
2728 s_cut = getStringCopyN(s, size);
2730 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731 s_cut, font, mask_mode);
2737 redraw_mask |= REDRAW_DOOR_1;
2740 game_status = GAME_MODE_PLAYING;
2743 void UpdateAndDisplayGameControlValues()
2745 if (tape.warp_forward)
2748 UpdateGameControlValues();
2749 DisplayGameControlValues();
2752 void DrawGameValue_Emeralds(int value)
2754 struct TextPosInfo *pos = &game.panel.gems;
2755 int font_nr = pos->font;
2756 int font_width = getFontWidth(font_nr);
2757 int chars = pos->size;
2760 return; /* !!! USE NEW STUFF !!! */
2763 if (PANEL_DEACTIVATED(pos))
2766 pos->width = chars * font_width;
2768 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2771 void DrawGameValue_Dynamite(int value)
2773 struct TextPosInfo *pos = &game.panel.inventory_count;
2774 int font_nr = pos->font;
2775 int font_width = getFontWidth(font_nr);
2776 int chars = pos->size;
2779 return; /* !!! USE NEW STUFF !!! */
2782 if (PANEL_DEACTIVATED(pos))
2785 pos->width = chars * font_width;
2787 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2790 void DrawGameValue_Score(int value)
2792 struct TextPosInfo *pos = &game.panel.score;
2793 int font_nr = pos->font;
2794 int font_width = getFontWidth(font_nr);
2795 int chars = pos->size;
2798 return; /* !!! USE NEW STUFF !!! */
2801 if (PANEL_DEACTIVATED(pos))
2804 pos->width = chars * font_width;
2806 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 void DrawGameValue_Time(int value)
2811 struct TextPosInfo *pos = &game.panel.time;
2812 static int last_value = -1;
2815 int chars = pos->size;
2816 int font1_nr = pos->font;
2817 int font2_nr = pos->font_alt;
2818 int font_nr = font1_nr;
2819 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2822 return; /* !!! USE NEW STUFF !!! */
2825 if (PANEL_DEACTIVATED(pos))
2828 if (use_dynamic_chars) /* use dynamic number of chars */
2830 chars = (value < 1000 ? chars1 : chars2);
2831 font_nr = (value < 1000 ? font1_nr : font2_nr);
2834 /* clear background if value just changed its size (dynamic chars only) */
2835 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2837 int width1 = chars1 * getFontWidth(font1_nr);
2838 int width2 = chars2 * getFontWidth(font2_nr);
2839 int max_width = MAX(width1, width2);
2840 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2842 pos->width = max_width;
2844 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2845 max_width, max_height);
2848 pos->width = chars * getFontWidth(font_nr);
2850 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2855 void DrawGameValue_Level(int value)
2857 struct TextPosInfo *pos = &game.panel.level_number;
2860 int chars = pos->size;
2861 int font1_nr = pos->font;
2862 int font2_nr = pos->font_alt;
2863 int font_nr = font1_nr;
2864 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2867 return; /* !!! USE NEW STUFF !!! */
2870 if (PANEL_DEACTIVATED(pos))
2873 if (use_dynamic_chars) /* use dynamic number of chars */
2875 chars = (level_nr < 100 ? chars1 : chars2);
2876 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2879 pos->width = chars * getFontWidth(font_nr);
2881 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2884 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2889 return; /* !!! USE NEW STUFF !!! */
2892 for (i = 0; i < MAX_NUM_KEYS; i++)
2894 struct TextPosInfo *pos = &game.panel.key[i];
2895 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2896 int src_y = DOOR_GFX_PAGEY1 + 123;
2897 int dst_x = PANEL_XPOS(pos);
2898 int dst_y = PANEL_YPOS(pos);
2900 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2901 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2903 int graphic = el2edimg(element);
2905 if (PANEL_DEACTIVATED(pos))
2909 /* masked blit with tiles from half-size scaled bitmap does not work yet
2910 (no mask bitmap created for these sizes after loading and scaling) --
2911 solution: load without creating mask, scale, then create final mask */
2913 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2914 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2921 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2923 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2924 dst_x - src_x, dst_y - src_y);
2925 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2930 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2932 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2933 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2938 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2941 int key[MAX_NUM_KEYS];
2944 /* prevent EM engine from updating time/score values parallel to GameWon() */
2945 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2946 local_player->LevelSolved)
2949 for (i = 0; i < MAX_NUM_KEYS; i++)
2950 key[i] = key_bits & (1 << i);
2952 DrawGameValue_Level(level_nr);
2954 DrawGameValue_Emeralds(emeralds);
2955 DrawGameValue_Dynamite(dynamite);
2956 DrawGameValue_Score(score);
2957 DrawGameValue_Time(time);
2959 DrawGameValue_Keys(key);
2962 void UpdateGameDoorValues()
2964 UpdateGameControlValues();
2967 void DrawGameDoorValues()
2969 DisplayGameControlValues();
2972 void DrawGameDoorValues_OLD()
2974 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2975 int dynamite_value = 0;
2976 int score_value = (local_player->LevelSolved ? local_player->score_final :
2977 local_player->score);
2978 int gems_value = local_player->gems_still_needed;
2982 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2984 DrawGameDoorValues_EM();
2989 if (game.centered_player_nr == -1)
2991 for (i = 0; i < MAX_PLAYERS; i++)
2993 for (j = 0; j < MAX_NUM_KEYS; j++)
2994 if (stored_player[i].key[j])
2995 key_bits |= (1 << j);
2997 dynamite_value += stored_player[i].inventory_size;
3002 int player_nr = game.centered_player_nr;
3004 for (i = 0; i < MAX_NUM_KEYS; i++)
3005 if (stored_player[player_nr].key[i])
3006 key_bits |= (1 << i);
3008 dynamite_value = stored_player[player_nr].inventory_size;
3011 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3017 =============================================================================
3019 -----------------------------------------------------------------------------
3020 initialize game engine due to level / tape version number
3021 =============================================================================
3024 static void InitGameEngine()
3026 int i, j, k, l, x, y;
3028 /* set game engine from tape file when re-playing, else from level file */
3029 game.engine_version = (tape.playing ? tape.engine_version :
3030 level.game_version);
3032 /* set single or multi-player game mode (needed for re-playing tapes) */
3033 game.team_mode = setup.team_mode;
3037 int num_players = 0;
3039 for (i = 0; i < MAX_PLAYERS; i++)
3040 if (tape.player_participates[i])
3043 /* multi-player tapes contain input data for more than one player */
3044 game.team_mode = (num_players > 1);
3047 /* ---------------------------------------------------------------------- */
3048 /* set flags for bugs and changes according to active game engine version */
3049 /* ---------------------------------------------------------------------- */
3052 Summary of bugfix/change:
3053 Fixed handling for custom elements that change when pushed by the player.
3055 Fixed/changed in version:
3059 Before 3.1.0, custom elements that "change when pushing" changed directly
3060 after the player started pushing them (until then handled in "DigField()").
3061 Since 3.1.0, these custom elements are not changed until the "pushing"
3062 move of the element is finished (now handled in "ContinueMoving()").
3064 Affected levels/tapes:
3065 The first condition is generally needed for all levels/tapes before version
3066 3.1.0, which might use the old behaviour before it was changed; known tapes
3067 that are affected are some tapes from the level set "Walpurgis Gardens" by
3069 The second condition is an exception from the above case and is needed for
3070 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3071 above (including some development versions of 3.1.0), but before it was
3072 known that this change would break tapes like the above and was fixed in
3073 3.1.1, so that the changed behaviour was active although the engine version
3074 while recording maybe was before 3.1.0. There is at least one tape that is
3075 affected by this exception, which is the tape for the one-level set "Bug
3076 Machine" by Juergen Bonhagen.
3079 game.use_change_when_pushing_bug =
3080 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3082 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3083 tape.game_version < VERSION_IDENT(3,1,1,0)));
3086 Summary of bugfix/change:
3087 Fixed handling for blocking the field the player leaves when moving.
3089 Fixed/changed in version:
3093 Before 3.1.1, when "block last field when moving" was enabled, the field
3094 the player is leaving when moving was blocked for the time of the move,
3095 and was directly unblocked afterwards. This resulted in the last field
3096 being blocked for exactly one less than the number of frames of one player
3097 move. Additionally, even when blocking was disabled, the last field was
3098 blocked for exactly one frame.
3099 Since 3.1.1, due to changes in player movement handling, the last field
3100 is not blocked at all when blocking is disabled. When blocking is enabled,
3101 the last field is blocked for exactly the number of frames of one player
3102 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3103 last field is blocked for exactly one more than the number of frames of
3106 Affected levels/tapes:
3107 (!!! yet to be determined -- probably many !!!)
3110 game.use_block_last_field_bug =
3111 (game.engine_version < VERSION_IDENT(3,1,1,0));
3114 Summary of bugfix/change:
3115 Changed behaviour of CE changes with multiple changes per single frame.
3117 Fixed/changed in version:
3121 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3122 This resulted in race conditions where CEs seem to behave strange in some
3123 situations (where triggered CE changes were just skipped because there was
3124 already a CE change on that tile in the playfield in that engine frame).
3125 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3126 (The number of changes per frame must be limited in any case, because else
3127 it is easily possible to define CE changes that would result in an infinite
3128 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3129 should be set large enough so that it would only be reached in cases where
3130 the corresponding CE change conditions run into a loop. Therefore, it seems
3131 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3132 maximal number of change pages for custom elements.)
3134 Affected levels/tapes:
3138 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3139 game.max_num_changes_per_frame = 1;
3141 game.max_num_changes_per_frame =
3142 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3145 /* ---------------------------------------------------------------------- */
3147 /* default scan direction: scan playfield from top/left to bottom/right */
3148 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3150 /* dynamically adjust element properties according to game engine version */
3151 InitElementPropertiesEngine(game.engine_version);
3154 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3155 printf(" tape version == %06d [%s] [file: %06d]\n",
3156 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3158 printf(" => game.engine_version == %06d\n", game.engine_version);
3161 /* ---------- initialize player's initial move delay --------------------- */
3163 /* dynamically adjust player properties according to level information */
3164 for (i = 0; i < MAX_PLAYERS; i++)
3165 game.initial_move_delay_value[i] =
3166 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3168 /* dynamically adjust player properties according to game engine version */
3169 for (i = 0; i < MAX_PLAYERS; i++)
3170 game.initial_move_delay[i] =
3171 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3172 game.initial_move_delay_value[i] : 0);
3174 /* ---------- initialize player's initial push delay --------------------- */
3176 /* dynamically adjust player properties according to game engine version */
3177 game.initial_push_delay_value =
3178 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3180 /* ---------- initialize changing elements ------------------------------- */
3182 /* initialize changing elements information */
3183 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3185 struct ElementInfo *ei = &element_info[i];
3187 /* this pointer might have been changed in the level editor */
3188 ei->change = &ei->change_page[0];
3190 if (!IS_CUSTOM_ELEMENT(i))
3192 ei->change->target_element = EL_EMPTY_SPACE;
3193 ei->change->delay_fixed = 0;
3194 ei->change->delay_random = 0;
3195 ei->change->delay_frames = 1;
3198 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3200 ei->has_change_event[j] = FALSE;
3202 ei->event_page_nr[j] = 0;
3203 ei->event_page[j] = &ei->change_page[0];
3207 /* add changing elements from pre-defined list */
3208 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3210 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3211 struct ElementInfo *ei = &element_info[ch_delay->element];
3213 ei->change->target_element = ch_delay->target_element;
3214 ei->change->delay_fixed = ch_delay->change_delay;
3216 ei->change->pre_change_function = ch_delay->pre_change_function;
3217 ei->change->change_function = ch_delay->change_function;
3218 ei->change->post_change_function = ch_delay->post_change_function;
3220 ei->change->can_change = TRUE;
3221 ei->change->can_change_or_has_action = TRUE;
3223 ei->has_change_event[CE_DELAY] = TRUE;
3225 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3226 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3229 /* ---------- initialize internal run-time variables --------------------- */
3231 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3233 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3235 for (j = 0; j < ei->num_change_pages; j++)
3237 ei->change_page[j].can_change_or_has_action =
3238 (ei->change_page[j].can_change |
3239 ei->change_page[j].has_action);
3243 /* add change events from custom element configuration */
3244 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3248 for (j = 0; j < ei->num_change_pages; j++)
3250 if (!ei->change_page[j].can_change_or_has_action)
3253 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3255 /* only add event page for the first page found with this event */
3256 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3258 ei->has_change_event[k] = TRUE;
3260 ei->event_page_nr[k] = j;
3261 ei->event_page[k] = &ei->change_page[j];
3268 /* ---------- initialize reference elements in change conditions --------- */
3270 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3272 int element = EL_CUSTOM_START + i;
3273 struct ElementInfo *ei = &element_info[element];
3275 for (j = 0; j < ei->num_change_pages; j++)
3277 int trigger_element = ei->change_page[j].initial_trigger_element;
3279 if (trigger_element >= EL_PREV_CE_8 &&
3280 trigger_element <= EL_NEXT_CE_8)
3281 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3283 ei->change_page[j].trigger_element = trigger_element;
3288 /* ---------- initialize run-time trigger player and element ------------- */
3290 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3292 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3294 for (j = 0; j < ei->num_change_pages; j++)
3296 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3297 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3298 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3299 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3300 ei->change_page[j].actual_trigger_ce_value = 0;
3301 ei->change_page[j].actual_trigger_ce_score = 0;
3305 /* ---------- initialize trigger events ---------------------------------- */
3307 /* initialize trigger events information */
3308 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3309 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3310 trigger_events[i][j] = FALSE;
3312 /* add trigger events from element change event properties */
3313 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3315 struct ElementInfo *ei = &element_info[i];
3317 for (j = 0; j < ei->num_change_pages; j++)
3319 if (!ei->change_page[j].can_change_or_has_action)
3322 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3324 int trigger_element = ei->change_page[j].trigger_element;
3326 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3328 if (ei->change_page[j].has_event[k])
3330 if (IS_GROUP_ELEMENT(trigger_element))
3332 struct ElementGroupInfo *group =
3333 element_info[trigger_element].group;
3335 for (l = 0; l < group->num_elements_resolved; l++)
3336 trigger_events[group->element_resolved[l]][k] = TRUE;
3338 else if (trigger_element == EL_ANY_ELEMENT)
3339 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3340 trigger_events[l][k] = TRUE;
3342 trigger_events[trigger_element][k] = TRUE;
3349 /* ---------- initialize push delay -------------------------------------- */
3351 /* initialize push delay values to default */
3352 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354 if (!IS_CUSTOM_ELEMENT(i))
3356 /* set default push delay values (corrected since version 3.0.7-1) */
3357 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3359 element_info[i].push_delay_fixed = 2;
3360 element_info[i].push_delay_random = 8;
3364 element_info[i].push_delay_fixed = 8;
3365 element_info[i].push_delay_random = 8;
3370 /* set push delay value for certain elements from pre-defined list */
3371 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3373 int e = push_delay_list[i].element;
3375 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3376 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3379 /* set push delay value for Supaplex elements for newer engine versions */
3380 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3382 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3384 if (IS_SP_ELEMENT(i))
3386 /* set SP push delay to just enough to push under a falling zonk */
3387 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3389 element_info[i].push_delay_fixed = delay;
3390 element_info[i].push_delay_random = 0;
3395 /* ---------- initialize move stepsize ----------------------------------- */
3397 /* initialize move stepsize values to default */
3398 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3399 if (!IS_CUSTOM_ELEMENT(i))
3400 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3402 /* set move stepsize value for certain elements from pre-defined list */
3403 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3405 int e = move_stepsize_list[i].element;
3407 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3410 /* ---------- initialize collect score ----------------------------------- */
3412 /* initialize collect score values for custom elements from initial value */
3413 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414 if (IS_CUSTOM_ELEMENT(i))
3415 element_info[i].collect_score = element_info[i].collect_score_initial;
3417 /* ---------- initialize collect count ----------------------------------- */
3419 /* initialize collect count values for non-custom elements */
3420 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421 if (!IS_CUSTOM_ELEMENT(i))
3422 element_info[i].collect_count_initial = 0;
3424 /* add collect count values for all elements from pre-defined list */
3425 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3426 element_info[collect_count_list[i].element].collect_count_initial =
3427 collect_count_list[i].count;
3429 /* ---------- initialize access direction -------------------------------- */
3431 /* initialize access direction values to default (access from every side) */
3432 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3433 if (!IS_CUSTOM_ELEMENT(i))
3434 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3436 /* set access direction value for certain elements from pre-defined list */
3437 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3438 element_info[access_direction_list[i].element].access_direction =
3439 access_direction_list[i].direction;
3441 /* ---------- initialize explosion content ------------------------------- */
3442 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3444 if (IS_CUSTOM_ELEMENT(i))
3447 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3449 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3451 element_info[i].content.e[x][y] =
3452 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3453 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3454 i == EL_PLAYER_3 ? EL_EMERALD :
3455 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3456 i == EL_MOLE ? EL_EMERALD_RED :
3457 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3458 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3459 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3460 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3461 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3462 i == EL_WALL_EMERALD ? EL_EMERALD :
3463 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3464 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3465 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3466 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3467 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3468 i == EL_WALL_PEARL ? EL_PEARL :
3469 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3474 /* ---------- initialize recursion detection ------------------------------ */
3475 recursion_loop_depth = 0;
3476 recursion_loop_detected = FALSE;
3477 recursion_loop_element = EL_UNDEFINED;
3479 /* ---------- initialize graphics engine ---------------------------------- */
3480 game.scroll_delay_value =
3481 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3482 setup.scroll_delay ? setup.scroll_delay_value : 0);
3483 game.scroll_delay_value =
3484 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3487 int get_num_special_action(int element, int action_first, int action_last)
3489 int num_special_action = 0;
3492 for (i = action_first; i <= action_last; i++)
3494 boolean found = FALSE;
3496 for (j = 0; j < NUM_DIRECTIONS; j++)
3497 if (el_act_dir2img(element, i, j) !=
3498 el_act_dir2img(element, ACTION_DEFAULT, j))
3502 num_special_action++;
3507 return num_special_action;
3512 =============================================================================
3514 -----------------------------------------------------------------------------
3515 initialize and start new game
3516 =============================================================================
3521 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3522 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3524 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3525 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3526 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3528 boolean do_fading = (game_status == GAME_MODE_MAIN);
3531 int initial_move_dir = MV_DOWN;
3533 int initial_move_dir = MV_NONE;
3538 game_status = GAME_MODE_PLAYING;
3545 if (!game.restart_level)
3546 CloseDoor(DOOR_CLOSE_1);
3549 if (level_editor_test_game)
3550 FadeSkipNextFadeIn();
3552 FadeSetEnterScreen();
3554 if (level_editor_test_game)
3555 fading = fading_none;
3557 fading = menu.destination;
3561 FadeOut(REDRAW_FIELD);
3564 FadeOut(REDRAW_FIELD);
3570 printf("::: FADING OUT: DONE\n");
3575 game_status = GAME_MODE_PLAYING;
3579 /* needed if different viewport properties defined for playing */
3580 ChangeViewportPropertiesIfNeeded();
3584 DrawCompleteVideoDisplay();
3588 InitGameControlValues();
3590 /* don't play tapes over network */
3591 network_playing = (options.network && !tape.playing);
3593 for (i = 0; i < MAX_PLAYERS; i++)
3595 struct PlayerInfo *player = &stored_player[i];
3597 player->index_nr = i;
3598 player->index_bit = (1 << i);
3599 player->element_nr = EL_PLAYER_1 + i;
3601 player->present = FALSE;
3602 player->active = FALSE;
3603 player->mapped = FALSE;
3605 player->killed = FALSE;
3606 player->reanimated = FALSE;
3609 player->effective_action = 0;
3610 player->programmed_action = 0;
3613 player->score_final = 0;
3615 player->gems_still_needed = level.gems_needed;
3616 player->sokobanfields_still_needed = 0;
3617 player->lights_still_needed = 0;
3618 player->friends_still_needed = 0;
3620 for (j = 0; j < MAX_NUM_KEYS; j++)
3621 player->key[j] = FALSE;
3623 player->num_white_keys = 0;
3625 player->dynabomb_count = 0;
3626 player->dynabomb_size = 1;
3627 player->dynabombs_left = 0;
3628 player->dynabomb_xl = FALSE;
3630 player->MovDir = initial_move_dir;
3633 player->GfxDir = initial_move_dir;
3634 player->GfxAction = ACTION_DEFAULT;
3636 player->StepFrame = 0;
3638 player->initial_element = player->element_nr;
3639 player->artwork_element =
3640 (level.use_artwork_element[i] ? level.artwork_element[i] :
3641 player->element_nr);
3642 player->use_murphy = FALSE;
3644 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3645 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3647 player->gravity = level.initial_player_gravity[i];
3649 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3651 player->actual_frame_counter = 0;
3653 player->step_counter = 0;
3655 player->last_move_dir = initial_move_dir;
3657 player->is_active = FALSE;
3659 player->is_waiting = FALSE;
3660 player->is_moving = FALSE;
3661 player->is_auto_moving = FALSE;
3662 player->is_digging = FALSE;
3663 player->is_snapping = FALSE;
3664 player->is_collecting = FALSE;
3665 player->is_pushing = FALSE;
3666 player->is_switching = FALSE;
3667 player->is_dropping = FALSE;
3668 player->is_dropping_pressed = FALSE;
3670 player->is_bored = FALSE;
3671 player->is_sleeping = FALSE;
3673 player->frame_counter_bored = -1;
3674 player->frame_counter_sleeping = -1;
3676 player->anim_delay_counter = 0;
3677 player->post_delay_counter = 0;
3679 player->dir_waiting = initial_move_dir;
3680 player->action_waiting = ACTION_DEFAULT;
3681 player->last_action_waiting = ACTION_DEFAULT;
3682 player->special_action_bored = ACTION_DEFAULT;
3683 player->special_action_sleeping = ACTION_DEFAULT;
3685 player->switch_x = -1;
3686 player->switch_y = -1;
3688 player->drop_x = -1;
3689 player->drop_y = -1;
3691 player->show_envelope = 0;
3693 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3695 player->push_delay = -1; /* initialized when pushing starts */
3696 player->push_delay_value = game.initial_push_delay_value;
3698 player->drop_delay = 0;
3699 player->drop_pressed_delay = 0;
3701 player->last_jx = -1;
3702 player->last_jy = -1;
3706 player->shield_normal_time_left = 0;
3707 player->shield_deadly_time_left = 0;
3709 player->inventory_infinite_element = EL_UNDEFINED;
3710 player->inventory_size = 0;
3712 if (level.use_initial_inventory[i])
3714 for (j = 0; j < level.initial_inventory_size[i]; j++)
3716 int element = level.initial_inventory_content[i][j];
3717 int collect_count = element_info[element].collect_count_initial;
3720 if (!IS_CUSTOM_ELEMENT(element))
3723 if (collect_count == 0)
3724 player->inventory_infinite_element = element;
3726 for (k = 0; k < collect_count; k++)
3727 if (player->inventory_size < MAX_INVENTORY_SIZE)
3728 player->inventory_element[player->inventory_size++] = element;
3732 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3733 SnapField(player, 0, 0);
3735 player->LevelSolved = FALSE;
3736 player->GameOver = FALSE;
3738 player->LevelSolved_GameWon = FALSE;
3739 player->LevelSolved_GameEnd = FALSE;
3740 player->LevelSolved_PanelOff = FALSE;
3741 player->LevelSolved_SaveTape = FALSE;
3742 player->LevelSolved_SaveScore = FALSE;
3743 player->LevelSolved_CountingTime = 0;
3744 player->LevelSolved_CountingScore = 0;
3746 map_player_action[i] = i;
3749 network_player_action_received = FALSE;
3751 #if defined(NETWORK_AVALIABLE)
3752 /* initial null action */
3753 if (network_playing)
3754 SendToServer_MovePlayer(MV_NONE);
3763 TimeLeft = level.time;
3766 ScreenMovDir = MV_NONE;
3770 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3772 AllPlayersGone = FALSE;
3774 game.no_time_limit = (level.time == 0);
3776 game.yamyam_content_nr = 0;
3777 game.robot_wheel_active = FALSE;
3778 game.magic_wall_active = FALSE;
3779 game.magic_wall_time_left = 0;
3780 game.light_time_left = 0;
3781 game.timegate_time_left = 0;
3782 game.switchgate_pos = 0;
3783 game.wind_direction = level.wind_direction_initial;
3785 #if !USE_PLAYER_GRAVITY
3786 game.gravity = FALSE;
3787 game.explosions_delayed = TRUE;
3790 game.lenses_time_left = 0;
3791 game.magnify_time_left = 0;
3793 game.ball_state = level.ball_state_initial;
3794 game.ball_content_nr = 0;
3796 game.envelope_active = FALSE;
3798 /* set focus to local player for network games, else to all players */
3799 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3800 game.centered_player_nr_next = game.centered_player_nr;
3801 game.set_centered_player = FALSE;
3803 if (network_playing && tape.recording)
3805 /* store client dependent player focus when recording network games */
3806 tape.centered_player_nr_next = game.centered_player_nr_next;
3807 tape.set_centered_player = TRUE;
3810 for (i = 0; i < NUM_BELTS; i++)
3812 game.belt_dir[i] = MV_NONE;
3813 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3816 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3817 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3819 #if DEBUG_INIT_PLAYER
3822 printf("Player status at level initialization:\n");
3826 SCAN_PLAYFIELD(x, y)
3828 Feld[x][y] = level.field[x][y];
3829 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3830 ChangeDelay[x][y] = 0;
3831 ChangePage[x][y] = -1;
3832 #if USE_NEW_CUSTOM_VALUE
3833 CustomValue[x][y] = 0; /* initialized in InitField() */
3835 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3837 WasJustMoving[x][y] = 0;
3838 WasJustFalling[x][y] = 0;
3839 CheckCollision[x][y] = 0;
3840 CheckImpact[x][y] = 0;
3842 Pushed[x][y] = FALSE;
3844 ChangeCount[x][y] = 0;
3845 ChangeEvent[x][y] = -1;
3847 ExplodePhase[x][y] = 0;
3848 ExplodeDelay[x][y] = 0;
3849 ExplodeField[x][y] = EX_TYPE_NONE;
3851 RunnerVisit[x][y] = 0;
3852 PlayerVisit[x][y] = 0;
3855 GfxRandom[x][y] = INIT_GFX_RANDOM();
3856 GfxElement[x][y] = EL_UNDEFINED;
3857 GfxAction[x][y] = ACTION_DEFAULT;
3858 GfxDir[x][y] = MV_NONE;
3859 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3862 SCAN_PLAYFIELD(x, y)
3864 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3866 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3868 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3871 InitField(x, y, TRUE);
3873 ResetGfxAnimation(x, y);
3878 for (i = 0; i < MAX_PLAYERS; i++)
3880 struct PlayerInfo *player = &stored_player[i];
3882 /* set number of special actions for bored and sleeping animation */
3883 player->num_special_action_bored =
3884 get_num_special_action(player->artwork_element,
3885 ACTION_BORING_1, ACTION_BORING_LAST);
3886 player->num_special_action_sleeping =
3887 get_num_special_action(player->artwork_element,
3888 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3891 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3892 emulate_sb ? EMU_SOKOBAN :
3893 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3895 #if USE_NEW_ALL_SLIPPERY
3896 /* initialize type of slippery elements */
3897 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3899 if (!IS_CUSTOM_ELEMENT(i))
3901 /* default: elements slip down either to the left or right randomly */
3902 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3904 /* SP style elements prefer to slip down on the left side */
3905 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3906 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3908 /* BD style elements prefer to slip down on the left side */
3909 if (game.emulation == EMU_BOULDERDASH)
3910 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3915 /* initialize explosion and ignition delay */
3916 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3918 if (!IS_CUSTOM_ELEMENT(i))
3921 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3922 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3923 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3924 int last_phase = (num_phase + 1) * delay;
3925 int half_phase = (num_phase / 2) * delay;
3927 element_info[i].explosion_delay = last_phase - 1;
3928 element_info[i].ignition_delay = half_phase;
3930 if (i == EL_BLACK_ORB)
3931 element_info[i].ignition_delay = 1;
3935 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3936 element_info[i].explosion_delay = 1;
3938 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3939 element_info[i].ignition_delay = 1;
3943 /* correct non-moving belts to start moving left */
3944 for (i = 0; i < NUM_BELTS; i++)
3945 if (game.belt_dir[i] == MV_NONE)
3946 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3948 #if USE_NEW_PLAYER_ASSIGNMENTS
3949 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3950 /* choose default local player */
3951 local_player = &stored_player[0];
3953 for (i = 0; i < MAX_PLAYERS; i++)
3954 stored_player[i].connected = FALSE;
3956 local_player->connected = TRUE;
3957 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3960 printf("::: TEAM MODE: %d\n", game.team_mode);
3966 for (i = 0; i < MAX_PLAYERS; i++)
3967 stored_player[i].connected = tape.player_participates[i];
3969 /* try to guess locally connected team mode players (needed for correct
3970 assignment of player figures from level to locally playing players) */
3972 for (i = 0; i < MAX_PLAYERS; i++)
3973 if (tape.player_participates[i])
3974 stored_player[i].connected = TRUE;
3977 else if (game.team_mode && !options.network)
3979 /* try to guess locally connected team mode players (needed for correct
3980 assignment of player figures from level to locally playing players) */
3982 for (i = 0; i < MAX_PLAYERS; i++)
3983 if (setup.input[i].use_joystick ||
3984 setup.input[i].key.left != KSYM_UNDEFINED)
3985 stored_player[i].connected = TRUE;
3988 #if DEBUG_INIT_PLAYER
3991 printf("Player status after level initialization:\n");
3993 for (i = 0; i < MAX_PLAYERS; i++)
3995 struct PlayerInfo *player = &stored_player[i];
3997 printf("- player %d: present == %d, connected == %d, active == %d",
4003 if (local_player == player)
4004 printf(" (local player)");
4011 #if DEBUG_INIT_PLAYER
4013 printf("Reassigning players ...\n");
4016 /* check if any connected player was not found in playfield */
4017 for (i = 0; i < MAX_PLAYERS; i++)
4019 struct PlayerInfo *player = &stored_player[i];
4021 if (player->connected && !player->present)
4023 struct PlayerInfo *field_player = NULL;
4025 #if DEBUG_INIT_PLAYER
4027 printf("- looking for field player for player %d ...\n", i + 1);
4030 /* assign first free player found that is present in the playfield */
4033 /* first try: look for unmapped playfield player that is not connected */
4034 for (j = 0; j < MAX_PLAYERS; j++)
4035 if (field_player == NULL &&
4036 stored_player[j].present &&
4037 !stored_player[j].mapped &&
4038 !stored_player[j].connected)
4039 field_player = &stored_player[j];
4041 /* second try: look for *any* unmapped playfield player */
4042 for (j = 0; j < MAX_PLAYERS; j++)
4043 if (field_player == NULL &&
4044 stored_player[j].present &&
4045 !stored_player[j].mapped)
4046 field_player = &stored_player[j];
4048 /* first try: look for unmapped playfield player that is not connected */
4049 if (field_player == NULL)
4050 for (j = 0; j < MAX_PLAYERS; j++)
4051 if (stored_player[j].present &&
4052 !stored_player[j].mapped &&
4053 !stored_player[j].connected)
4054 field_player = &stored_player[j];
4056 /* second try: look for *any* unmapped playfield player */
4057 if (field_player == NULL)
4058 for (j = 0; j < MAX_PLAYERS; j++)
4059 if (stored_player[j].present &&
4060 !stored_player[j].mapped)
4061 field_player = &stored_player[j];
4064 if (field_player != NULL)
4066 int jx = field_player->jx, jy = field_player->jy;
4068 #if DEBUG_INIT_PLAYER
4070 printf("- found player %d\n", field_player->index_nr + 1);
4073 player->present = FALSE;
4074 player->active = FALSE;
4076 field_player->present = TRUE;
4077 field_player->active = TRUE;
4080 player->initial_element = field_player->initial_element;
4081 player->artwork_element = field_player->artwork_element;
4083 player->block_last_field = field_player->block_last_field;
4084 player->block_delay_adjustment = field_player->block_delay_adjustment;
4087 StorePlayer[jx][jy] = field_player->element_nr;
4089 field_player->jx = field_player->last_jx = jx;
4090 field_player->jy = field_player->last_jy = jy;
4092 if (local_player == player)
4093 local_player = field_player;
4095 map_player_action[field_player->index_nr] = i;
4097 field_player->mapped = TRUE;
4099 #if DEBUG_INIT_PLAYER
4101 printf("- map_player_action[%d] == %d\n",
4102 field_player->index_nr + 1, i + 1);
4107 if (player->connected && player->present)
4108 player->mapped = TRUE;
4111 #if DEBUG_INIT_PLAYER
4114 printf("Player status after player assignment (first stage):\n");
4116 for (i = 0; i < MAX_PLAYERS; i++)
4118 struct PlayerInfo *player = &stored_player[i];
4120 printf("- player %d: present == %d, connected == %d, active == %d",
4126 if (local_player == player)
4127 printf(" (local player)");
4136 /* check if any connected player was not found in playfield */
4137 for (i = 0; i < MAX_PLAYERS; i++)
4139 struct PlayerInfo *player = &stored_player[i];
4141 if (player->connected && !player->present)
4143 for (j = 0; j < MAX_PLAYERS; j++)
4145 struct PlayerInfo *field_player = &stored_player[j];
4146 int jx = field_player->jx, jy = field_player->jy;
4148 /* assign first free player found that is present in the playfield */
4149 if (field_player->present && !field_player->connected)
4151 player->present = TRUE;
4152 player->active = TRUE;
4154 field_player->present = FALSE;
4155 field_player->active = FALSE;
4157 player->initial_element = field_player->initial_element;
4158 player->artwork_element = field_player->artwork_element;
4160 player->block_last_field = field_player->block_last_field;
4161 player->block_delay_adjustment = field_player->block_delay_adjustment;
4163 StorePlayer[jx][jy] = player->element_nr;
4165 player->jx = player->last_jx = jx;
4166 player->jy = player->last_jy = jy;
4176 printf("::: local_player->present == %d\n", local_player->present);
4181 /* when playing a tape, eliminate all players who do not participate */
4183 #if USE_NEW_PLAYER_ASSIGNMENTS
4186 if (!game.team_mode)
4189 for (i = 0; i < MAX_PLAYERS; i++)
4191 if (stored_player[i].active &&
4192 !tape.player_participates[map_player_action[i]])
4194 struct PlayerInfo *player = &stored_player[i];
4195 int jx = player->jx, jy = player->jy;
4197 #if DEBUG_INIT_PLAYER
4199 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4202 player->active = FALSE;
4203 StorePlayer[jx][jy] = 0;
4204 Feld[jx][jy] = EL_EMPTY;
4210 for (i = 0; i < MAX_PLAYERS; i++)
4212 if (stored_player[i].active &&
4213 !tape.player_participates[i])
4215 struct PlayerInfo *player = &stored_player[i];
4216 int jx = player->jx, jy = player->jy;
4218 player->active = FALSE;
4219 StorePlayer[jx][jy] = 0;
4220 Feld[jx][jy] = EL_EMPTY;
4225 else if (!options.network && !game.team_mode) /* && !tape.playing */
4227 /* when in single player mode, eliminate all but the first active player */
4229 for (i = 0; i < MAX_PLAYERS; i++)
4231 if (stored_player[i].active)
4233 for (j = i + 1; j < MAX_PLAYERS; j++)
4235 if (stored_player[j].active)
4237 struct PlayerInfo *player = &stored_player[j];
4238 int jx = player->jx, jy = player->jy;
4240 player->active = FALSE;
4241 player->present = FALSE;
4243 StorePlayer[jx][jy] = 0;
4244 Feld[jx][jy] = EL_EMPTY;
4251 /* when recording the game, store which players take part in the game */
4254 #if USE_NEW_PLAYER_ASSIGNMENTS
4255 for (i = 0; i < MAX_PLAYERS; i++)
4256 if (stored_player[i].connected)
4257 tape.player_participates[i] = TRUE;
4259 for (i = 0; i < MAX_PLAYERS; i++)
4260 if (stored_player[i].active)
4261 tape.player_participates[i] = TRUE;
4265 #if DEBUG_INIT_PLAYER
4268 printf("Player status after player assignment (final stage):\n");
4270 for (i = 0; i < MAX_PLAYERS; i++)
4272 struct PlayerInfo *player = &stored_player[i];
4274 printf("- player %d: present == %d, connected == %d, active == %d",
4280 if (local_player == player)
4281 printf(" (local player)");
4288 if (BorderElement == EL_EMPTY)
4291 SBX_Right = lev_fieldx - SCR_FIELDX;
4293 SBY_Lower = lev_fieldy - SCR_FIELDY;
4298 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4300 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4305 // printf("::: START-0: %d, %d\n", lev_fieldx, SCR_FIELDX);
4306 // printf("::: START-1: %d, %d\n", SBX_Left, SBX_Right);
4309 if (full_lev_fieldx <= SCR_FIELDX)
4310 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4312 if (full_lev_fieldy <= SCR_FIELDY)
4313 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4315 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4316 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4318 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4319 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4323 printf("::: START-2: %d, %d (%d)\n", SBX_Left, SBX_Right,
4324 SBX_Right - SBX_Left + 1);
4328 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4330 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4333 if (EVEN(SCR_FIELDX))
4335 if (EVEN(SCR_FIELDY))
4340 printf("::: START-3: %d, %d\n", SBX_Left, SBX_Right);
4346 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4347 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4349 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4350 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4353 /* if local player not found, look for custom element that might create
4354 the player (make some assumptions about the right custom element) */
4355 if (!local_player->present)
4357 int start_x = 0, start_y = 0;
4358 int found_rating = 0;
4359 int found_element = EL_UNDEFINED;
4360 int player_nr = local_player->index_nr;
4362 SCAN_PLAYFIELD(x, y)
4364 int element = Feld[x][y];
4369 if (level.use_start_element[player_nr] &&
4370 level.start_element[player_nr] == element &&
4377 found_element = element;
4380 if (!IS_CUSTOM_ELEMENT(element))
4383 if (CAN_CHANGE(element))
4385 for (i = 0; i < element_info[element].num_change_pages; i++)
4387 /* check for player created from custom element as single target */
4388 content = element_info[element].change_page[i].target_element;
4389 is_player = ELEM_IS_PLAYER(content);
4391 if (is_player && (found_rating < 3 ||
4392 (found_rating == 3 && element < found_element)))
4398 found_element = element;
4403 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4405 /* check for player created from custom element as explosion content */
4406 content = element_info[element].content.e[xx][yy];
4407 is_player = ELEM_IS_PLAYER(content);
4409 if (is_player && (found_rating < 2 ||
4410 (found_rating == 2 && element < found_element)))
4412 start_x = x + xx - 1;
4413 start_y = y + yy - 1;
4416 found_element = element;
4419 if (!CAN_CHANGE(element))
4422 for (i = 0; i < element_info[element].num_change_pages; i++)
4424 /* check for player created from custom element as extended target */
4426 element_info[element].change_page[i].target_content.e[xx][yy];
4428 is_player = ELEM_IS_PLAYER(content);
4430 if (is_player && (found_rating < 1 ||
4431 (found_rating == 1 && element < found_element)))
4433 start_x = x + xx - 1;
4434 start_y = y + yy - 1;
4437 found_element = element;
4443 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4444 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4447 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4448 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4453 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4454 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4455 local_player->jx - MIDPOSX);
4457 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4458 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4459 local_player->jy - MIDPOSY);
4463 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4467 /* do not use PLAYING mask for fading out from main screen */
4468 game_status = GAME_MODE_MAIN;
4475 if (!game.restart_level)
4476 CloseDoor(DOOR_CLOSE_1);
4479 if (level_editor_test_game)
4480 FadeSkipNextFadeIn();
4482 FadeSetEnterScreen();
4484 if (level_editor_test_game)
4485 fading = fading_none;
4487 fading = menu.destination;
4491 FadeOut(REDRAW_FIELD);
4494 FadeOut(REDRAW_FIELD);
4500 game_status = GAME_MODE_PLAYING;
4503 /* !!! FIX THIS (START) !!! */
4504 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4506 InitGameEngine_EM();
4509 /* blit playfield from scroll buffer to normal back buffer for fading in */
4510 BlitScreenToBitmap_EM(backbuffer);
4513 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4515 InitGameEngine_SP();
4518 /* blit playfield from scroll buffer to normal back buffer for fading in */
4519 BlitScreenToBitmap_SP(backbuffer);
4524 DrawLevel(REDRAW_FIELD);
4527 /* after drawing the level, correct some elements */
4528 if (game.timegate_time_left == 0)
4529 CloseAllOpenTimegates();
4532 /* blit playfield from scroll buffer to normal back buffer for fading in */
4534 BlitScreenToBitmap(backbuffer);
4536 if (setup.soft_scrolling)
4537 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4542 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4546 /* blit playfield from scroll buffer to normal back buffer for fading in */
4547 BlitScreenToBitmap(backbuffer);
4549 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4551 /* !!! FIX THIS (END) !!! */
4554 FadeIn(REDRAW_FIELD);
4557 FadeIn(REDRAW_FIELD);
4562 if (!game.restart_level)
4564 /* copy default game door content to main double buffer */
4567 /* !!! CHECK AGAIN !!! */
4568 SetPanelBackground();
4569 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4570 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4572 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4574 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4575 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4576 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4577 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4580 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4581 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4585 SetPanelBackground();
4586 SetDrawBackgroundMask(REDRAW_DOOR_1);
4589 UpdateAndDisplayGameControlValues();
4591 UpdateGameDoorValues();
4592 DrawGameDoorValues();
4595 if (!game.restart_level)
4599 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4600 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4601 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4605 /* copy actual game door content to door double buffer for OpenDoor() */
4607 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4609 BlitBitmap(drawto, bitmap_db_door,
4610 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4613 OpenDoor(DOOR_OPEN_ALL);
4615 PlaySound(SND_GAME_STARTING);
4617 if (setup.sound_music)
4620 KeyboardAutoRepeatOffUnlessAutoplay();
4622 #if DEBUG_INIT_PLAYER
4625 printf("Player status (final):\n");
4627 for (i = 0; i < MAX_PLAYERS; i++)
4629 struct PlayerInfo *player = &stored_player[i];
4631 printf("- player %d: present == %d, connected == %d, active == %d",
4637 if (local_player == player)
4638 printf(" (local player)");
4653 if (!game.restart_level && !tape.playing)
4655 LevelStats_incPlayed(level_nr);
4657 SaveLevelSetup_SeriesInfo();
4660 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4664 game.restart_level = FALSE;
4667 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4669 /* this is used for non-R'n'D game engines to update certain engine values */
4671 /* needed to determine if sounds are played within the visible screen area */
4672 scroll_x = actual_scroll_x;
4673 scroll_y = actual_scroll_y;
4676 void InitMovDir(int x, int y)
4678 int i, element = Feld[x][y];
4679 static int xy[4][2] =
4686 static int direction[3][4] =
4688 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4689 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4690 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4699 Feld[x][y] = EL_BUG;
4700 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4703 case EL_SPACESHIP_RIGHT:
4704 case EL_SPACESHIP_UP:
4705 case EL_SPACESHIP_LEFT:
4706 case EL_SPACESHIP_DOWN:
4707 Feld[x][y] = EL_SPACESHIP;
4708 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4711 case EL_BD_BUTTERFLY_RIGHT:
4712 case EL_BD_BUTTERFLY_UP:
4713 case EL_BD_BUTTERFLY_LEFT:
4714 case EL_BD_BUTTERFLY_DOWN:
4715 Feld[x][y] = EL_BD_BUTTERFLY;
4716 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4719 case EL_BD_FIREFLY_RIGHT:
4720 case EL_BD_FIREFLY_UP:
4721 case EL_BD_FIREFLY_LEFT:
4722 case EL_BD_FIREFLY_DOWN:
4723 Feld[x][y] = EL_BD_FIREFLY;
4724 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4727 case EL_PACMAN_RIGHT:
4729 case EL_PACMAN_LEFT:
4730 case EL_PACMAN_DOWN:
4731 Feld[x][y] = EL_PACMAN;
4732 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4735 case EL_YAMYAM_LEFT:
4736 case EL_YAMYAM_RIGHT:
4738 case EL_YAMYAM_DOWN:
4739 Feld[x][y] = EL_YAMYAM;
4740 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4743 case EL_SP_SNIKSNAK:
4744 MovDir[x][y] = MV_UP;
4747 case EL_SP_ELECTRON:
4748 MovDir[x][y] = MV_LEFT;
4755 Feld[x][y] = EL_MOLE;
4756 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4760 if (IS_CUSTOM_ELEMENT(element))
4762 struct ElementInfo *ei = &element_info[element];
4763 int move_direction_initial = ei->move_direction_initial;
4764 int move_pattern = ei->move_pattern;
4766 if (move_direction_initial == MV_START_PREVIOUS)
4768 if (MovDir[x][y] != MV_NONE)
4771 move_direction_initial = MV_START_AUTOMATIC;
4774 if (move_direction_initial == MV_START_RANDOM)
4775 MovDir[x][y] = 1 << RND(4);
4776 else if (move_direction_initial & MV_ANY_DIRECTION)
4777 MovDir[x][y] = move_direction_initial;
4778 else if (move_pattern == MV_ALL_DIRECTIONS ||
4779 move_pattern == MV_TURNING_LEFT ||
4780 move_pattern == MV_TURNING_RIGHT ||
4781 move_pattern == MV_TURNING_LEFT_RIGHT ||
4782 move_pattern == MV_TURNING_RIGHT_LEFT ||
4783 move_pattern == MV_TURNING_RANDOM)
4784 MovDir[x][y] = 1 << RND(4);
4785 else if (move_pattern == MV_HORIZONTAL)
4786 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4787 else if (move_pattern == MV_VERTICAL)
4788 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4789 else if (move_pattern & MV_ANY_DIRECTION)
4790 MovDir[x][y] = element_info[element].move_pattern;
4791 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4792 move_pattern == MV_ALONG_RIGHT_SIDE)
4794 /* use random direction as default start direction */
4795 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4796 MovDir[x][y] = 1 << RND(4);
4798 for (i = 0; i < NUM_DIRECTIONS; i++)
4800 int x1 = x + xy[i][0];
4801 int y1 = y + xy[i][1];
4803 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4805 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4806 MovDir[x][y] = direction[0][i];
4808 MovDir[x][y] = direction[1][i];
4817 MovDir[x][y] = 1 << RND(4);
4819 if (element != EL_BUG &&
4820 element != EL_SPACESHIP &&
4821 element != EL_BD_BUTTERFLY &&
4822 element != EL_BD_FIREFLY)
4825 for (i = 0; i < NUM_DIRECTIONS; i++)
4827 int x1 = x + xy[i][0];
4828 int y1 = y + xy[i][1];
4830 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4832 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4834 MovDir[x][y] = direction[0][i];
4837 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4838 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4840 MovDir[x][y] = direction[1][i];
4849 GfxDir[x][y] = MovDir[x][y];
4852 void InitAmoebaNr(int x, int y)
4855 int group_nr = AmoebeNachbarNr(x, y);
4859 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4861 if (AmoebaCnt[i] == 0)
4869 AmoebaNr[x][y] = group_nr;
4870 AmoebaCnt[group_nr]++;
4871 AmoebaCnt2[group_nr]++;
4874 static void PlayerWins(struct PlayerInfo *player)
4876 player->LevelSolved = TRUE;
4877 player->GameOver = TRUE;
4879 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4880 level.native_em_level->lev->score : player->score);
4882 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4884 player->LevelSolved_CountingScore = player->score_final;
4889 static int time, time_final;
4890 static int score, score_final;
4891 static int game_over_delay_1 = 0;
4892 static int game_over_delay_2 = 0;
4893 int game_over_delay_value_1 = 50;
4894 int game_over_delay_value_2 = 50;
4896 if (!local_player->LevelSolved_GameWon)
4900 /* do not start end game actions before the player stops moving (to exit) */
4901 if (local_player->MovPos)
4904 local_player->LevelSolved_GameWon = TRUE;
4905 local_player->LevelSolved_SaveTape = tape.recording;
4906 local_player->LevelSolved_SaveScore = !tape.playing;
4910 LevelStats_incSolved(level_nr);
4912 SaveLevelSetup_SeriesInfo();
4915 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4919 if (tape.auto_play) /* tape might already be stopped here */
4920 tape.auto_play_level_solved = TRUE;
4926 game_over_delay_1 = game_over_delay_value_1;
4927 game_over_delay_2 = game_over_delay_value_2;
4929 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4930 score = score_final = local_player->score_final;
4935 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4937 else if (game.no_time_limit && TimePlayed < 999)
4940 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4943 local_player->score_final = score_final;
4945 if (level_editor_test_game)
4948 score = score_final;
4951 local_player->LevelSolved_CountingTime = time;
4952 local_player->LevelSolved_CountingScore = score;
4954 game_panel_controls[GAME_PANEL_TIME].value = time;
4955 game_panel_controls[GAME_PANEL_SCORE].value = score;
4957 DisplayGameControlValues();
4959 DrawGameValue_Time(time);
4960 DrawGameValue_Score(score);
4964 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4966 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4968 /* close exit door after last player */
4969 if ((AllPlayersGone &&
4970 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4971 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4972 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4973 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4974 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4976 int element = Feld[ExitX][ExitY];
4979 if (element == EL_EM_EXIT_OPEN ||
4980 element == EL_EM_STEEL_EXIT_OPEN)
4987 Feld[ExitX][ExitY] =
4988 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4989 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4990 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4991 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4992 EL_EM_STEEL_EXIT_CLOSING);
4994 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4998 /* player disappears */
4999 DrawLevelField(ExitX, ExitY);
5002 for (i = 0; i < MAX_PLAYERS; i++)
5004 struct PlayerInfo *player = &stored_player[i];
5006 if (player->present)
5008 RemovePlayer(player);
5010 /* player disappears */
5011 DrawLevelField(player->jx, player->jy);
5016 PlaySound(SND_GAME_WINNING);
5019 if (game_over_delay_1 > 0)
5021 game_over_delay_1--;
5026 if (time != time_final)
5028 int time_to_go = ABS(time_final - time);
5029 int time_count_dir = (time < time_final ? +1 : -1);
5030 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5032 time += time_count_steps * time_count_dir;
5033 score += time_count_steps * level.score[SC_TIME_BONUS];
5036 local_player->LevelSolved_CountingTime = time;
5037 local_player->LevelSolved_CountingScore = score;
5039 game_panel_controls[GAME_PANEL_TIME].value = time;
5040 game_panel_controls[GAME_PANEL_SCORE].value = score;
5042 DisplayGameControlValues();
5044 DrawGameValue_Time(time);
5045 DrawGameValue_Score(score);
5048 if (time == time_final)
5049 StopSound(SND_GAME_LEVELTIME_BONUS);
5050 else if (setup.sound_loops)
5051 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5053 PlaySound(SND_GAME_LEVELTIME_BONUS);
5058 local_player->LevelSolved_PanelOff = TRUE;
5060 if (game_over_delay_2 > 0)
5062 game_over_delay_2--;
5075 boolean raise_level = FALSE;
5077 local_player->LevelSolved_GameEnd = TRUE;
5079 CloseDoor(DOOR_CLOSE_1);
5081 if (local_player->LevelSolved_SaveTape)
5088 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5090 SaveTape(tape.level_nr); /* ask to save tape */
5094 if (level_editor_test_game)
5096 game_status = GAME_MODE_MAIN;
5099 DrawAndFadeInMainMenu(REDRAW_FIELD);
5107 if (!local_player->LevelSolved_SaveScore)
5110 FadeOut(REDRAW_FIELD);
5113 game_status = GAME_MODE_MAIN;
5115 DrawAndFadeInMainMenu(REDRAW_FIELD);
5120 if (level_nr == leveldir_current->handicap_level)
5122 leveldir_current->handicap_level++;
5124 SaveLevelSetup_SeriesInfo();
5127 if (level_nr < leveldir_current->last_level)
5128 raise_level = TRUE; /* advance to next level */
5130 if ((hi_pos = NewHiScore()) >= 0)
5132 game_status = GAME_MODE_SCORES;
5134 DrawHallOfFame(hi_pos);
5145 FadeOut(REDRAW_FIELD);
5148 game_status = GAME_MODE_MAIN;
5156 DrawAndFadeInMainMenu(REDRAW_FIELD);
5165 LoadScore(level_nr);
5167 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5168 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5171 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5173 if (local_player->score_final > highscore[k].Score)
5175 /* player has made it to the hall of fame */
5177 if (k < MAX_SCORE_ENTRIES - 1)
5179 int m = MAX_SCORE_ENTRIES - 1;
5182 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5183 if (strEqual(setup.player_name, highscore[l].Name))
5185 if (m == k) /* player's new highscore overwrites his old one */
5189 for (l = m; l > k; l--)
5191 strcpy(highscore[l].Name, highscore[l - 1].Name);
5192 highscore[l].Score = highscore[l - 1].Score;
5199 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5200 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5201 highscore[k].Score = local_player->score_final;
5207 else if (!strncmp(setup.player_name, highscore[k].Name,
5208 MAX_PLAYER_NAME_LEN))
5209 break; /* player already there with a higher score */
5215 SaveScore(level_nr);
5220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5222 int element = Feld[x][y];
5223 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5224 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5225 int horiz_move = (dx != 0);
5226 int sign = (horiz_move ? dx : dy);
5227 int step = sign * element_info[element].move_stepsize;
5229 /* special values for move stepsize for spring and things on conveyor belt */
5232 if (CAN_FALL(element) &&
5233 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5234 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5235 else if (element == EL_SPRING)
5236 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5242 inline static int getElementMoveStepsize(int x, int y)
5244 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5249 if (player->GfxAction != action || player->GfxDir != dir)
5252 printf("Player frame reset! (%d => %d, %d => %d)\n",
5253 player->GfxAction, action, player->GfxDir, dir);
5256 player->GfxAction = action;
5257 player->GfxDir = dir;
5259 player->StepFrame = 0;
5263 #if USE_GFX_RESET_GFX_ANIMATION
5264 static void ResetGfxFrame(int x, int y, boolean redraw)
5266 int element = Feld[x][y];
5267 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5268 int last_gfx_frame = GfxFrame[x][y];
5270 if (graphic_info[graphic].anim_global_sync)
5271 GfxFrame[x][y] = FrameCounter;
5272 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5273 GfxFrame[x][y] = CustomValue[x][y];
5274 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5275 GfxFrame[x][y] = element_info[element].collect_score;
5276 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5277 GfxFrame[x][y] = ChangeDelay[x][y];
5279 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5280 DrawLevelGraphicAnimation(x, y, graphic);
5284 static void ResetGfxAnimation(int x, int y)
5286 GfxAction[x][y] = ACTION_DEFAULT;
5287 GfxDir[x][y] = MovDir[x][y];
5290 #if USE_GFX_RESET_GFX_ANIMATION
5291 ResetGfxFrame(x, y, FALSE);
5295 static void ResetRandomAnimationValue(int x, int y)
5297 GfxRandom[x][y] = INIT_GFX_RANDOM();
5300 void InitMovingField(int x, int y, int direction)
5302 int element = Feld[x][y];
5303 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5304 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5307 boolean is_moving_before, is_moving_after;
5309 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5312 /* check if element was/is moving or being moved before/after mode change */
5315 is_moving_before = (WasJustMoving[x][y] != 0);
5317 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5318 is_moving_before = WasJustMoving[x][y];
5321 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5323 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5325 /* reset animation only for moving elements which change direction of moving
5326 or which just started or stopped moving
5327 (else CEs with property "can move" / "not moving" are reset each frame) */
5328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5330 if (is_moving_before != is_moving_after ||
5331 direction != MovDir[x][y])
5332 ResetGfxAnimation(x, y);
5334 if ((is_moving_before || is_moving_after) && !continues_moving)
5335 ResetGfxAnimation(x, y);
5338 if (!continues_moving)
5339 ResetGfxAnimation(x, y);
5342 MovDir[x][y] = direction;
5343 GfxDir[x][y] = direction;
5345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5346 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5347 direction == MV_DOWN && CAN_FALL(element) ?
5348 ACTION_FALLING : ACTION_MOVING);
5350 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5351 ACTION_FALLING : ACTION_MOVING);
5354 /* this is needed for CEs with property "can move" / "not moving" */
5356 if (is_moving_after)
5358 if (Feld[newx][newy] == EL_EMPTY)
5359 Feld[newx][newy] = EL_BLOCKED;
5361 MovDir[newx][newy] = MovDir[x][y];
5363 #if USE_NEW_CUSTOM_VALUE
5364 CustomValue[newx][newy] = CustomValue[x][y];
5367 GfxFrame[newx][newy] = GfxFrame[x][y];
5368 GfxRandom[newx][newy] = GfxRandom[x][y];
5369 GfxAction[newx][newy] = GfxAction[x][y];
5370 GfxDir[newx][newy] = GfxDir[x][y];
5374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5376 int direction = MovDir[x][y];
5377 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5378 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5386 int oldx = x, oldy = y;
5387 int direction = MovDir[x][y];
5389 if (direction == MV_LEFT)
5391 else if (direction == MV_RIGHT)
5393 else if (direction == MV_UP)
5395 else if (direction == MV_DOWN)
5398 *comes_from_x = oldx;
5399 *comes_from_y = oldy;
5402 int MovingOrBlocked2Element(int x, int y)
5404 int element = Feld[x][y];
5406 if (element == EL_BLOCKED)
5410 Blocked2Moving(x, y, &oldx, &oldy);
5411 return Feld[oldx][oldy];
5417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5419 /* like MovingOrBlocked2Element(), but if element is moving
5420 and (x,y) is the field the moving element is just leaving,
5421 return EL_BLOCKED instead of the element value */
5422 int element = Feld[x][y];
5424 if (IS_MOVING(x, y))
5426 if (element == EL_BLOCKED)
5430 Blocked2Moving(x, y, &oldx, &oldy);
5431 return Feld[oldx][oldy];
5440 static void RemoveField(int x, int y)
5442 Feld[x][y] = EL_EMPTY;
5448 #if USE_NEW_CUSTOM_VALUE
5449 CustomValue[x][y] = 0;
5453 ChangeDelay[x][y] = 0;
5454 ChangePage[x][y] = -1;
5455 Pushed[x][y] = FALSE;
5458 ExplodeField[x][y] = EX_TYPE_NONE;
5461 GfxElement[x][y] = EL_UNDEFINED;
5462 GfxAction[x][y] = ACTION_DEFAULT;
5463 GfxDir[x][y] = MV_NONE;
5465 /* !!! this would prevent the removed tile from being redrawn !!! */
5466 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5470 void RemoveMovingField(int x, int y)
5472 int oldx = x, oldy = y, newx = x, newy = y;
5473 int element = Feld[x][y];
5474 int next_element = EL_UNDEFINED;
5476 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5479 if (IS_MOVING(x, y))
5481 Moving2Blocked(x, y, &newx, &newy);
5483 if (Feld[newx][newy] != EL_BLOCKED)
5485 /* element is moving, but target field is not free (blocked), but
5486 already occupied by something different (example: acid pool);
5487 in this case, only remove the moving field, but not the target */
5489 RemoveField(oldx, oldy);
5491 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5493 TEST_DrawLevelField(oldx, oldy);
5498 else if (element == EL_BLOCKED)
5500 Blocked2Moving(x, y, &oldx, &oldy);
5501 if (!IS_MOVING(oldx, oldy))
5505 if (element == EL_BLOCKED &&
5506 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5507 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5508 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5509 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5510 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5511 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5512 next_element = get_next_element(Feld[oldx][oldy]);
5514 RemoveField(oldx, oldy);
5515 RemoveField(newx, newy);
5517 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5519 if (next_element != EL_UNDEFINED)
5520 Feld[oldx][oldy] = next_element;
5522 TEST_DrawLevelField(oldx, oldy);
5523 TEST_DrawLevelField(newx, newy);
5526 void DrawDynamite(int x, int y)
5528 int sx = SCREENX(x), sy = SCREENY(y);
5529 int graphic = el2img(Feld[x][y]);
5532 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5535 if (IS_WALKABLE_INSIDE(Back[x][y]))
5539 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5540 else if (Store[x][y])
5541 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5543 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5545 if (Back[x][y] || Store[x][y])
5546 DrawGraphicThruMask(sx, sy, graphic, frame);
5548 DrawGraphic(sx, sy, graphic, frame);
5551 void CheckDynamite(int x, int y)
5553 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5557 if (MovDelay[x][y] != 0)
5560 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5566 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5571 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5573 boolean num_checked_players = 0;
5576 for (i = 0; i < MAX_PLAYERS; i++)
5578 if (stored_player[i].active)
5580 int sx = stored_player[i].jx;
5581 int sy = stored_player[i].jy;
5583 if (num_checked_players == 0)
5590 *sx1 = MIN(*sx1, sx);
5591 *sy1 = MIN(*sy1, sy);
5592 *sx2 = MAX(*sx2, sx);
5593 *sy2 = MAX(*sy2, sy);
5596 num_checked_players++;
5601 static boolean checkIfAllPlayersFitToScreen_RND()
5603 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5605 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5607 return (sx2 - sx1 < SCR_FIELDX &&
5608 sy2 - sy1 < SCR_FIELDY);
5611 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5613 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5615 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5617 *sx = (sx1 + sx2) / 2;
5618 *sy = (sy1 + sy2) / 2;
5621 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5622 boolean center_screen, boolean quick_relocation)
5624 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625 boolean no_delay = (tape.warp_forward);
5626 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5629 if (quick_relocation)
5631 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5633 if (!level.shifted_relocation || center_screen)
5635 /* quick relocation (without scrolling), with centering of screen */
5637 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5638 x > SBX_Right + MIDPOSX ? SBX_Right :
5641 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5647 /* quick relocation (without scrolling), but do not center screen */
5649 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5650 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5653 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5657 int offset_x = x + (scroll_x - center_scroll_x);
5658 int offset_y = y + (scroll_y - center_scroll_y);
5660 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5661 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5662 offset_x - MIDPOSX);
5664 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5665 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5666 offset_y - MIDPOSY);
5672 if (!level.shifted_relocation || center_screen)
5674 /* quick relocation (without scrolling), with centering of screen */
5676 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5677 x > SBX_Right + MIDPOSX ? SBX_Right :
5680 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5686 /* quick relocation (without scrolling), but do not center screen */
5688 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5689 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5692 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5693 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5696 int offset_x = x + (scroll_x - center_scroll_x);
5697 int offset_y = y + (scroll_y - center_scroll_y);
5699 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5700 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5701 offset_x - MIDPOSX);
5703 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5705 offset_y - MIDPOSY);
5708 /* quick relocation (without scrolling), inside visible screen area */
5710 int offset = game.scroll_delay_value;
5712 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5713 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5714 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5716 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5717 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5718 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5720 /* don't scroll over playfield boundaries */
5721 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5722 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5724 /* don't scroll over playfield boundaries */
5725 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5726 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5730 RedrawPlayfield(TRUE, 0,0,0,0);
5735 int scroll_xx, scroll_yy;
5737 if (!level.shifted_relocation || center_screen)
5739 /* visible relocation (with scrolling), with centering of screen */
5741 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5742 x > SBX_Right + MIDPOSX ? SBX_Right :
5745 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5751 /* visible relocation (with scrolling), but do not center screen */
5753 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5754 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5757 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5758 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5761 int offset_x = x + (scroll_x - center_scroll_x);
5762 int offset_y = y + (scroll_y - center_scroll_y);
5764 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5765 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5766 offset_x - MIDPOSX);
5768 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5769 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5770 offset_y - MIDPOSY);
5775 /* visible relocation (with scrolling), with centering of screen */
5777 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5778 x > SBX_Right + MIDPOSX ? SBX_Right :
5781 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5782 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5786 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5788 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5791 int fx = FX, fy = FY;
5793 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5794 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5796 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5802 fx += dx * TILEX / 2;
5803 fy += dy * TILEY / 2;
5805 ScrollLevel(dx, dy);
5808 /* scroll in two steps of half tile size to make things smoother */
5809 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5811 Delay(wait_delay_value);
5813 /* scroll second step to align at full tile size */
5815 Delay(wait_delay_value);
5820 Delay(wait_delay_value);
5824 void RelocatePlayer(int jx, int jy, int el_player_raw)
5826 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5827 int player_nr = GET_PLAYER_NR(el_player);
5828 struct PlayerInfo *player = &stored_player[player_nr];
5829 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5830 boolean no_delay = (tape.warp_forward);
5831 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5832 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5833 int old_jx = player->jx;
5834 int old_jy = player->jy;
5835 int old_element = Feld[old_jx][old_jy];
5836 int element = Feld[jx][jy];
5837 boolean player_relocated = (old_jx != jx || old_jy != jy);
5839 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5840 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5841 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5842 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5843 int leave_side_horiz = move_dir_horiz;
5844 int leave_side_vert = move_dir_vert;
5845 int enter_side = enter_side_horiz | enter_side_vert;
5846 int leave_side = leave_side_horiz | leave_side_vert;
5848 if (player->GameOver) /* do not reanimate dead player */
5851 if (!player_relocated) /* no need to relocate the player */
5854 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5856 RemoveField(jx, jy); /* temporarily remove newly placed player */
5857 DrawLevelField(jx, jy);
5860 if (player->present)
5862 while (player->MovPos)
5864 ScrollPlayer(player, SCROLL_GO_ON);
5865 ScrollScreen(NULL, SCROLL_GO_ON);
5867 AdvanceFrameAndPlayerCounters(player->index_nr);
5872 Delay(wait_delay_value);
5875 DrawPlayer(player); /* needed here only to cleanup last field */
5876 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5878 player->is_moving = FALSE;
5881 if (IS_CUSTOM_ELEMENT(old_element))
5882 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5884 player->index_bit, leave_side);
5886 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5888 player->index_bit, leave_side);
5890 Feld[jx][jy] = el_player;
5891 InitPlayerField(jx, jy, el_player, TRUE);
5893 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5894 possible that the relocation target field did not contain a player element,
5895 but a walkable element, to which the new player was relocated -- in this
5896 case, restore that (already initialized!) element on the player field */
5897 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5899 Feld[jx][jy] = element; /* restore previously existing element */
5901 /* !!! do not initialize already initialized element a second time !!! */
5902 /* (this causes at least problems with "element creation" CE trigger for
5903 already existing elements, and existing Sokoban fields counted twice) */
5904 InitField(jx, jy, FALSE);
5908 /* only visually relocate centered player */
5909 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5910 FALSE, level.instant_relocation);
5912 TestIfPlayerTouchesBadThing(jx, jy);
5913 TestIfPlayerTouchesCustomElement(jx, jy);
5915 if (IS_CUSTOM_ELEMENT(element))
5916 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5917 player->index_bit, enter_side);
5919 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5920 player->index_bit, enter_side);
5923 if (player->is_switching)
5925 /* ensure that relocation while still switching an element does not cause
5926 a new element to be treated as also switched directly after relocation
5927 (this is important for teleporter switches that teleport the player to
5928 a place where another teleporter switch is in the same direction, which
5929 would then incorrectly be treated as immediately switched before the
5930 direction key that caused the switch was released) */
5932 player->switch_x += jx - old_jx;
5933 player->switch_y += jy - old_jy;
5938 void Explode(int ex, int ey, int phase, int mode)
5944 /* !!! eliminate this variable !!! */
5945 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5947 if (game.explosions_delayed)
5949 ExplodeField[ex][ey] = mode;
5953 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5955 int center_element = Feld[ex][ey];
5956 int artwork_element, explosion_element; /* set these values later */
5959 /* --- This is only really needed (and now handled) in "Impact()". --- */
5960 /* do not explode moving elements that left the explode field in time */
5961 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5962 center_element == EL_EMPTY &&
5963 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5968 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5969 if (mode == EX_TYPE_NORMAL ||
5970 mode == EX_TYPE_CENTER ||
5971 mode == EX_TYPE_CROSS)
5972 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5975 /* remove things displayed in background while burning dynamite */
5976 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5979 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5981 /* put moving element to center field (and let it explode there) */
5982 center_element = MovingOrBlocked2Element(ex, ey);
5983 RemoveMovingField(ex, ey);
5984 Feld[ex][ey] = center_element;
5987 /* now "center_element" is finally determined -- set related values now */
5988 artwork_element = center_element; /* for custom player artwork */
5989 explosion_element = center_element; /* for custom player artwork */
5991 if (IS_PLAYER(ex, ey))
5993 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5995 artwork_element = stored_player[player_nr].artwork_element;
5997 if (level.use_explosion_element[player_nr])
5999 explosion_element = level.explosion_element[player_nr];
6000 artwork_element = explosion_element;
6005 if (mode == EX_TYPE_NORMAL ||
6006 mode == EX_TYPE_CENTER ||
6007 mode == EX_TYPE_CROSS)
6008 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6011 last_phase = element_info[explosion_element].explosion_delay + 1;
6013 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6015 int xx = x - ex + 1;
6016 int yy = y - ey + 1;
6019 if (!IN_LEV_FIELD(x, y) ||
6020 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6021 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
6024 element = Feld[x][y];
6026 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6028 element = MovingOrBlocked2Element(x, y);
6030 if (!IS_EXPLOSION_PROOF(element))
6031 RemoveMovingField(x, y);
6034 /* indestructible elements can only explode in center (but not flames) */
6035 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6036 mode == EX_TYPE_BORDER)) ||
6037 element == EL_FLAMES)
6040 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6041 behaviour, for example when touching a yamyam that explodes to rocks
6042 with active deadly shield, a rock is created under the player !!! */
6043 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6045 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6046 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6047 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6049 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6052 if (IS_ACTIVE_BOMB(element))
6054 /* re-activate things under the bomb like gate or penguin */
6055 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6062 /* save walkable background elements while explosion on same tile */
6063 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6064 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6065 Back[x][y] = element;
6067 /* ignite explodable elements reached by other explosion */
6068 if (element == EL_EXPLOSION)
6069 element = Store2[x][y];
6071 if (AmoebaNr[x][y] &&
6072 (element == EL_AMOEBA_FULL ||
6073 element == EL_BD_AMOEBA ||
6074 element == EL_AMOEBA_GROWING))
6076 AmoebaCnt[AmoebaNr[x][y]]--;
6077 AmoebaCnt2[AmoebaNr[x][y]]--;
6082 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6084 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6086 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6088 if (PLAYERINFO(ex, ey)->use_murphy)
6089 Store[x][y] = EL_EMPTY;
6092 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6093 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6094 else if (ELEM_IS_PLAYER(center_element))
6095 Store[x][y] = EL_EMPTY;
6096 else if (center_element == EL_YAMYAM)
6097 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6098 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6099 Store[x][y] = element_info[center_element].content.e[xx][yy];
6101 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6102 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6103 otherwise) -- FIX THIS !!! */
6104 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6105 Store[x][y] = element_info[element].content.e[1][1];
6107 else if (!CAN_EXPLODE(element))
6108 Store[x][y] = element_info[element].content.e[1][1];
6111 Store[x][y] = EL_EMPTY;
6113 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6114 center_element == EL_AMOEBA_TO_DIAMOND)
6115 Store2[x][y] = element;
6117 Feld[x][y] = EL_EXPLOSION;
6118 GfxElement[x][y] = artwork_element;
6120 ExplodePhase[x][y] = 1;
6121 ExplodeDelay[x][y] = last_phase;
6126 if (center_element == EL_YAMYAM)
6127 game.yamyam_content_nr =
6128 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6140 GfxFrame[x][y] = 0; /* restart explosion animation */
6142 last_phase = ExplodeDelay[x][y];
6144 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6148 /* activate this even in non-DEBUG version until cause for crash in
6149 getGraphicAnimationFrame() (see below) is found and eliminated */
6155 /* this can happen if the player leaves an explosion just in time */
6156 if (GfxElement[x][y] == EL_UNDEFINED)
6157 GfxElement[x][y] = EL_EMPTY;
6159 if (GfxElement[x][y] == EL_UNDEFINED)
6162 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6163 printf("Explode(): This should never happen!\n");
6166 GfxElement[x][y] = EL_EMPTY;
6172 border_element = Store2[x][y];
6173 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6174 border_element = StorePlayer[x][y];
6176 if (phase == element_info[border_element].ignition_delay ||
6177 phase == last_phase)
6179 boolean border_explosion = FALSE;
6181 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6182 !PLAYER_EXPLOSION_PROTECTED(x, y))
6184 KillPlayerUnlessExplosionProtected(x, y);
6185 border_explosion = TRUE;
6187 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6189 Feld[x][y] = Store2[x][y];
6192 border_explosion = TRUE;
6194 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6196 AmoebeUmwandeln(x, y);
6198 border_explosion = TRUE;
6201 /* if an element just explodes due to another explosion (chain-reaction),
6202 do not immediately end the new explosion when it was the last frame of
6203 the explosion (as it would be done in the following "if"-statement!) */
6204 if (border_explosion && phase == last_phase)
6208 if (phase == last_phase)
6212 element = Feld[x][y] = Store[x][y];
6213 Store[x][y] = Store2[x][y] = 0;
6214 GfxElement[x][y] = EL_UNDEFINED;
6216 /* player can escape from explosions and might therefore be still alive */
6217 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6218 element <= EL_PLAYER_IS_EXPLODING_4)
6220 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6221 int explosion_element = EL_PLAYER_1 + player_nr;
6222 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6223 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6225 if (level.use_explosion_element[player_nr])
6226 explosion_element = level.explosion_element[player_nr];
6228 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6229 element_info[explosion_element].content.e[xx][yy]);
6232 /* restore probably existing indestructible background element */
6233 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6234 element = Feld[x][y] = Back[x][y];
6237 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6238 GfxDir[x][y] = MV_NONE;
6239 ChangeDelay[x][y] = 0;
6240 ChangePage[x][y] = -1;
6242 #if USE_NEW_CUSTOM_VALUE
6243 CustomValue[x][y] = 0;
6246 InitField_WithBug2(x, y, FALSE);
6248 TEST_DrawLevelField(x, y);
6250 TestIfElementTouchesCustomElement(x, y);
6252 if (GFX_CRUMBLED(element))
6253 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6255 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6256 StorePlayer[x][y] = 0;
6258 if (ELEM_IS_PLAYER(element))
6259 RelocatePlayer(x, y, element);
6261 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6263 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6264 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6267 TEST_DrawLevelFieldCrumbled(x, y);
6269 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6271 DrawLevelElement(x, y, Back[x][y]);
6272 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6274 else if (IS_WALKABLE_UNDER(Back[x][y]))
6276 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6277 DrawLevelElementThruMask(x, y, Back[x][y]);
6279 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6280 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6284 void DynaExplode(int ex, int ey)
6287 int dynabomb_element = Feld[ex][ey];
6288 int dynabomb_size = 1;
6289 boolean dynabomb_xl = FALSE;
6290 struct PlayerInfo *player;
6291 static int xy[4][2] =
6299 if (IS_ACTIVE_BOMB(dynabomb_element))
6301 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6302 dynabomb_size = player->dynabomb_size;
6303 dynabomb_xl = player->dynabomb_xl;
6304 player->dynabombs_left++;
6307 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6309 for (i = 0; i < NUM_DIRECTIONS; i++)
6311 for (j = 1; j <= dynabomb_size; j++)
6313 int x = ex + j * xy[i][0];
6314 int y = ey + j * xy[i][1];
6317 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6320 element = Feld[x][y];
6322 /* do not restart explosions of fields with active bombs */
6323 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6326 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6328 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6329 !IS_DIGGABLE(element) && !dynabomb_xl)
6335 void Bang(int x, int y)
6337 int element = MovingOrBlocked2Element(x, y);
6338 int explosion_type = EX_TYPE_NORMAL;
6340 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6342 struct PlayerInfo *player = PLAYERINFO(x, y);
6344 #if USE_FIX_CE_ACTION_WITH_PLAYER
6345 element = Feld[x][y] = player->initial_element;
6347 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6348 player->element_nr);
6351 if (level.use_explosion_element[player->index_nr])
6353 int explosion_element = level.explosion_element[player->index_nr];
6355 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6356 explosion_type = EX_TYPE_CROSS;
6357 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6358 explosion_type = EX_TYPE_CENTER;
6366 case EL_BD_BUTTERFLY:
6369 case EL_DARK_YAMYAM:
6373 RaiseScoreElement(element);
6376 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6377 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6378 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6379 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6380 case EL_DYNABOMB_INCREASE_NUMBER:
6381 case EL_DYNABOMB_INCREASE_SIZE:
6382 case EL_DYNABOMB_INCREASE_POWER:
6383 explosion_type = EX_TYPE_DYNA;
6386 case EL_DC_LANDMINE:
6388 case EL_EM_EXIT_OPEN:
6389 case EL_EM_STEEL_EXIT_OPEN:
6391 explosion_type = EX_TYPE_CENTER;
6396 case EL_LAMP_ACTIVE:
6397 case EL_AMOEBA_TO_DIAMOND:
6398 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6399 explosion_type = EX_TYPE_CENTER;
6403 if (element_info[element].explosion_type == EXPLODES_CROSS)
6404 explosion_type = EX_TYPE_CROSS;
6405 else if (element_info[element].explosion_type == EXPLODES_1X1)
6406 explosion_type = EX_TYPE_CENTER;
6410 if (explosion_type == EX_TYPE_DYNA)
6413 Explode(x, y, EX_PHASE_START, explosion_type);
6415 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6418 void SplashAcid(int x, int y)
6420 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6421 (!IN_LEV_FIELD(x - 1, y - 2) ||
6422 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6423 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6425 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6426 (!IN_LEV_FIELD(x + 1, y - 2) ||
6427 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6428 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6430 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6433 static void InitBeltMovement()
6435 static int belt_base_element[4] =
6437 EL_CONVEYOR_BELT_1_LEFT,
6438 EL_CONVEYOR_BELT_2_LEFT,
6439 EL_CONVEYOR_BELT_3_LEFT,
6440 EL_CONVEYOR_BELT_4_LEFT
6442 static int belt_base_active_element[4] =
6444 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6445 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6446 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6447 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6452 /* set frame order for belt animation graphic according to belt direction */
6453 for (i = 0; i < NUM_BELTS; i++)
6457 for (j = 0; j < NUM_BELT_PARTS; j++)
6459 int element = belt_base_active_element[belt_nr] + j;
6460 int graphic_1 = el2img(element);
6461 int graphic_2 = el2panelimg(element);
6463 if (game.belt_dir[i] == MV_LEFT)
6465 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6466 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6470 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6471 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6476 SCAN_PLAYFIELD(x, y)
6478 int element = Feld[x][y];
6480 for (i = 0; i < NUM_BELTS; i++)
6482 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6484 int e_belt_nr = getBeltNrFromBeltElement(element);
6487 if (e_belt_nr == belt_nr)
6489 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6491 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6498 static void ToggleBeltSwitch(int x, int y)
6500 static int belt_base_element[4] =
6502 EL_CONVEYOR_BELT_1_LEFT,
6503 EL_CONVEYOR_BELT_2_LEFT,
6504 EL_CONVEYOR_BELT_3_LEFT,
6505 EL_CONVEYOR_BELT_4_LEFT
6507 static int belt_base_active_element[4] =
6509 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6510 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6511 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6512 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6514 static int belt_base_switch_element[4] =
6516 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6517 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6518 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6519 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6521 static int belt_move_dir[4] =
6529 int element = Feld[x][y];
6530 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6531 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6532 int belt_dir = belt_move_dir[belt_dir_nr];
6535 if (!IS_BELT_SWITCH(element))
6538 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6539 game.belt_dir[belt_nr] = belt_dir;
6541 if (belt_dir_nr == 3)
6544 /* set frame order for belt animation graphic according to belt direction */
6545 for (i = 0; i < NUM_BELT_PARTS; i++)
6547 int element = belt_base_active_element[belt_nr] + i;
6548 int graphic_1 = el2img(element);
6549 int graphic_2 = el2panelimg(element);
6551 if (belt_dir == MV_LEFT)
6553 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6554 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6558 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6559 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6563 SCAN_PLAYFIELD(xx, yy)
6565 int element = Feld[xx][yy];
6567 if (IS_BELT_SWITCH(element))
6569 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6571 if (e_belt_nr == belt_nr)
6573 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6574 TEST_DrawLevelField(xx, yy);
6577 else if (IS_BELT(element) && belt_dir != MV_NONE)
6579 int e_belt_nr = getBeltNrFromBeltElement(element);
6581 if (e_belt_nr == belt_nr)
6583 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6585 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6586 TEST_DrawLevelField(xx, yy);
6589 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6591 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6593 if (e_belt_nr == belt_nr)
6595 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6597 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6598 TEST_DrawLevelField(xx, yy);
6604 static void ToggleSwitchgateSwitch(int x, int y)
6608 game.switchgate_pos = !game.switchgate_pos;
6610 SCAN_PLAYFIELD(xx, yy)
6612 int element = Feld[xx][yy];
6614 #if !USE_BOTH_SWITCHGATE_SWITCHES
6615 if (element == EL_SWITCHGATE_SWITCH_UP ||
6616 element == EL_SWITCHGATE_SWITCH_DOWN)
6618 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6619 TEST_DrawLevelField(xx, yy);
6621 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6622 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6624 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6625 TEST_DrawLevelField(xx, yy);
6628 if (element == EL_SWITCHGATE_SWITCH_UP)
6630 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6631 TEST_DrawLevelField(xx, yy);
6633 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6635 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6636 TEST_DrawLevelField(xx, yy);
6638 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6640 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6641 TEST_DrawLevelField(xx, yy);
6643 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6645 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6646 TEST_DrawLevelField(xx, yy);
6649 else if (element == EL_SWITCHGATE_OPEN ||
6650 element == EL_SWITCHGATE_OPENING)
6652 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6654 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6656 else if (element == EL_SWITCHGATE_CLOSED ||
6657 element == EL_SWITCHGATE_CLOSING)
6659 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6661 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6666 static int getInvisibleActiveFromInvisibleElement(int element)
6668 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6669 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6670 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6674 static int getInvisibleFromInvisibleActiveElement(int element)
6676 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6677 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6678 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6682 static void RedrawAllLightSwitchesAndInvisibleElements()
6686 SCAN_PLAYFIELD(x, y)
6688 int element = Feld[x][y];
6690 if (element == EL_LIGHT_SWITCH &&
6691 game.light_time_left > 0)
6693 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6694 TEST_DrawLevelField(x, y);
6696 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6697 game.light_time_left == 0)
6699 Feld[x][y] = EL_LIGHT_SWITCH;
6700 TEST_DrawLevelField(x, y);
6702 else if (element == EL_EMC_DRIPPER &&
6703 game.light_time_left > 0)
6705 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6706 TEST_DrawLevelField(x, y);
6708 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6709 game.light_time_left == 0)
6711 Feld[x][y] = EL_EMC_DRIPPER;
6712 TEST_DrawLevelField(x, y);
6714 else if (element == EL_INVISIBLE_STEELWALL ||
6715 element == EL_INVISIBLE_WALL ||
6716 element == EL_INVISIBLE_SAND)
6718 if (game.light_time_left > 0)
6719 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6721 TEST_DrawLevelField(x, y);
6723 /* uncrumble neighbour fields, if needed */
6724 if (element == EL_INVISIBLE_SAND)
6725 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6727 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6728 element == EL_INVISIBLE_WALL_ACTIVE ||
6729 element == EL_INVISIBLE_SAND_ACTIVE)
6731 if (game.light_time_left == 0)
6732 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6734 TEST_DrawLevelField(x, y);
6736 /* re-crumble neighbour fields, if needed */
6737 if (element == EL_INVISIBLE_SAND)
6738 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6743 static void RedrawAllInvisibleElementsForLenses()
6747 SCAN_PLAYFIELD(x, y)
6749 int element = Feld[x][y];
6751 if (element == EL_EMC_DRIPPER &&
6752 game.lenses_time_left > 0)
6754 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6755 TEST_DrawLevelField(x, y);
6757 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6758 game.lenses_time_left == 0)
6760 Feld[x][y] = EL_EMC_DRIPPER;
6761 TEST_DrawLevelField(x, y);
6763 else if (element == EL_INVISIBLE_STEELWALL ||
6764 element == EL_INVISIBLE_WALL ||
6765 element == EL_INVISIBLE_SAND)
6767 if (game.lenses_time_left > 0)
6768 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6770 TEST_DrawLevelField(x, y);
6772 /* uncrumble neighbour fields, if needed */
6773 if (element == EL_INVISIBLE_SAND)
6774 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6776 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6777 element == EL_INVISIBLE_WALL_ACTIVE ||
6778 element == EL_INVISIBLE_SAND_ACTIVE)
6780 if (game.lenses_time_left == 0)
6781 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6783 TEST_DrawLevelField(x, y);
6785 /* re-crumble neighbour fields, if needed */
6786 if (element == EL_INVISIBLE_SAND)
6787 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6792 static void RedrawAllInvisibleElementsForMagnifier()
6796 SCAN_PLAYFIELD(x, y)
6798 int element = Feld[x][y];
6800 if (element == EL_EMC_FAKE_GRASS &&
6801 game.magnify_time_left > 0)
6803 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6804 TEST_DrawLevelField(x, y);
6806 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6807 game.magnify_time_left == 0)
6809 Feld[x][y] = EL_EMC_FAKE_GRASS;
6810 TEST_DrawLevelField(x, y);
6812 else if (IS_GATE_GRAY(element) &&
6813 game.magnify_time_left > 0)
6815 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6816 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6817 IS_EM_GATE_GRAY(element) ?
6818 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6819 IS_EMC_GATE_GRAY(element) ?
6820 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6821 IS_DC_GATE_GRAY(element) ?
6822 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6824 TEST_DrawLevelField(x, y);
6826 else if (IS_GATE_GRAY_ACTIVE(element) &&
6827 game.magnify_time_left == 0)
6829 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6830 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6831 IS_EM_GATE_GRAY_ACTIVE(element) ?
6832 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6833 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6834 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6835 IS_DC_GATE_GRAY_ACTIVE(element) ?
6836 EL_DC_GATE_WHITE_GRAY :
6838 TEST_DrawLevelField(x, y);
6843 static void ToggleLightSwitch(int x, int y)
6845 int element = Feld[x][y];
6847 game.light_time_left =
6848 (element == EL_LIGHT_SWITCH ?
6849 level.time_light * FRAMES_PER_SECOND : 0);
6851 RedrawAllLightSwitchesAndInvisibleElements();
6854 static void ActivateTimegateSwitch(int x, int y)
6858 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6860 SCAN_PLAYFIELD(xx, yy)
6862 int element = Feld[xx][yy];
6864 if (element == EL_TIMEGATE_CLOSED ||
6865 element == EL_TIMEGATE_CLOSING)
6867 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6868 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6872 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6874 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6875 TEST_DrawLevelField(xx, yy);
6882 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6883 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6885 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6889 void Impact(int x, int y)
6891 boolean last_line = (y == lev_fieldy - 1);
6892 boolean object_hit = FALSE;
6893 boolean impact = (last_line || object_hit);
6894 int element = Feld[x][y];
6895 int smashed = EL_STEELWALL;
6897 if (!last_line) /* check if element below was hit */
6899 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6902 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6903 MovDir[x][y + 1] != MV_DOWN ||
6904 MovPos[x][y + 1] <= TILEY / 2));
6906 /* do not smash moving elements that left the smashed field in time */
6907 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6908 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6911 #if USE_QUICKSAND_IMPACT_BUGFIX
6912 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6914 RemoveMovingField(x, y + 1);
6915 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6916 Feld[x][y + 2] = EL_ROCK;
6917 TEST_DrawLevelField(x, y + 2);
6922 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6924 RemoveMovingField(x, y + 1);
6925 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6926 Feld[x][y + 2] = EL_ROCK;
6927 TEST_DrawLevelField(x, y + 2);
6934 smashed = MovingOrBlocked2Element(x, y + 1);
6936 impact = (last_line || object_hit);
6939 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6941 SplashAcid(x, y + 1);
6945 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6946 /* only reset graphic animation if graphic really changes after impact */
6948 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6950 ResetGfxAnimation(x, y);
6951 TEST_DrawLevelField(x, y);
6954 if (impact && CAN_EXPLODE_IMPACT(element))
6959 else if (impact && element == EL_PEARL &&
6960 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6962 ResetGfxAnimation(x, y);
6964 Feld[x][y] = EL_PEARL_BREAKING;
6965 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6968 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6970 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6975 if (impact && element == EL_AMOEBA_DROP)
6977 if (object_hit && IS_PLAYER(x, y + 1))
6978 KillPlayerUnlessEnemyProtected(x, y + 1);
6979 else if (object_hit && smashed == EL_PENGUIN)
6983 Feld[x][y] = EL_AMOEBA_GROWING;
6984 Store[x][y] = EL_AMOEBA_WET;
6986 ResetRandomAnimationValue(x, y);
6991 if (object_hit) /* check which object was hit */
6993 if ((CAN_PASS_MAGIC_WALL(element) &&
6994 (smashed == EL_MAGIC_WALL ||
6995 smashed == EL_BD_MAGIC_WALL)) ||
6996 (CAN_PASS_DC_MAGIC_WALL(element) &&
6997 smashed == EL_DC_MAGIC_WALL))
7000 int activated_magic_wall =
7001 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7002 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7003 EL_DC_MAGIC_WALL_ACTIVE);
7005 /* activate magic wall / mill */
7006 SCAN_PLAYFIELD(xx, yy)
7008 if (Feld[xx][yy] == smashed)
7009 Feld[xx][yy] = activated_magic_wall;
7012 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7013 game.magic_wall_active = TRUE;
7015 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7016 SND_MAGIC_WALL_ACTIVATING :
7017 smashed == EL_BD_MAGIC_WALL ?
7018 SND_BD_MAGIC_WALL_ACTIVATING :
7019 SND_DC_MAGIC_WALL_ACTIVATING));
7022 if (IS_PLAYER(x, y + 1))
7024 if (CAN_SMASH_PLAYER(element))
7026 KillPlayerUnlessEnemyProtected(x, y + 1);
7030 else if (smashed == EL_PENGUIN)
7032 if (CAN_SMASH_PLAYER(element))
7038 else if (element == EL_BD_DIAMOND)
7040 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7046 else if (((element == EL_SP_INFOTRON ||
7047 element == EL_SP_ZONK) &&
7048 (smashed == EL_SP_SNIKSNAK ||
7049 smashed == EL_SP_ELECTRON ||
7050 smashed == EL_SP_DISK_ORANGE)) ||
7051 (element == EL_SP_INFOTRON &&
7052 smashed == EL_SP_DISK_YELLOW))
7057 else if (CAN_SMASH_EVERYTHING(element))
7059 if (IS_CLASSIC_ENEMY(smashed) ||
7060 CAN_EXPLODE_SMASHED(smashed))
7065 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7067 if (smashed == EL_LAMP ||
7068 smashed == EL_LAMP_ACTIVE)
7073 else if (smashed == EL_NUT)
7075 Feld[x][y + 1] = EL_NUT_BREAKING;
7076 PlayLevelSound(x, y, SND_NUT_BREAKING);
7077 RaiseScoreElement(EL_NUT);
7080 else if (smashed == EL_PEARL)
7082 ResetGfxAnimation(x, y);
7084 Feld[x][y + 1] = EL_PEARL_BREAKING;
7085 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7088 else if (smashed == EL_DIAMOND)
7090 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7091 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7094 else if (IS_BELT_SWITCH(smashed))
7096 ToggleBeltSwitch(x, y + 1);
7098 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7099 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7100 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7101 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7103 ToggleSwitchgateSwitch(x, y + 1);
7105 else if (smashed == EL_LIGHT_SWITCH ||
7106 smashed == EL_LIGHT_SWITCH_ACTIVE)
7108 ToggleLightSwitch(x, y + 1);
7113 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7116 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7118 CheckElementChangeBySide(x, y + 1, smashed, element,
7119 CE_SWITCHED, CH_SIDE_TOP);
7120 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7126 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7131 /* play sound of magic wall / mill */
7133 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7134 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7135 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7137 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7138 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7139 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7140 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7141 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7142 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7147 /* play sound of object that hits the ground */
7148 if (last_line || object_hit)
7149 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7152 inline static void TurnRoundExt(int x, int y)
7164 { 0, 0 }, { 0, 0 }, { 0, 0 },
7169 int left, right, back;
7173 { MV_DOWN, MV_UP, MV_RIGHT },
7174 { MV_UP, MV_DOWN, MV_LEFT },
7176 { MV_LEFT, MV_RIGHT, MV_DOWN },
7180 { MV_RIGHT, MV_LEFT, MV_UP }
7183 int element = Feld[x][y];
7184 int move_pattern = element_info[element].move_pattern;
7186 int old_move_dir = MovDir[x][y];
7187 int left_dir = turn[old_move_dir].left;
7188 int right_dir = turn[old_move_dir].right;
7189 int back_dir = turn[old_move_dir].back;
7191 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7192 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7193 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7194 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7196 int left_x = x + left_dx, left_y = y + left_dy;
7197 int right_x = x + right_dx, right_y = y + right_dy;
7198 int move_x = x + move_dx, move_y = y + move_dy;
7202 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7204 TestIfBadThingTouchesOtherBadThing(x, y);
7206 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7207 MovDir[x][y] = right_dir;
7208 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7209 MovDir[x][y] = left_dir;
7211 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7213 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7216 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7218 TestIfBadThingTouchesOtherBadThing(x, y);
7220 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7221 MovDir[x][y] = left_dir;
7222 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7223 MovDir[x][y] = right_dir;
7225 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7227 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7230 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7232 TestIfBadThingTouchesOtherBadThing(x, y);
7234 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7235 MovDir[x][y] = left_dir;
7236 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7237 MovDir[x][y] = right_dir;
7239 if (MovDir[x][y] != old_move_dir)
7242 else if (element == EL_YAMYAM)
7244 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7245 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7247 if (can_turn_left && can_turn_right)
7248 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7249 else if (can_turn_left)
7250 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7251 else if (can_turn_right)
7252 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7254 MovDir[x][y] = back_dir;
7256 MovDelay[x][y] = 16 + 16 * RND(3);
7258 else if (element == EL_DARK_YAMYAM)
7260 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7262 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7265 if (can_turn_left && can_turn_right)
7266 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7267 else if (can_turn_left)
7268 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7269 else if (can_turn_right)
7270 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7272 MovDir[x][y] = back_dir;
7274 MovDelay[x][y] = 16 + 16 * RND(3);
7276 else if (element == EL_PACMAN)
7278 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7279 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7281 if (can_turn_left && can_turn_right)
7282 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283 else if (can_turn_left)
7284 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285 else if (can_turn_right)
7286 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7288 MovDir[x][y] = back_dir;
7290 MovDelay[x][y] = 6 + RND(40);
7292 else if (element == EL_PIG)
7294 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7295 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7296 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7297 boolean should_turn_left, should_turn_right, should_move_on;
7299 int rnd = RND(rnd_value);
7301 should_turn_left = (can_turn_left &&
7303 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7304 y + back_dy + left_dy)));
7305 should_turn_right = (can_turn_right &&
7307 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7308 y + back_dy + right_dy)));
7309 should_move_on = (can_move_on &&
7312 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7313 y + move_dy + left_dy) ||
7314 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7315 y + move_dy + right_dy)));
7317 if (should_turn_left || should_turn_right || should_move_on)
7319 if (should_turn_left && should_turn_right && should_move_on)
7320 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7321 rnd < 2 * rnd_value / 3 ? right_dir :
7323 else if (should_turn_left && should_turn_right)
7324 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325 else if (should_turn_left && should_move_on)
7326 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7327 else if (should_turn_right && should_move_on)
7328 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7329 else if (should_turn_left)
7330 MovDir[x][y] = left_dir;
7331 else if (should_turn_right)
7332 MovDir[x][y] = right_dir;
7333 else if (should_move_on)
7334 MovDir[x][y] = old_move_dir;
7336 else if (can_move_on && rnd > rnd_value / 8)
7337 MovDir[x][y] = old_move_dir;
7338 else if (can_turn_left && can_turn_right)
7339 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7340 else if (can_turn_left && rnd > rnd_value / 8)
7341 MovDir[x][y] = left_dir;
7342 else if (can_turn_right && rnd > rnd_value/8)
7343 MovDir[x][y] = right_dir;
7345 MovDir[x][y] = back_dir;
7347 xx = x + move_xy[MovDir[x][y]].dx;
7348 yy = y + move_xy[MovDir[x][y]].dy;
7350 if (!IN_LEV_FIELD(xx, yy) ||
7351 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7352 MovDir[x][y] = old_move_dir;
7356 else if (element == EL_DRAGON)
7358 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7359 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7360 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7362 int rnd = RND(rnd_value);
7364 if (can_move_on && rnd > rnd_value / 8)
7365 MovDir[x][y] = old_move_dir;
7366 else if (can_turn_left && can_turn_right)
7367 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7368 else if (can_turn_left && rnd > rnd_value / 8)
7369 MovDir[x][y] = left_dir;
7370 else if (can_turn_right && rnd > rnd_value / 8)
7371 MovDir[x][y] = right_dir;
7373 MovDir[x][y] = back_dir;
7375 xx = x + move_xy[MovDir[x][y]].dx;
7376 yy = y + move_xy[MovDir[x][y]].dy;
7378 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7379 MovDir[x][y] = old_move_dir;
7383 else if (element == EL_MOLE)
7385 boolean can_move_on =
7386 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7387 IS_AMOEBOID(Feld[move_x][move_y]) ||
7388 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7391 boolean can_turn_left =
7392 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7393 IS_AMOEBOID(Feld[left_x][left_y])));
7395 boolean can_turn_right =
7396 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7397 IS_AMOEBOID(Feld[right_x][right_y])));
7399 if (can_turn_left && can_turn_right)
7400 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7401 else if (can_turn_left)
7402 MovDir[x][y] = left_dir;
7404 MovDir[x][y] = right_dir;
7407 if (MovDir[x][y] != old_move_dir)
7410 else if (element == EL_BALLOON)
7412 MovDir[x][y] = game.wind_direction;
7415 else if (element == EL_SPRING)
7417 #if USE_NEW_SPRING_BUMPER
7418 if (MovDir[x][y] & MV_HORIZONTAL)
7420 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7421 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7423 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7424 ResetGfxAnimation(move_x, move_y);
7425 TEST_DrawLevelField(move_x, move_y);
7427 MovDir[x][y] = back_dir;
7429 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7430 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7431 MovDir[x][y] = MV_NONE;
7434 if (MovDir[x][y] & MV_HORIZONTAL &&
7435 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7436 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7437 MovDir[x][y] = MV_NONE;
7442 else if (element == EL_ROBOT ||
7443 element == EL_SATELLITE ||
7444 element == EL_PENGUIN ||
7445 element == EL_EMC_ANDROID)
7447 int attr_x = -1, attr_y = -1;
7458 for (i = 0; i < MAX_PLAYERS; i++)
7460 struct PlayerInfo *player = &stored_player[i];
7461 int jx = player->jx, jy = player->jy;
7463 if (!player->active)
7467 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7475 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7476 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7477 game.engine_version < VERSION_IDENT(3,1,0,0)))
7483 if (element == EL_PENGUIN)
7486 static int xy[4][2] =
7494 for (i = 0; i < NUM_DIRECTIONS; i++)
7496 int ex = x + xy[i][0];
7497 int ey = y + xy[i][1];
7499 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7500 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7501 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7502 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7511 MovDir[x][y] = MV_NONE;
7513 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7514 else if (attr_x > x)
7515 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7517 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7518 else if (attr_y > y)
7519 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7521 if (element == EL_ROBOT)
7525 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7527 Moving2Blocked(x, y, &newx, &newy);
7529 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7530 MovDelay[x][y] = 8 + 8 * !RND(3);
7532 MovDelay[x][y] = 16;
7534 else if (element == EL_PENGUIN)
7540 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7542 boolean first_horiz = RND(2);
7543 int new_move_dir = MovDir[x][y];
7546 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7547 Moving2Blocked(x, y, &newx, &newy);
7549 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7553 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7554 Moving2Blocked(x, y, &newx, &newy);
7556 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7559 MovDir[x][y] = old_move_dir;
7563 else if (element == EL_SATELLITE)
7569 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7571 boolean first_horiz = RND(2);
7572 int new_move_dir = MovDir[x][y];
7575 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7576 Moving2Blocked(x, y, &newx, &newy);
7578 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7582 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7583 Moving2Blocked(x, y, &newx, &newy);
7585 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7588 MovDir[x][y] = old_move_dir;
7592 else if (element == EL_EMC_ANDROID)
7594 static int check_pos[16] =
7596 -1, /* 0 => (invalid) */
7597 7, /* 1 => MV_LEFT */
7598 3, /* 2 => MV_RIGHT */
7599 -1, /* 3 => (invalid) */
7601 0, /* 5 => MV_LEFT | MV_UP */
7602 2, /* 6 => MV_RIGHT | MV_UP */
7603 -1, /* 7 => (invalid) */
7604 5, /* 8 => MV_DOWN */
7605 6, /* 9 => MV_LEFT | MV_DOWN */
7606 4, /* 10 => MV_RIGHT | MV_DOWN */
7607 -1, /* 11 => (invalid) */
7608 -1, /* 12 => (invalid) */
7609 -1, /* 13 => (invalid) */
7610 -1, /* 14 => (invalid) */
7611 -1, /* 15 => (invalid) */
7619 { -1, -1, MV_LEFT | MV_UP },
7621 { +1, -1, MV_RIGHT | MV_UP },
7622 { +1, 0, MV_RIGHT },
7623 { +1, +1, MV_RIGHT | MV_DOWN },
7625 { -1, +1, MV_LEFT | MV_DOWN },
7628 int start_pos, check_order;
7629 boolean can_clone = FALSE;
7632 /* check if there is any free field around current position */
7633 for (i = 0; i < 8; i++)
7635 int newx = x + check_xy[i].dx;
7636 int newy = y + check_xy[i].dy;
7638 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7646 if (can_clone) /* randomly find an element to clone */
7650 start_pos = check_pos[RND(8)];
7651 check_order = (RND(2) ? -1 : +1);
7653 for (i = 0; i < 8; i++)
7655 int pos_raw = start_pos + i * check_order;
7656 int pos = (pos_raw + 8) % 8;
7657 int newx = x + check_xy[pos].dx;
7658 int newy = y + check_xy[pos].dy;
7660 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7662 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7663 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7665 Store[x][y] = Feld[newx][newy];
7674 if (can_clone) /* randomly find a direction to move */
7678 start_pos = check_pos[RND(8)];
7679 check_order = (RND(2) ? -1 : +1);
7681 for (i = 0; i < 8; i++)
7683 int pos_raw = start_pos + i * check_order;
7684 int pos = (pos_raw + 8) % 8;
7685 int newx = x + check_xy[pos].dx;
7686 int newy = y + check_xy[pos].dy;
7687 int new_move_dir = check_xy[pos].dir;
7689 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7691 MovDir[x][y] = new_move_dir;
7692 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7701 if (can_clone) /* cloning and moving successful */
7704 /* cannot clone -- try to move towards player */
7706 start_pos = check_pos[MovDir[x][y] & 0x0f];
7707 check_order = (RND(2) ? -1 : +1);
7709 for (i = 0; i < 3; i++)
7711 /* first check start_pos, then previous/next or (next/previous) pos */
7712 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7713 int pos = (pos_raw + 8) % 8;
7714 int newx = x + check_xy[pos].dx;
7715 int newy = y + check_xy[pos].dy;
7716 int new_move_dir = check_xy[pos].dir;
7718 if (IS_PLAYER(newx, newy))
7721 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7723 MovDir[x][y] = new_move_dir;
7724 MovDelay[x][y] = level.android_move_time * 8 + 1;
7731 else if (move_pattern == MV_TURNING_LEFT ||
7732 move_pattern == MV_TURNING_RIGHT ||
7733 move_pattern == MV_TURNING_LEFT_RIGHT ||
7734 move_pattern == MV_TURNING_RIGHT_LEFT ||
7735 move_pattern == MV_TURNING_RANDOM ||
7736 move_pattern == MV_ALL_DIRECTIONS)
7738 boolean can_turn_left =
7739 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7740 boolean can_turn_right =
7741 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7743 if (element_info[element].move_stepsize == 0) /* "not moving" */
7746 if (move_pattern == MV_TURNING_LEFT)
7747 MovDir[x][y] = left_dir;
7748 else if (move_pattern == MV_TURNING_RIGHT)
7749 MovDir[x][y] = right_dir;
7750 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7751 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7752 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7753 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7754 else if (move_pattern == MV_TURNING_RANDOM)
7755 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7756 can_turn_right && !can_turn_left ? right_dir :
7757 RND(2) ? left_dir : right_dir);
7758 else if (can_turn_left && can_turn_right)
7759 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7760 else if (can_turn_left)
7761 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7762 else if (can_turn_right)
7763 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7765 MovDir[x][y] = back_dir;
7767 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7769 else if (move_pattern == MV_HORIZONTAL ||
7770 move_pattern == MV_VERTICAL)
7772 if (move_pattern & old_move_dir)
7773 MovDir[x][y] = back_dir;
7774 else if (move_pattern == MV_HORIZONTAL)
7775 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7776 else if (move_pattern == MV_VERTICAL)
7777 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7779 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7781 else if (move_pattern & MV_ANY_DIRECTION)
7783 MovDir[x][y] = move_pattern;
7784 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7786 else if (move_pattern & MV_WIND_DIRECTION)
7788 MovDir[x][y] = game.wind_direction;
7789 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7791 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7793 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7794 MovDir[x][y] = left_dir;
7795 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796 MovDir[x][y] = right_dir;
7798 if (MovDir[x][y] != old_move_dir)
7799 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7801 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7803 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7804 MovDir[x][y] = right_dir;
7805 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7806 MovDir[x][y] = left_dir;
7808 if (MovDir[x][y] != old_move_dir)
7809 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7811 else if (move_pattern == MV_TOWARDS_PLAYER ||
7812 move_pattern == MV_AWAY_FROM_PLAYER)
7814 int attr_x = -1, attr_y = -1;
7816 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7827 for (i = 0; i < MAX_PLAYERS; i++)
7829 struct PlayerInfo *player = &stored_player[i];
7830 int jx = player->jx, jy = player->jy;
7832 if (!player->active)
7836 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7844 MovDir[x][y] = MV_NONE;
7846 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7847 else if (attr_x > x)
7848 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7850 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7851 else if (attr_y > y)
7852 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7854 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7856 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7858 boolean first_horiz = RND(2);
7859 int new_move_dir = MovDir[x][y];
7861 if (element_info[element].move_stepsize == 0) /* "not moving" */
7863 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7864 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7870 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7871 Moving2Blocked(x, y, &newx, &newy);
7873 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7877 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7878 Moving2Blocked(x, y, &newx, &newy);
7880 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7883 MovDir[x][y] = old_move_dir;
7886 else if (move_pattern == MV_WHEN_PUSHED ||
7887 move_pattern == MV_WHEN_DROPPED)
7889 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7890 MovDir[x][y] = MV_NONE;
7894 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7896 static int test_xy[7][2] =
7906 static int test_dir[7] =
7916 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7917 int move_preference = -1000000; /* start with very low preference */
7918 int new_move_dir = MV_NONE;
7919 int start_test = RND(4);
7922 for (i = 0; i < NUM_DIRECTIONS; i++)
7924 int move_dir = test_dir[start_test + i];
7925 int move_dir_preference;
7927 xx = x + test_xy[start_test + i][0];
7928 yy = y + test_xy[start_test + i][1];
7930 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7931 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7933 new_move_dir = move_dir;
7938 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7941 move_dir_preference = -1 * RunnerVisit[xx][yy];
7942 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7943 move_dir_preference = PlayerVisit[xx][yy];
7945 if (move_dir_preference > move_preference)
7947 /* prefer field that has not been visited for the longest time */
7948 move_preference = move_dir_preference;
7949 new_move_dir = move_dir;
7951 else if (move_dir_preference == move_preference &&
7952 move_dir == old_move_dir)
7954 /* prefer last direction when all directions are preferred equally */
7955 move_preference = move_dir_preference;
7956 new_move_dir = move_dir;
7960 MovDir[x][y] = new_move_dir;
7961 if (old_move_dir != new_move_dir)
7962 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7966 static void TurnRound(int x, int y)
7968 int direction = MovDir[x][y];
7972 GfxDir[x][y] = MovDir[x][y];
7974 if (direction != MovDir[x][y])
7978 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7980 ResetGfxFrame(x, y, FALSE);
7983 static boolean JustBeingPushed(int x, int y)
7987 for (i = 0; i < MAX_PLAYERS; i++)
7989 struct PlayerInfo *player = &stored_player[i];
7991 if (player->active && player->is_pushing && player->MovPos)
7993 int next_jx = player->jx + (player->jx - player->last_jx);
7994 int next_jy = player->jy + (player->jy - player->last_jy);
7996 if (x == next_jx && y == next_jy)
8004 void StartMoving(int x, int y)
8006 boolean started_moving = FALSE; /* some elements can fall _and_ move */
8007 int element = Feld[x][y];
8012 if (MovDelay[x][y] == 0)
8013 GfxAction[x][y] = ACTION_DEFAULT;
8015 if (CAN_FALL(element) && y < lev_fieldy - 1)
8017 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
8018 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8019 if (JustBeingPushed(x, y))
8022 if (element == EL_QUICKSAND_FULL)
8024 if (IS_FREE(x, y + 1))
8026 InitMovingField(x, y, MV_DOWN);
8027 started_moving = TRUE;
8029 Feld[x][y] = EL_QUICKSAND_EMPTYING;
8030 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8031 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8032 Store[x][y] = EL_ROCK;
8034 Store[x][y] = EL_ROCK;
8037 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8039 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8041 if (!MovDelay[x][y])
8043 MovDelay[x][y] = TILEY + 1;
8045 ResetGfxAnimation(x, y);
8046 ResetGfxAnimation(x, y + 1);
8051 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8052 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8059 Feld[x][y] = EL_QUICKSAND_EMPTY;
8060 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8061 Store[x][y + 1] = Store[x][y];
8064 PlayLevelSoundAction(x, y, ACTION_FILLING);
8066 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8068 if (!MovDelay[x][y])
8070 MovDelay[x][y] = TILEY + 1;
8072 ResetGfxAnimation(x, y);
8073 ResetGfxAnimation(x, y + 1);
8078 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8079 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8086 Feld[x][y] = EL_QUICKSAND_EMPTY;
8087 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088 Store[x][y + 1] = Store[x][y];
8091 PlayLevelSoundAction(x, y, ACTION_FILLING);
8094 else if (element == EL_QUICKSAND_FAST_FULL)
8096 if (IS_FREE(x, y + 1))
8098 InitMovingField(x, y, MV_DOWN);
8099 started_moving = TRUE;
8101 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8102 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8103 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8104 Store[x][y] = EL_ROCK;
8106 Store[x][y] = EL_ROCK;
8109 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8111 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8113 if (!MovDelay[x][y])
8115 MovDelay[x][y] = TILEY + 1;
8117 ResetGfxAnimation(x, y);
8118 ResetGfxAnimation(x, y + 1);
8123 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8124 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8131 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8132 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8133 Store[x][y + 1] = Store[x][y];
8136 PlayLevelSoundAction(x, y, ACTION_FILLING);
8138 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8140 if (!MovDelay[x][y])
8142 MovDelay[x][y] = TILEY + 1;
8144 ResetGfxAnimation(x, y);
8145 ResetGfxAnimation(x, y + 1);
8150 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8151 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8158 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8159 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8160 Store[x][y + 1] = Store[x][y];
8163 PlayLevelSoundAction(x, y, ACTION_FILLING);
8166 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8167 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8169 InitMovingField(x, y, MV_DOWN);
8170 started_moving = TRUE;
8172 Feld[x][y] = EL_QUICKSAND_FILLING;
8173 Store[x][y] = element;
8175 PlayLevelSoundAction(x, y, ACTION_FILLING);
8177 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8178 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8180 InitMovingField(x, y, MV_DOWN);
8181 started_moving = TRUE;
8183 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8184 Store[x][y] = element;
8186 PlayLevelSoundAction(x, y, ACTION_FILLING);
8188 else if (element == EL_MAGIC_WALL_FULL)
8190 if (IS_FREE(x, y + 1))
8192 InitMovingField(x, y, MV_DOWN);
8193 started_moving = TRUE;
8195 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8196 Store[x][y] = EL_CHANGED(Store[x][y]);
8198 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8200 if (!MovDelay[x][y])
8201 MovDelay[x][y] = TILEY / 4 + 1;
8210 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8211 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8212 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8216 else if (element == EL_BD_MAGIC_WALL_FULL)
8218 if (IS_FREE(x, y + 1))
8220 InitMovingField(x, y, MV_DOWN);
8221 started_moving = TRUE;
8223 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8224 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8226 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8228 if (!MovDelay[x][y])
8229 MovDelay[x][y] = TILEY / 4 + 1;
8238 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8239 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8240 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8244 else if (element == EL_DC_MAGIC_WALL_FULL)
8246 if (IS_FREE(x, y + 1))
8248 InitMovingField(x, y, MV_DOWN);
8249 started_moving = TRUE;
8251 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8252 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8254 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8256 if (!MovDelay[x][y])
8257 MovDelay[x][y] = TILEY / 4 + 1;
8266 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8267 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8268 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8272 else if ((CAN_PASS_MAGIC_WALL(element) &&
8273 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8274 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8275 (CAN_PASS_DC_MAGIC_WALL(element) &&
8276 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8279 InitMovingField(x, y, MV_DOWN);
8280 started_moving = TRUE;
8283 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8284 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8285 EL_DC_MAGIC_WALL_FILLING);
8286 Store[x][y] = element;
8288 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8290 SplashAcid(x, y + 1);
8292 InitMovingField(x, y, MV_DOWN);
8293 started_moving = TRUE;
8295 Store[x][y] = EL_ACID;
8298 #if USE_FIX_IMPACT_COLLISION
8299 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8300 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8302 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8303 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8305 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8306 CAN_FALL(element) && WasJustFalling[x][y] &&
8307 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8309 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8310 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8311 (Feld[x][y + 1] == EL_BLOCKED)))
8313 /* this is needed for a special case not covered by calling "Impact()"
8314 from "ContinueMoving()": if an element moves to a tile directly below
8315 another element which was just falling on that tile (which was empty
8316 in the previous frame), the falling element above would just stop
8317 instead of smashing the element below (in previous version, the above
8318 element was just checked for "moving" instead of "falling", resulting
8319 in incorrect smashes caused by horizontal movement of the above
8320 element; also, the case of the player being the element to smash was
8321 simply not covered here... :-/ ) */
8323 CheckCollision[x][y] = 0;
8324 CheckImpact[x][y] = 0;
8328 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8330 if (MovDir[x][y] == MV_NONE)
8332 InitMovingField(x, y, MV_DOWN);
8333 started_moving = TRUE;
8336 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8338 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8339 MovDir[x][y] = MV_DOWN;
8341 InitMovingField(x, y, MV_DOWN);
8342 started_moving = TRUE;
8344 else if (element == EL_AMOEBA_DROP)
8346 Feld[x][y] = EL_AMOEBA_GROWING;
8347 Store[x][y] = EL_AMOEBA_WET;
8349 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8350 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8351 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8352 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8354 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8355 (IS_FREE(x - 1, y + 1) ||
8356 Feld[x - 1][y + 1] == EL_ACID));
8357 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8358 (IS_FREE(x + 1, y + 1) ||
8359 Feld[x + 1][y + 1] == EL_ACID));
8360 boolean can_fall_any = (can_fall_left || can_fall_right);
8361 boolean can_fall_both = (can_fall_left && can_fall_right);
8362 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8364 #if USE_NEW_ALL_SLIPPERY
8365 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8367 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8368 can_fall_right = FALSE;
8369 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8370 can_fall_left = FALSE;
8371 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8372 can_fall_right = FALSE;
8373 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8374 can_fall_left = FALSE;
8376 can_fall_any = (can_fall_left || can_fall_right);
8377 can_fall_both = FALSE;
8380 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8382 if (slippery_type == SLIPPERY_ONLY_LEFT)
8383 can_fall_right = FALSE;
8384 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8385 can_fall_left = FALSE;
8386 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8387 can_fall_right = FALSE;
8388 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8389 can_fall_left = FALSE;
8391 can_fall_any = (can_fall_left || can_fall_right);
8392 can_fall_both = (can_fall_left && can_fall_right);
8396 #if USE_NEW_ALL_SLIPPERY
8398 #if USE_NEW_SP_SLIPPERY
8399 /* !!! better use the same properties as for custom elements here !!! */
8400 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8401 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8403 can_fall_right = FALSE; /* slip down on left side */
8404 can_fall_both = FALSE;
8409 #if USE_NEW_ALL_SLIPPERY
8412 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8413 can_fall_right = FALSE; /* slip down on left side */
8415 can_fall_left = !(can_fall_right = RND(2));
8417 can_fall_both = FALSE;
8422 if (game.emulation == EMU_BOULDERDASH ||
8423 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8424 can_fall_right = FALSE; /* slip down on left side */
8426 can_fall_left = !(can_fall_right = RND(2));
8428 can_fall_both = FALSE;
8434 /* if not determined otherwise, prefer left side for slipping down */
8435 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8436 started_moving = TRUE;
8440 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8442 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8445 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8446 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8447 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8448 int belt_dir = game.belt_dir[belt_nr];
8450 if ((belt_dir == MV_LEFT && left_is_free) ||
8451 (belt_dir == MV_RIGHT && right_is_free))
8453 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8455 InitMovingField(x, y, belt_dir);
8456 started_moving = TRUE;
8458 Pushed[x][y] = TRUE;
8459 Pushed[nextx][y] = TRUE;
8461 GfxAction[x][y] = ACTION_DEFAULT;
8465 MovDir[x][y] = 0; /* if element was moving, stop it */
8470 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8472 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8474 if (CAN_MOVE(element) && !started_moving)
8477 int move_pattern = element_info[element].move_pattern;
8482 if (MovDir[x][y] == MV_NONE)
8484 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8485 x, y, element, element_info[element].token_name);
8486 printf("StartMoving(): This should never happen!\n");
8491 Moving2Blocked(x, y, &newx, &newy);
8493 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8496 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8497 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8499 WasJustMoving[x][y] = 0;
8500 CheckCollision[x][y] = 0;
8502 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8504 if (Feld[x][y] != element) /* element has changed */
8508 if (!MovDelay[x][y]) /* start new movement phase */
8510 /* all objects that can change their move direction after each step
8511 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8513 if (element != EL_YAMYAM &&
8514 element != EL_DARK_YAMYAM &&
8515 element != EL_PACMAN &&
8516 !(move_pattern & MV_ANY_DIRECTION) &&
8517 move_pattern != MV_TURNING_LEFT &&
8518 move_pattern != MV_TURNING_RIGHT &&
8519 move_pattern != MV_TURNING_LEFT_RIGHT &&
8520 move_pattern != MV_TURNING_RIGHT_LEFT &&
8521 move_pattern != MV_TURNING_RANDOM)
8525 if (MovDelay[x][y] && (element == EL_BUG ||
8526 element == EL_SPACESHIP ||
8527 element == EL_SP_SNIKSNAK ||
8528 element == EL_SP_ELECTRON ||
8529 element == EL_MOLE))
8530 TEST_DrawLevelField(x, y);
8534 if (MovDelay[x][y]) /* wait some time before next movement */
8538 if (element == EL_ROBOT ||
8539 element == EL_YAMYAM ||
8540 element == EL_DARK_YAMYAM)
8542 DrawLevelElementAnimationIfNeeded(x, y, element);
8543 PlayLevelSoundAction(x, y, ACTION_WAITING);
8545 else if (element == EL_SP_ELECTRON)
8546 DrawLevelElementAnimationIfNeeded(x, y, element);
8547 else if (element == EL_DRAGON)
8550 int dir = MovDir[x][y];
8551 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8552 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8553 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8554 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8555 dir == MV_UP ? IMG_FLAMES_1_UP :
8556 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8557 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8559 GfxAction[x][y] = ACTION_ATTACKING;
8561 if (IS_PLAYER(x, y))
8562 DrawPlayerField(x, y);
8564 TEST_DrawLevelField(x, y);
8566 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8568 for (i = 1; i <= 3; i++)
8570 int xx = x + i * dx;
8571 int yy = y + i * dy;
8572 int sx = SCREENX(xx);
8573 int sy = SCREENY(yy);
8574 int flame_graphic = graphic + (i - 1);
8576 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8581 int flamed = MovingOrBlocked2Element(xx, yy);
8585 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8587 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8588 RemoveMovingField(xx, yy);
8590 RemoveField(xx, yy);
8592 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8595 RemoveMovingField(xx, yy);
8598 ChangeDelay[xx][yy] = 0;
8600 Feld[xx][yy] = EL_FLAMES;
8602 if (IN_SCR_FIELD(sx, sy))
8604 TEST_DrawLevelFieldCrumbled(xx, yy);
8605 DrawGraphic(sx, sy, flame_graphic, frame);
8610 if (Feld[xx][yy] == EL_FLAMES)
8611 Feld[xx][yy] = EL_EMPTY;
8612 TEST_DrawLevelField(xx, yy);
8617 if (MovDelay[x][y]) /* element still has to wait some time */
8619 PlayLevelSoundAction(x, y, ACTION_WAITING);
8625 /* now make next step */
8627 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8629 if (DONT_COLLIDE_WITH(element) &&
8630 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8631 !PLAYER_ENEMY_PROTECTED(newx, newy))
8633 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8638 else if (CAN_MOVE_INTO_ACID(element) &&
8639 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8640 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8641 (MovDir[x][y] == MV_DOWN ||
8642 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8644 SplashAcid(newx, newy);
8645 Store[x][y] = EL_ACID;
8647 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8649 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8650 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8651 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8652 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8655 TEST_DrawLevelField(x, y);
8657 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8658 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8659 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8661 local_player->friends_still_needed--;
8662 if (!local_player->friends_still_needed &&
8663 !local_player->GameOver && AllPlayersGone)
8664 PlayerWins(local_player);
8668 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8670 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8671 TEST_DrawLevelField(newx, newy);
8673 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8675 else if (!IS_FREE(newx, newy))
8677 GfxAction[x][y] = ACTION_WAITING;
8679 if (IS_PLAYER(x, y))
8680 DrawPlayerField(x, y);
8682 TEST_DrawLevelField(x, y);
8687 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8689 if (IS_FOOD_PIG(Feld[newx][newy]))
8691 if (IS_MOVING(newx, newy))
8692 RemoveMovingField(newx, newy);
8695 Feld[newx][newy] = EL_EMPTY;
8696 TEST_DrawLevelField(newx, newy);
8699 PlayLevelSound(x, y, SND_PIG_DIGGING);
8701 else if (!IS_FREE(newx, newy))
8703 if (IS_PLAYER(x, y))
8704 DrawPlayerField(x, y);
8706 TEST_DrawLevelField(x, y);
8711 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8713 if (Store[x][y] != EL_EMPTY)
8715 boolean can_clone = FALSE;
8718 /* check if element to clone is still there */
8719 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8721 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8729 /* cannot clone or target field not free anymore -- do not clone */
8730 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8731 Store[x][y] = EL_EMPTY;
8734 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8736 if (IS_MV_DIAGONAL(MovDir[x][y]))
8738 int diagonal_move_dir = MovDir[x][y];
8739 int stored = Store[x][y];
8740 int change_delay = 8;
8743 /* android is moving diagonally */
8745 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8747 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8748 GfxElement[x][y] = EL_EMC_ANDROID;
8749 GfxAction[x][y] = ACTION_SHRINKING;
8750 GfxDir[x][y] = diagonal_move_dir;
8751 ChangeDelay[x][y] = change_delay;
8753 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8756 DrawLevelGraphicAnimation(x, y, graphic);
8757 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8759 if (Feld[newx][newy] == EL_ACID)
8761 SplashAcid(newx, newy);
8766 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8768 Store[newx][newy] = EL_EMC_ANDROID;
8769 GfxElement[newx][newy] = EL_EMC_ANDROID;
8770 GfxAction[newx][newy] = ACTION_GROWING;
8771 GfxDir[newx][newy] = diagonal_move_dir;
8772 ChangeDelay[newx][newy] = change_delay;
8774 graphic = el_act_dir2img(GfxElement[newx][newy],
8775 GfxAction[newx][newy], GfxDir[newx][newy]);
8777 DrawLevelGraphicAnimation(newx, newy, graphic);
8778 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8784 Feld[newx][newy] = EL_EMPTY;
8785 TEST_DrawLevelField(newx, newy);
8787 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8790 else if (!IS_FREE(newx, newy))
8793 if (IS_PLAYER(x, y))
8794 DrawPlayerField(x, y);
8796 TEST_DrawLevelField(x, y);
8802 else if (IS_CUSTOM_ELEMENT(element) &&
8803 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8806 if (!DigFieldByCE(newx, newy, element))
8809 int new_element = Feld[newx][newy];
8811 if (!IS_FREE(newx, newy))
8813 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8814 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8817 /* no element can dig solid indestructible elements */
8818 if (IS_INDESTRUCTIBLE(new_element) &&
8819 !IS_DIGGABLE(new_element) &&
8820 !IS_COLLECTIBLE(new_element))
8823 if (AmoebaNr[newx][newy] &&
8824 (new_element == EL_AMOEBA_FULL ||
8825 new_element == EL_BD_AMOEBA ||
8826 new_element == EL_AMOEBA_GROWING))
8828 AmoebaCnt[AmoebaNr[newx][newy]]--;
8829 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8832 if (IS_MOVING(newx, newy))
8833 RemoveMovingField(newx, newy);
8836 RemoveField(newx, newy);
8837 TEST_DrawLevelField(newx, newy);
8840 /* if digged element was about to explode, prevent the explosion */
8841 ExplodeField[newx][newy] = EX_TYPE_NONE;
8843 PlayLevelSoundAction(x, y, action);
8846 Store[newx][newy] = EL_EMPTY;
8849 /* this makes it possible to leave the removed element again */
8850 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8851 Store[newx][newy] = new_element;
8853 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8855 int move_leave_element = element_info[element].move_leave_element;
8857 /* this makes it possible to leave the removed element again */
8858 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8859 new_element : move_leave_element);
8865 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8867 RunnerVisit[x][y] = FrameCounter;
8868 PlayerVisit[x][y] /= 8; /* expire player visit path */
8871 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8873 if (!IS_FREE(newx, newy))
8875 if (IS_PLAYER(x, y))
8876 DrawPlayerField(x, y);
8878 TEST_DrawLevelField(x, y);
8884 boolean wanna_flame = !RND(10);
8885 int dx = newx - x, dy = newy - y;
8886 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8887 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8888 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8889 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8890 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8891 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8894 IS_CLASSIC_ENEMY(element1) ||
8895 IS_CLASSIC_ENEMY(element2)) &&
8896 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8897 element1 != EL_FLAMES && element2 != EL_FLAMES)
8899 ResetGfxAnimation(x, y);
8900 GfxAction[x][y] = ACTION_ATTACKING;
8902 if (IS_PLAYER(x, y))
8903 DrawPlayerField(x, y);
8905 TEST_DrawLevelField(x, y);
8907 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8909 MovDelay[x][y] = 50;
8913 RemoveField(newx, newy);
8915 Feld[newx][newy] = EL_FLAMES;
8916 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8919 RemoveField(newx1, newy1);
8921 Feld[newx1][newy1] = EL_FLAMES;
8923 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8926 RemoveField(newx2, newy2);
8928 Feld[newx2][newy2] = EL_FLAMES;
8935 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8936 Feld[newx][newy] == EL_DIAMOND)
8938 if (IS_MOVING(newx, newy))
8939 RemoveMovingField(newx, newy);
8942 Feld[newx][newy] = EL_EMPTY;
8943 TEST_DrawLevelField(newx, newy);
8946 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8948 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8949 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8951 if (AmoebaNr[newx][newy])
8953 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8954 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8955 Feld[newx][newy] == EL_BD_AMOEBA)
8956 AmoebaCnt[AmoebaNr[newx][newy]]--;
8961 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8963 RemoveMovingField(newx, newy);
8966 if (IS_MOVING(newx, newy))
8968 RemoveMovingField(newx, newy);
8973 Feld[newx][newy] = EL_EMPTY;
8974 TEST_DrawLevelField(newx, newy);
8977 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8979 else if ((element == EL_PACMAN || element == EL_MOLE)
8980 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8982 if (AmoebaNr[newx][newy])
8984 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8985 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8986 Feld[newx][newy] == EL_BD_AMOEBA)
8987 AmoebaCnt[AmoebaNr[newx][newy]]--;
8990 if (element == EL_MOLE)
8992 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8993 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8995 ResetGfxAnimation(x, y);
8996 GfxAction[x][y] = ACTION_DIGGING;
8997 TEST_DrawLevelField(x, y);
8999 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
9001 return; /* wait for shrinking amoeba */
9003 else /* element == EL_PACMAN */
9005 Feld[newx][newy] = EL_EMPTY;
9006 TEST_DrawLevelField(newx, newy);
9007 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9010 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9011 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9012 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9014 /* wait for shrinking amoeba to completely disappear */
9017 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9019 /* object was running against a wall */
9024 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9025 if (move_pattern & MV_ANY_DIRECTION &&
9026 move_pattern == MovDir[x][y])
9028 int blocking_element =
9029 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9031 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9034 element = Feld[x][y]; /* element might have changed */
9038 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
9039 DrawLevelElementAnimation(x, y, element);
9041 if (DONT_TOUCH(element))
9042 TestIfBadThingTouchesPlayer(x, y);
9047 InitMovingField(x, y, MovDir[x][y]);
9049 PlayLevelSoundAction(x, y, ACTION_MOVING);
9053 ContinueMoving(x, y);
9056 void ContinueMoving(int x, int y)
9058 int element = Feld[x][y];
9059 struct ElementInfo *ei = &element_info[element];
9060 int direction = MovDir[x][y];
9061 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9062 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9063 int newx = x + dx, newy = y + dy;
9064 int stored = Store[x][y];
9065 int stored_new = Store[newx][newy];
9066 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9067 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9068 boolean last_line = (newy == lev_fieldy - 1);
9070 MovPos[x][y] += getElementMoveStepsize(x, y);
9072 if (pushed_by_player) /* special case: moving object pushed by player */
9073 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9075 if (ABS(MovPos[x][y]) < TILEX)
9078 int ee = Feld[x][y];
9079 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9080 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9082 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9083 x, y, ABS(MovPos[x][y]),
9085 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9088 TEST_DrawLevelField(x, y);
9090 return; /* element is still moving */
9093 /* element reached destination field */
9095 Feld[x][y] = EL_EMPTY;
9096 Feld[newx][newy] = element;
9097 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9099 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9101 element = Feld[newx][newy] = EL_ACID;
9103 else if (element == EL_MOLE)
9105 Feld[x][y] = EL_SAND;
9107 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9109 else if (element == EL_QUICKSAND_FILLING)
9111 element = Feld[newx][newy] = get_next_element(element);
9112 Store[newx][newy] = Store[x][y];
9114 else if (element == EL_QUICKSAND_EMPTYING)
9116 Feld[x][y] = get_next_element(element);
9117 element = Feld[newx][newy] = Store[x][y];
9119 else if (element == EL_QUICKSAND_FAST_FILLING)
9121 element = Feld[newx][newy] = get_next_element(element);
9122 Store[newx][newy] = Store[x][y];
9124 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9126 Feld[x][y] = get_next_element(element);
9127 element = Feld[newx][newy] = Store[x][y];
9129 else if (element == EL_MAGIC_WALL_FILLING)
9131 element = Feld[newx][newy] = get_next_element(element);
9132 if (!game.magic_wall_active)
9133 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9134 Store[newx][newy] = Store[x][y];
9136 else if (element == EL_MAGIC_WALL_EMPTYING)
9138 Feld[x][y] = get_next_element(element);
9139 if (!game.magic_wall_active)
9140 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9141 element = Feld[newx][newy] = Store[x][y];
9143 #if USE_NEW_CUSTOM_VALUE
9144 InitField(newx, newy, FALSE);
9147 else if (element == EL_BD_MAGIC_WALL_FILLING)
9149 element = Feld[newx][newy] = get_next_element(element);
9150 if (!game.magic_wall_active)
9151 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9152 Store[newx][newy] = Store[x][y];
9154 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9156 Feld[x][y] = get_next_element(element);
9157 if (!game.magic_wall_active)
9158 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9159 element = Feld[newx][newy] = Store[x][y];
9161 #if USE_NEW_CUSTOM_VALUE
9162 InitField(newx, newy, FALSE);
9165 else if (element == EL_DC_MAGIC_WALL_FILLING)
9167 element = Feld[newx][newy] = get_next_element(element);
9168 if (!game.magic_wall_active)
9169 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9170 Store[newx][newy] = Store[x][y];
9172 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9174 Feld[x][y] = get_next_element(element);
9175 if (!game.magic_wall_active)
9176 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9177 element = Feld[newx][newy] = Store[x][y];
9179 #if USE_NEW_CUSTOM_VALUE
9180 InitField(newx, newy, FALSE);
9183 else if (element == EL_AMOEBA_DROPPING)
9185 Feld[x][y] = get_next_element(element);
9186 element = Feld[newx][newy] = Store[x][y];
9188 else if (element == EL_SOKOBAN_OBJECT)
9191 Feld[x][y] = Back[x][y];
9193 if (Back[newx][newy])
9194 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9196 Back[x][y] = Back[newx][newy] = 0;
9199 Store[x][y] = EL_EMPTY;
9204 MovDelay[newx][newy] = 0;
9206 if (CAN_CHANGE_OR_HAS_ACTION(element))
9208 /* copy element change control values to new field */
9209 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9210 ChangePage[newx][newy] = ChangePage[x][y];
9211 ChangeCount[newx][newy] = ChangeCount[x][y];
9212 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9215 #if USE_NEW_CUSTOM_VALUE
9216 CustomValue[newx][newy] = CustomValue[x][y];
9219 ChangeDelay[x][y] = 0;
9220 ChangePage[x][y] = -1;
9221 ChangeCount[x][y] = 0;
9222 ChangeEvent[x][y] = -1;
9224 #if USE_NEW_CUSTOM_VALUE
9225 CustomValue[x][y] = 0;
9228 /* copy animation control values to new field */
9229 GfxFrame[newx][newy] = GfxFrame[x][y];
9230 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9231 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9232 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9234 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9236 /* some elements can leave other elements behind after moving */
9238 if (ei->move_leave_element != EL_EMPTY &&
9239 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9240 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9242 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9243 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9244 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9247 int move_leave_element = ei->move_leave_element;
9251 /* this makes it possible to leave the removed element again */
9252 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9253 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9255 /* this makes it possible to leave the removed element again */
9256 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9257 move_leave_element = stored;
9260 /* this makes it possible to leave the removed element again */
9261 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9262 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9263 move_leave_element = stored;
9266 Feld[x][y] = move_leave_element;
9268 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9269 MovDir[x][y] = direction;
9271 InitField(x, y, FALSE);
9273 if (GFX_CRUMBLED(Feld[x][y]))
9274 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9276 if (ELEM_IS_PLAYER(move_leave_element))
9277 RelocatePlayer(x, y, move_leave_element);
9280 /* do this after checking for left-behind element */
9281 ResetGfxAnimation(x, y); /* reset animation values for old field */
9283 if (!CAN_MOVE(element) ||
9284 (CAN_FALL(element) && direction == MV_DOWN &&
9285 (element == EL_SPRING ||
9286 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9287 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9288 GfxDir[x][y] = MovDir[newx][newy] = 0;
9290 TEST_DrawLevelField(x, y);
9291 TEST_DrawLevelField(newx, newy);
9293 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9295 /* prevent pushed element from moving on in pushed direction */
9296 if (pushed_by_player && CAN_MOVE(element) &&
9297 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9298 !(element_info[element].move_pattern & direction))
9299 TurnRound(newx, newy);
9301 /* prevent elements on conveyor belt from moving on in last direction */
9302 if (pushed_by_conveyor && CAN_FALL(element) &&
9303 direction & MV_HORIZONTAL)
9304 MovDir[newx][newy] = 0;
9306 if (!pushed_by_player)
9308 int nextx = newx + dx, nexty = newy + dy;
9309 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9311 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9313 if (CAN_FALL(element) && direction == MV_DOWN)
9314 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9316 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9317 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9319 #if USE_FIX_IMPACT_COLLISION
9320 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9321 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9325 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9327 TestIfBadThingTouchesPlayer(newx, newy);
9328 TestIfBadThingTouchesFriend(newx, newy);
9330 if (!IS_CUSTOM_ELEMENT(element))
9331 TestIfBadThingTouchesOtherBadThing(newx, newy);
9333 else if (element == EL_PENGUIN)
9334 TestIfFriendTouchesBadThing(newx, newy);
9336 if (DONT_GET_HIT_BY(element))
9338 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9341 /* give the player one last chance (one more frame) to move away */
9342 if (CAN_FALL(element) && direction == MV_DOWN &&
9343 (last_line || (!IS_FREE(x, newy + 1) &&
9344 (!IS_PLAYER(x, newy + 1) ||
9345 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9348 if (pushed_by_player && !game.use_change_when_pushing_bug)
9350 int push_side = MV_DIR_OPPOSITE(direction);
9351 struct PlayerInfo *player = PLAYERINFO(x, y);
9353 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9354 player->index_bit, push_side);
9355 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9356 player->index_bit, push_side);
9359 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9360 MovDelay[newx][newy] = 1;
9362 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9364 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9367 if (ChangePage[newx][newy] != -1) /* delayed change */
9369 int page = ChangePage[newx][newy];
9370 struct ElementChangeInfo *change = &ei->change_page[page];
9372 ChangePage[newx][newy] = -1;
9374 if (change->can_change)
9376 if (ChangeElement(newx, newy, element, page))
9378 if (change->post_change_function)
9379 change->post_change_function(newx, newy);
9383 if (change->has_action)
9384 ExecuteCustomElementAction(newx, newy, element, page);
9388 TestIfElementHitsCustomElement(newx, newy, direction);
9389 TestIfPlayerTouchesCustomElement(newx, newy);
9390 TestIfElementTouchesCustomElement(newx, newy);
9392 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9393 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9394 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9395 MV_DIR_OPPOSITE(direction));
9398 int AmoebeNachbarNr(int ax, int ay)
9401 int element = Feld[ax][ay];
9403 static int xy[4][2] =
9411 for (i = 0; i < NUM_DIRECTIONS; i++)
9413 int x = ax + xy[i][0];
9414 int y = ay + xy[i][1];
9416 if (!IN_LEV_FIELD(x, y))
9419 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9420 group_nr = AmoebaNr[x][y];
9426 void AmoebenVereinigen(int ax, int ay)
9428 int i, x, y, xx, yy;
9429 int new_group_nr = AmoebaNr[ax][ay];
9430 static int xy[4][2] =
9438 if (new_group_nr == 0)
9441 for (i = 0; i < NUM_DIRECTIONS; i++)
9446 if (!IN_LEV_FIELD(x, y))
9449 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9450 Feld[x][y] == EL_BD_AMOEBA ||
9451 Feld[x][y] == EL_AMOEBA_DEAD) &&
9452 AmoebaNr[x][y] != new_group_nr)
9454 int old_group_nr = AmoebaNr[x][y];
9456 if (old_group_nr == 0)
9459 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9460 AmoebaCnt[old_group_nr] = 0;
9461 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9462 AmoebaCnt2[old_group_nr] = 0;
9464 SCAN_PLAYFIELD(xx, yy)
9466 if (AmoebaNr[xx][yy] == old_group_nr)
9467 AmoebaNr[xx][yy] = new_group_nr;
9473 void AmoebeUmwandeln(int ax, int ay)
9477 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9479 int group_nr = AmoebaNr[ax][ay];
9484 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9485 printf("AmoebeUmwandeln(): This should never happen!\n");
9490 SCAN_PLAYFIELD(x, y)
9492 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9495 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9499 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9500 SND_AMOEBA_TURNING_TO_GEM :
9501 SND_AMOEBA_TURNING_TO_ROCK));
9506 static int xy[4][2] =
9514 for (i = 0; i < NUM_DIRECTIONS; i++)
9519 if (!IN_LEV_FIELD(x, y))
9522 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9524 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9525 SND_AMOEBA_TURNING_TO_GEM :
9526 SND_AMOEBA_TURNING_TO_ROCK));
9533 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9536 int group_nr = AmoebaNr[ax][ay];
9537 boolean done = FALSE;
9542 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9543 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9548 SCAN_PLAYFIELD(x, y)
9550 if (AmoebaNr[x][y] == group_nr &&
9551 (Feld[x][y] == EL_AMOEBA_DEAD ||
9552 Feld[x][y] == EL_BD_AMOEBA ||
9553 Feld[x][y] == EL_AMOEBA_GROWING))
9556 Feld[x][y] = new_element;
9557 InitField(x, y, FALSE);
9558 TEST_DrawLevelField(x, y);
9564 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9565 SND_BD_AMOEBA_TURNING_TO_ROCK :
9566 SND_BD_AMOEBA_TURNING_TO_GEM));
9569 void AmoebeWaechst(int x, int y)
9571 static unsigned int sound_delay = 0;
9572 static unsigned int sound_delay_value = 0;
9574 if (!MovDelay[x][y]) /* start new growing cycle */
9578 if (DelayReached(&sound_delay, sound_delay_value))
9580 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9581 sound_delay_value = 30;
9585 if (MovDelay[x][y]) /* wait some time before growing bigger */
9588 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9590 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9591 6 - MovDelay[x][y]);
9593 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9596 if (!MovDelay[x][y])
9598 Feld[x][y] = Store[x][y];
9600 TEST_DrawLevelField(x, y);
9605 void AmoebaDisappearing(int x, int y)
9607 static unsigned int sound_delay = 0;
9608 static unsigned int sound_delay_value = 0;
9610 if (!MovDelay[x][y]) /* start new shrinking cycle */
9614 if (DelayReached(&sound_delay, sound_delay_value))
9615 sound_delay_value = 30;
9618 if (MovDelay[x][y]) /* wait some time before shrinking */
9621 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9623 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9624 6 - MovDelay[x][y]);
9626 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9629 if (!MovDelay[x][y])
9631 Feld[x][y] = EL_EMPTY;
9632 TEST_DrawLevelField(x, y);
9634 /* don't let mole enter this field in this cycle;
9635 (give priority to objects falling to this field from above) */
9641 void AmoebeAbleger(int ax, int ay)
9644 int element = Feld[ax][ay];
9645 int graphic = el2img(element);
9646 int newax = ax, neway = ay;
9647 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9648 static int xy[4][2] =
9656 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9658 Feld[ax][ay] = EL_AMOEBA_DEAD;
9659 TEST_DrawLevelField(ax, ay);
9663 if (IS_ANIMATED(graphic))
9664 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9666 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9667 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9669 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9672 if (MovDelay[ax][ay])
9676 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9679 int x = ax + xy[start][0];
9680 int y = ay + xy[start][1];
9682 if (!IN_LEV_FIELD(x, y))
9685 if (IS_FREE(x, y) ||
9686 CAN_GROW_INTO(Feld[x][y]) ||
9687 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9688 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9694 if (newax == ax && neway == ay)
9697 else /* normal or "filled" (BD style) amoeba */
9700 boolean waiting_for_player = FALSE;
9702 for (i = 0; i < NUM_DIRECTIONS; i++)
9704 int j = (start + i) % 4;
9705 int x = ax + xy[j][0];
9706 int y = ay + xy[j][1];
9708 if (!IN_LEV_FIELD(x, y))
9711 if (IS_FREE(x, y) ||
9712 CAN_GROW_INTO(Feld[x][y]) ||
9713 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9714 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9720 else if (IS_PLAYER(x, y))
9721 waiting_for_player = TRUE;
9724 if (newax == ax && neway == ay) /* amoeba cannot grow */
9726 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9728 Feld[ax][ay] = EL_AMOEBA_DEAD;
9729 TEST_DrawLevelField(ax, ay);
9730 AmoebaCnt[AmoebaNr[ax][ay]]--;
9732 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9734 if (element == EL_AMOEBA_FULL)
9735 AmoebeUmwandeln(ax, ay);
9736 else if (element == EL_BD_AMOEBA)
9737 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9742 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9744 /* amoeba gets larger by growing in some direction */
9746 int new_group_nr = AmoebaNr[ax][ay];
9749 if (new_group_nr == 0)
9751 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9752 printf("AmoebeAbleger(): This should never happen!\n");
9757 AmoebaNr[newax][neway] = new_group_nr;
9758 AmoebaCnt[new_group_nr]++;
9759 AmoebaCnt2[new_group_nr]++;
9761 /* if amoeba touches other amoeba(s) after growing, unify them */
9762 AmoebenVereinigen(newax, neway);
9764 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9766 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9772 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9773 (neway == lev_fieldy - 1 && newax != ax))
9775 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9776 Store[newax][neway] = element;
9778 else if (neway == ay || element == EL_EMC_DRIPPER)
9780 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9782 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9786 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9787 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9788 Store[ax][ay] = EL_AMOEBA_DROP;
9789 ContinueMoving(ax, ay);
9793 TEST_DrawLevelField(newax, neway);
9796 void Life(int ax, int ay)
9800 int element = Feld[ax][ay];
9801 int graphic = el2img(element);
9802 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9804 boolean changed = FALSE;
9806 if (IS_ANIMATED(graphic))
9807 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9812 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9813 MovDelay[ax][ay] = life_time;
9815 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9818 if (MovDelay[ax][ay])
9822 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9824 int xx = ax+x1, yy = ay+y1;
9827 if (!IN_LEV_FIELD(xx, yy))
9830 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9832 int x = xx+x2, y = yy+y2;
9834 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9837 if (((Feld[x][y] == element ||
9838 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9840 (IS_FREE(x, y) && Stop[x][y]))
9844 if (xx == ax && yy == ay) /* field in the middle */
9846 if (nachbarn < life_parameter[0] ||
9847 nachbarn > life_parameter[1])
9849 Feld[xx][yy] = EL_EMPTY;
9851 TEST_DrawLevelField(xx, yy);
9852 Stop[xx][yy] = TRUE;
9856 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9857 { /* free border field */
9858 if (nachbarn >= life_parameter[2] &&
9859 nachbarn <= life_parameter[3])
9861 Feld[xx][yy] = element;
9862 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9864 TEST_DrawLevelField(xx, yy);
9865 Stop[xx][yy] = TRUE;
9872 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9873 SND_GAME_OF_LIFE_GROWING);
9876 static void InitRobotWheel(int x, int y)
9878 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9881 static void RunRobotWheel(int x, int y)
9883 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9886 static void StopRobotWheel(int x, int y)
9888 if (ZX == x && ZY == y)
9892 game.robot_wheel_active = FALSE;
9896 static void InitTimegateWheel(int x, int y)
9898 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9901 static void RunTimegateWheel(int x, int y)
9903 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9906 static void InitMagicBallDelay(int x, int y)
9909 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9911 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9915 static void ActivateMagicBall(int bx, int by)
9919 if (level.ball_random)
9921 int pos_border = RND(8); /* select one of the eight border elements */
9922 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9923 int xx = pos_content % 3;
9924 int yy = pos_content / 3;
9929 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9930 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9934 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9936 int xx = x - bx + 1;
9937 int yy = y - by + 1;
9939 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9940 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9944 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9947 void CheckExit(int x, int y)
9949 if (local_player->gems_still_needed > 0 ||
9950 local_player->sokobanfields_still_needed > 0 ||
9951 local_player->lights_still_needed > 0)
9953 int element = Feld[x][y];
9954 int graphic = el2img(element);
9956 if (IS_ANIMATED(graphic))
9957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9962 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9965 Feld[x][y] = EL_EXIT_OPENING;
9967 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9970 void CheckExitEM(int x, int y)
9972 if (local_player->gems_still_needed > 0 ||
9973 local_player->sokobanfields_still_needed > 0 ||
9974 local_player->lights_still_needed > 0)
9976 int element = Feld[x][y];
9977 int graphic = el2img(element);
9979 if (IS_ANIMATED(graphic))
9980 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9985 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9988 Feld[x][y] = EL_EM_EXIT_OPENING;
9990 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9993 void CheckExitSteel(int x, int y)
9995 if (local_player->gems_still_needed > 0 ||
9996 local_player->sokobanfields_still_needed > 0 ||
9997 local_player->lights_still_needed > 0)
9999 int element = Feld[x][y];
10000 int graphic = el2img(element);
10002 if (IS_ANIMATED(graphic))
10003 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10008 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10011 Feld[x][y] = EL_STEEL_EXIT_OPENING;
10013 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10016 void CheckExitSteelEM(int x, int y)
10018 if (local_player->gems_still_needed > 0 ||
10019 local_player->sokobanfields_still_needed > 0 ||
10020 local_player->lights_still_needed > 0)
10022 int element = Feld[x][y];
10023 int graphic = el2img(element);
10025 if (IS_ANIMATED(graphic))
10026 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10031 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10034 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10036 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10039 void CheckExitSP(int x, int y)
10041 if (local_player->gems_still_needed > 0)
10043 int element = Feld[x][y];
10044 int graphic = el2img(element);
10046 if (IS_ANIMATED(graphic))
10047 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10052 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10055 Feld[x][y] = EL_SP_EXIT_OPENING;
10057 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10060 static void CloseAllOpenTimegates()
10064 SCAN_PLAYFIELD(x, y)
10066 int element = Feld[x][y];
10068 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10070 Feld[x][y] = EL_TIMEGATE_CLOSING;
10072 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10077 void DrawTwinkleOnField(int x, int y)
10079 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10082 if (Feld[x][y] == EL_BD_DIAMOND)
10085 if (MovDelay[x][y] == 0) /* next animation frame */
10086 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10088 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10092 DrawLevelElementAnimation(x, y, Feld[x][y]);
10094 if (MovDelay[x][y] != 0)
10096 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10097 10 - MovDelay[x][y]);
10099 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10104 void MauerWaechst(int x, int y)
10108 if (!MovDelay[x][y]) /* next animation frame */
10109 MovDelay[x][y] = 3 * delay;
10111 if (MovDelay[x][y]) /* wait some time before next frame */
10115 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10117 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10118 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10120 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10123 if (!MovDelay[x][y])
10125 if (MovDir[x][y] == MV_LEFT)
10127 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10128 TEST_DrawLevelField(x - 1, y);
10130 else if (MovDir[x][y] == MV_RIGHT)
10132 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10133 TEST_DrawLevelField(x + 1, y);
10135 else if (MovDir[x][y] == MV_UP)
10137 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10138 TEST_DrawLevelField(x, y - 1);
10142 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10143 TEST_DrawLevelField(x, y + 1);
10146 Feld[x][y] = Store[x][y];
10148 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10149 TEST_DrawLevelField(x, y);
10154 void MauerAbleger(int ax, int ay)
10156 int element = Feld[ax][ay];
10157 int graphic = el2img(element);
10158 boolean oben_frei = FALSE, unten_frei = FALSE;
10159 boolean links_frei = FALSE, rechts_frei = FALSE;
10160 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10161 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10162 boolean new_wall = FALSE;
10164 if (IS_ANIMATED(graphic))
10165 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10167 if (!MovDelay[ax][ay]) /* start building new wall */
10168 MovDelay[ax][ay] = 6;
10170 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10172 MovDelay[ax][ay]--;
10173 if (MovDelay[ax][ay])
10177 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10179 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10181 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10183 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10184 rechts_frei = TRUE;
10186 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10187 element == EL_EXPANDABLE_WALL_ANY)
10191 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10192 Store[ax][ay-1] = element;
10193 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10194 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10195 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10196 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10201 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10202 Store[ax][ay+1] = element;
10203 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10204 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10205 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10206 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10211 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10212 element == EL_EXPANDABLE_WALL_ANY ||
10213 element == EL_EXPANDABLE_WALL ||
10214 element == EL_BD_EXPANDABLE_WALL)
10218 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10219 Store[ax-1][ay] = element;
10220 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10221 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10222 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10223 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10229 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10230 Store[ax+1][ay] = element;
10231 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10232 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10233 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10234 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10239 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10240 TEST_DrawLevelField(ax, ay);
10242 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10243 oben_massiv = TRUE;
10244 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10245 unten_massiv = TRUE;
10246 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10247 links_massiv = TRUE;
10248 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10249 rechts_massiv = TRUE;
10251 if (((oben_massiv && unten_massiv) ||
10252 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10253 element == EL_EXPANDABLE_WALL) &&
10254 ((links_massiv && rechts_massiv) ||
10255 element == EL_EXPANDABLE_WALL_VERTICAL))
10256 Feld[ax][ay] = EL_WALL;
10259 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10262 void MauerAblegerStahl(int ax, int ay)
10264 int element = Feld[ax][ay];
10265 int graphic = el2img(element);
10266 boolean oben_frei = FALSE, unten_frei = FALSE;
10267 boolean links_frei = FALSE, rechts_frei = FALSE;
10268 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10269 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10270 boolean new_wall = FALSE;
10272 if (IS_ANIMATED(graphic))
10273 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10275 if (!MovDelay[ax][ay]) /* start building new wall */
10276 MovDelay[ax][ay] = 6;
10278 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10280 MovDelay[ax][ay]--;
10281 if (MovDelay[ax][ay])
10285 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10287 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10289 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10291 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10292 rechts_frei = TRUE;
10294 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10295 element == EL_EXPANDABLE_STEELWALL_ANY)
10299 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10300 Store[ax][ay-1] = element;
10301 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10302 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10303 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10304 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10309 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10310 Store[ax][ay+1] = element;
10311 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10312 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10313 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10314 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10319 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10320 element == EL_EXPANDABLE_STEELWALL_ANY)
10324 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10325 Store[ax-1][ay] = element;
10326 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10327 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10328 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10329 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10335 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10336 Store[ax+1][ay] = element;
10337 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10338 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10339 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10340 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10345 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10346 oben_massiv = TRUE;
10347 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10348 unten_massiv = TRUE;
10349 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10350 links_massiv = TRUE;
10351 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10352 rechts_massiv = TRUE;
10354 if (((oben_massiv && unten_massiv) ||
10355 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10356 ((links_massiv && rechts_massiv) ||
10357 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10358 Feld[ax][ay] = EL_STEELWALL;
10361 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10364 void CheckForDragon(int x, int y)
10367 boolean dragon_found = FALSE;
10368 static int xy[4][2] =
10376 for (i = 0; i < NUM_DIRECTIONS; i++)
10378 for (j = 0; j < 4; j++)
10380 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10382 if (IN_LEV_FIELD(xx, yy) &&
10383 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10385 if (Feld[xx][yy] == EL_DRAGON)
10386 dragon_found = TRUE;
10395 for (i = 0; i < NUM_DIRECTIONS; i++)
10397 for (j = 0; j < 3; j++)
10399 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10401 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10403 Feld[xx][yy] = EL_EMPTY;
10404 TEST_DrawLevelField(xx, yy);
10413 static void InitBuggyBase(int x, int y)
10415 int element = Feld[x][y];
10416 int activating_delay = FRAMES_PER_SECOND / 4;
10418 ChangeDelay[x][y] =
10419 (element == EL_SP_BUGGY_BASE ?
10420 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10421 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10423 element == EL_SP_BUGGY_BASE_ACTIVE ?
10424 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10427 static void WarnBuggyBase(int x, int y)
10430 static int xy[4][2] =
10438 for (i = 0; i < NUM_DIRECTIONS; i++)
10440 int xx = x + xy[i][0];
10441 int yy = y + xy[i][1];
10443 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10445 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10452 static void InitTrap(int x, int y)
10454 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10457 static void ActivateTrap(int x, int y)
10459 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10462 static void ChangeActiveTrap(int x, int y)
10464 int graphic = IMG_TRAP_ACTIVE;
10466 /* if new animation frame was drawn, correct crumbled sand border */
10467 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10468 TEST_DrawLevelFieldCrumbled(x, y);
10471 static int getSpecialActionElement(int element, int number, int base_element)
10473 return (element != EL_EMPTY ? element :
10474 number != -1 ? base_element + number - 1 :
10478 static int getModifiedActionNumber(int value_old, int operator, int operand,
10479 int value_min, int value_max)
10481 int value_new = (operator == CA_MODE_SET ? operand :
10482 operator == CA_MODE_ADD ? value_old + operand :
10483 operator == CA_MODE_SUBTRACT ? value_old - operand :
10484 operator == CA_MODE_MULTIPLY ? value_old * operand :
10485 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10486 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10489 return (value_new < value_min ? value_min :
10490 value_new > value_max ? value_max :
10494 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10496 struct ElementInfo *ei = &element_info[element];
10497 struct ElementChangeInfo *change = &ei->change_page[page];
10498 int target_element = change->target_element;
10499 int action_type = change->action_type;
10500 int action_mode = change->action_mode;
10501 int action_arg = change->action_arg;
10502 int action_element = change->action_element;
10505 if (!change->has_action)
10508 /* ---------- determine action paramater values -------------------------- */
10510 int level_time_value =
10511 (level.time > 0 ? TimeLeft :
10514 int action_arg_element_raw =
10515 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10516 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10517 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10518 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10519 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10520 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10521 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10523 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10526 if (action_arg_element_raw == EL_GROUP_START)
10527 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10530 int action_arg_direction =
10531 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10532 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10533 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10534 change->actual_trigger_side :
10535 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10536 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10539 int action_arg_number_min =
10540 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10543 int action_arg_number_max =
10544 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10545 action_type == CA_SET_LEVEL_GEMS ? 999 :
10546 action_type == CA_SET_LEVEL_TIME ? 9999 :
10547 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10548 action_type == CA_SET_CE_VALUE ? 9999 :
10549 action_type == CA_SET_CE_SCORE ? 9999 :
10552 int action_arg_number_reset =
10553 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10554 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10555 action_type == CA_SET_LEVEL_TIME ? level.time :
10556 action_type == CA_SET_LEVEL_SCORE ? 0 :
10557 #if USE_NEW_CUSTOM_VALUE
10558 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10560 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10562 action_type == CA_SET_CE_SCORE ? 0 :
10565 int action_arg_number =
10566 (action_arg <= CA_ARG_MAX ? action_arg :
10567 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10568 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10569 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10570 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10571 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10572 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10573 #if USE_NEW_CUSTOM_VALUE
10574 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10576 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10578 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10579 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10580 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10581 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10582 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10583 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10584 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10585 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10586 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10587 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10588 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10589 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10590 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10591 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10594 int action_arg_number_old =
10595 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10596 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10597 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10598 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10599 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10602 int action_arg_number_new =
10603 getModifiedActionNumber(action_arg_number_old,
10604 action_mode, action_arg_number,
10605 action_arg_number_min, action_arg_number_max);
10608 int trigger_player_bits =
10609 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10610 change->actual_trigger_player_bits : change->trigger_player);
10612 int trigger_player_bits =
10613 (change->actual_trigger_player >= EL_PLAYER_1 &&
10614 change->actual_trigger_player <= EL_PLAYER_4 ?
10615 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10619 int action_arg_player_bits =
10620 (action_arg >= CA_ARG_PLAYER_1 &&
10621 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10622 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10623 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10626 /* ---------- execute action -------------------------------------------- */
10628 switch (action_type)
10635 /* ---------- level actions ------------------------------------------- */
10637 case CA_RESTART_LEVEL:
10639 game.restart_level = TRUE;
10644 case CA_SHOW_ENVELOPE:
10646 int element = getSpecialActionElement(action_arg_element,
10647 action_arg_number, EL_ENVELOPE_1);
10649 if (IS_ENVELOPE(element))
10650 local_player->show_envelope = element;
10655 case CA_SET_LEVEL_TIME:
10657 if (level.time > 0) /* only modify limited time value */
10659 TimeLeft = action_arg_number_new;
10662 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10664 DisplayGameControlValues();
10666 DrawGameValue_Time(TimeLeft);
10669 if (!TimeLeft && setup.time_limit)
10670 for (i = 0; i < MAX_PLAYERS; i++)
10671 KillPlayer(&stored_player[i]);
10677 case CA_SET_LEVEL_SCORE:
10679 local_player->score = action_arg_number_new;
10682 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10684 DisplayGameControlValues();
10686 DrawGameValue_Score(local_player->score);
10692 case CA_SET_LEVEL_GEMS:
10694 local_player->gems_still_needed = action_arg_number_new;
10697 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10699 DisplayGameControlValues();
10701 DrawGameValue_Emeralds(local_player->gems_still_needed);
10707 #if !USE_PLAYER_GRAVITY
10708 case CA_SET_LEVEL_GRAVITY:
10710 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10711 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10712 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10718 case CA_SET_LEVEL_WIND:
10720 game.wind_direction = action_arg_direction;
10725 case CA_SET_LEVEL_RANDOM_SEED:
10728 /* ensure that setting a new random seed while playing is predictable */
10729 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10731 InitRND(action_arg_number_new);
10735 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10743 for (i = 0; i < 9; i++)
10744 printf("%d, ", RND(2));
10752 /* ---------- player actions ------------------------------------------ */
10754 case CA_MOVE_PLAYER:
10756 /* automatically move to the next field in specified direction */
10757 for (i = 0; i < MAX_PLAYERS; i++)
10758 if (trigger_player_bits & (1 << i))
10759 stored_player[i].programmed_action = action_arg_direction;
10764 case CA_EXIT_PLAYER:
10766 for (i = 0; i < MAX_PLAYERS; i++)
10767 if (action_arg_player_bits & (1 << i))
10768 PlayerWins(&stored_player[i]);
10773 case CA_KILL_PLAYER:
10775 for (i = 0; i < MAX_PLAYERS; i++)
10776 if (action_arg_player_bits & (1 << i))
10777 KillPlayer(&stored_player[i]);
10782 case CA_SET_PLAYER_KEYS:
10784 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10785 int element = getSpecialActionElement(action_arg_element,
10786 action_arg_number, EL_KEY_1);
10788 if (IS_KEY(element))
10790 for (i = 0; i < MAX_PLAYERS; i++)
10792 if (trigger_player_bits & (1 << i))
10794 stored_player[i].key[KEY_NR(element)] = key_state;
10796 DrawGameDoorValues();
10804 case CA_SET_PLAYER_SPEED:
10807 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10810 for (i = 0; i < MAX_PLAYERS; i++)
10812 if (trigger_player_bits & (1 << i))
10814 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10816 if (action_arg == CA_ARG_SPEED_FASTER &&
10817 stored_player[i].cannot_move)
10819 action_arg_number = STEPSIZE_VERY_SLOW;
10821 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10822 action_arg == CA_ARG_SPEED_FASTER)
10824 action_arg_number = 2;
10825 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10828 else if (action_arg == CA_ARG_NUMBER_RESET)
10830 action_arg_number = level.initial_player_stepsize[i];
10834 getModifiedActionNumber(move_stepsize,
10837 action_arg_number_min,
10838 action_arg_number_max);
10840 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10847 case CA_SET_PLAYER_SHIELD:
10849 for (i = 0; i < MAX_PLAYERS; i++)
10851 if (trigger_player_bits & (1 << i))
10853 if (action_arg == CA_ARG_SHIELD_OFF)
10855 stored_player[i].shield_normal_time_left = 0;
10856 stored_player[i].shield_deadly_time_left = 0;
10858 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10860 stored_player[i].shield_normal_time_left = 999999;
10862 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10864 stored_player[i].shield_normal_time_left = 999999;
10865 stored_player[i].shield_deadly_time_left = 999999;
10873 #if USE_PLAYER_GRAVITY
10874 case CA_SET_PLAYER_GRAVITY:
10876 for (i = 0; i < MAX_PLAYERS; i++)
10878 if (trigger_player_bits & (1 << i))
10880 stored_player[i].gravity =
10881 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10882 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10883 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10884 stored_player[i].gravity);
10892 case CA_SET_PLAYER_ARTWORK:
10894 for (i = 0; i < MAX_PLAYERS; i++)
10896 if (trigger_player_bits & (1 << i))
10898 int artwork_element = action_arg_element;
10900 if (action_arg == CA_ARG_ELEMENT_RESET)
10902 (level.use_artwork_element[i] ? level.artwork_element[i] :
10903 stored_player[i].element_nr);
10905 #if USE_GFX_RESET_PLAYER_ARTWORK
10906 if (stored_player[i].artwork_element != artwork_element)
10907 stored_player[i].Frame = 0;
10910 stored_player[i].artwork_element = artwork_element;
10912 SetPlayerWaiting(&stored_player[i], FALSE);
10914 /* set number of special actions for bored and sleeping animation */
10915 stored_player[i].num_special_action_bored =
10916 get_num_special_action(artwork_element,
10917 ACTION_BORING_1, ACTION_BORING_LAST);
10918 stored_player[i].num_special_action_sleeping =
10919 get_num_special_action(artwork_element,
10920 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10927 case CA_SET_PLAYER_INVENTORY:
10929 for (i = 0; i < MAX_PLAYERS; i++)
10931 struct PlayerInfo *player = &stored_player[i];
10934 if (trigger_player_bits & (1 << i))
10936 int inventory_element = action_arg_element;
10938 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10939 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10940 action_arg == CA_ARG_ELEMENT_ACTION)
10942 int element = inventory_element;
10943 int collect_count = element_info[element].collect_count_initial;
10945 if (!IS_CUSTOM_ELEMENT(element))
10948 if (collect_count == 0)
10949 player->inventory_infinite_element = element;
10951 for (k = 0; k < collect_count; k++)
10952 if (player->inventory_size < MAX_INVENTORY_SIZE)
10953 player->inventory_element[player->inventory_size++] =
10956 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10957 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10958 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10960 if (player->inventory_infinite_element != EL_UNDEFINED &&
10961 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10962 action_arg_element_raw))
10963 player->inventory_infinite_element = EL_UNDEFINED;
10965 for (k = 0, j = 0; j < player->inventory_size; j++)
10967 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10968 action_arg_element_raw))
10969 player->inventory_element[k++] = player->inventory_element[j];
10972 player->inventory_size = k;
10974 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10976 if (player->inventory_size > 0)
10978 for (j = 0; j < player->inventory_size - 1; j++)
10979 player->inventory_element[j] = player->inventory_element[j + 1];
10981 player->inventory_size--;
10984 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10986 if (player->inventory_size > 0)
10987 player->inventory_size--;
10989 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10991 player->inventory_infinite_element = EL_UNDEFINED;
10992 player->inventory_size = 0;
10994 else if (action_arg == CA_ARG_INVENTORY_RESET)
10996 player->inventory_infinite_element = EL_UNDEFINED;
10997 player->inventory_size = 0;
10999 if (level.use_initial_inventory[i])
11001 for (j = 0; j < level.initial_inventory_size[i]; j++)
11003 int element = level.initial_inventory_content[i][j];
11004 int collect_count = element_info[element].collect_count_initial;
11006 if (!IS_CUSTOM_ELEMENT(element))
11009 if (collect_count == 0)
11010 player->inventory_infinite_element = element;
11012 for (k = 0; k < collect_count; k++)
11013 if (player->inventory_size < MAX_INVENTORY_SIZE)
11014 player->inventory_element[player->inventory_size++] =
11025 /* ---------- CE actions ---------------------------------------------- */
11027 case CA_SET_CE_VALUE:
11029 #if USE_NEW_CUSTOM_VALUE
11030 int last_ce_value = CustomValue[x][y];
11032 CustomValue[x][y] = action_arg_number_new;
11034 if (CustomValue[x][y] != last_ce_value)
11036 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11037 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11039 if (CustomValue[x][y] == 0)
11041 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11042 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11050 case CA_SET_CE_SCORE:
11052 #if USE_NEW_CUSTOM_VALUE
11053 int last_ce_score = ei->collect_score;
11055 ei->collect_score = action_arg_number_new;
11057 if (ei->collect_score != last_ce_score)
11059 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11060 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11062 if (ei->collect_score == 0)
11066 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11067 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11070 This is a very special case that seems to be a mixture between
11071 CheckElementChange() and CheckTriggeredElementChange(): while
11072 the first one only affects single elements that are triggered
11073 directly, the second one affects multiple elements in the playfield
11074 that are triggered indirectly by another element. This is a third
11075 case: Changing the CE score always affects multiple identical CEs,
11076 so every affected CE must be checked, not only the single CE for
11077 which the CE score was changed in the first place (as every instance
11078 of that CE shares the same CE score, and therefore also can change)!
11080 SCAN_PLAYFIELD(xx, yy)
11082 if (Feld[xx][yy] == element)
11083 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11084 CE_SCORE_GETS_ZERO);
11093 case CA_SET_CE_ARTWORK:
11095 int artwork_element = action_arg_element;
11096 boolean reset_frame = FALSE;
11099 if (action_arg == CA_ARG_ELEMENT_RESET)
11100 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11103 if (ei->gfx_element != artwork_element)
11104 reset_frame = TRUE;
11106 ei->gfx_element = artwork_element;
11108 SCAN_PLAYFIELD(xx, yy)
11110 if (Feld[xx][yy] == element)
11114 ResetGfxAnimation(xx, yy);
11115 ResetRandomAnimationValue(xx, yy);
11118 TEST_DrawLevelField(xx, yy);
11125 /* ---------- engine actions ------------------------------------------ */
11127 case CA_SET_ENGINE_SCAN_MODE:
11129 InitPlayfieldScanMode(action_arg);
11139 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11141 int old_element = Feld[x][y];
11142 int new_element = GetElementFromGroupElement(element);
11143 int previous_move_direction = MovDir[x][y];
11144 #if USE_NEW_CUSTOM_VALUE
11145 int last_ce_value = CustomValue[x][y];
11147 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11148 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11149 boolean add_player_onto_element = (new_element_is_player &&
11150 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11151 /* this breaks SnakeBite when a snake is
11152 halfway through a door that closes */
11153 /* NOW FIXED AT LEVEL INIT IN files.c */
11154 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11156 IS_WALKABLE(old_element));
11159 /* check if element under the player changes from accessible to unaccessible
11160 (needed for special case of dropping element which then changes) */
11161 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11162 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11170 if (!add_player_onto_element)
11172 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11173 RemoveMovingField(x, y);
11177 Feld[x][y] = new_element;
11179 #if !USE_GFX_RESET_GFX_ANIMATION
11180 ResetGfxAnimation(x, y);
11181 ResetRandomAnimationValue(x, y);
11184 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11185 MovDir[x][y] = previous_move_direction;
11187 #if USE_NEW_CUSTOM_VALUE
11188 if (element_info[new_element].use_last_ce_value)
11189 CustomValue[x][y] = last_ce_value;
11192 InitField_WithBug1(x, y, FALSE);
11194 new_element = Feld[x][y]; /* element may have changed */
11196 #if USE_GFX_RESET_GFX_ANIMATION
11197 ResetGfxAnimation(x, y);
11198 ResetRandomAnimationValue(x, y);
11201 TEST_DrawLevelField(x, y);
11203 if (GFX_CRUMBLED(new_element))
11204 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11208 /* check if element under the player changes from accessible to unaccessible
11209 (needed for special case of dropping element which then changes) */
11210 /* (must be checked after creating new element for walkable group elements) */
11211 #if USE_FIX_KILLED_BY_NON_WALKABLE
11212 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11213 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11220 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11221 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11230 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11231 if (new_element_is_player)
11232 RelocatePlayer(x, y, new_element);
11235 ChangeCount[x][y]++; /* count number of changes in the same frame */
11237 TestIfBadThingTouchesPlayer(x, y);
11238 TestIfPlayerTouchesCustomElement(x, y);
11239 TestIfElementTouchesCustomElement(x, y);
11242 static void CreateField(int x, int y, int element)
11244 CreateFieldExt(x, y, element, FALSE);
11247 static void CreateElementFromChange(int x, int y, int element)
11249 element = GET_VALID_RUNTIME_ELEMENT(element);
11251 #if USE_STOP_CHANGED_ELEMENTS
11252 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11254 int old_element = Feld[x][y];
11256 /* prevent changed element from moving in same engine frame
11257 unless both old and new element can either fall or move */
11258 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11259 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11264 CreateFieldExt(x, y, element, TRUE);
11267 static boolean ChangeElement(int x, int y, int element, int page)
11269 struct ElementInfo *ei = &element_info[element];
11270 struct ElementChangeInfo *change = &ei->change_page[page];
11271 int ce_value = CustomValue[x][y];
11272 int ce_score = ei->collect_score;
11273 int target_element;
11274 int old_element = Feld[x][y];
11276 /* always use default change event to prevent running into a loop */
11277 if (ChangeEvent[x][y] == -1)
11278 ChangeEvent[x][y] = CE_DELAY;
11280 if (ChangeEvent[x][y] == CE_DELAY)
11282 /* reset actual trigger element, trigger player and action element */
11283 change->actual_trigger_element = EL_EMPTY;
11284 change->actual_trigger_player = EL_EMPTY;
11285 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11286 change->actual_trigger_side = CH_SIDE_NONE;
11287 change->actual_trigger_ce_value = 0;
11288 change->actual_trigger_ce_score = 0;
11291 /* do not change elements more than a specified maximum number of changes */
11292 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11295 ChangeCount[x][y]++; /* count number of changes in the same frame */
11297 if (change->explode)
11304 if (change->use_target_content)
11306 boolean complete_replace = TRUE;
11307 boolean can_replace[3][3];
11310 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11313 boolean is_walkable;
11314 boolean is_diggable;
11315 boolean is_collectible;
11316 boolean is_removable;
11317 boolean is_destructible;
11318 int ex = x + xx - 1;
11319 int ey = y + yy - 1;
11320 int content_element = change->target_content.e[xx][yy];
11323 can_replace[xx][yy] = TRUE;
11325 if (ex == x && ey == y) /* do not check changing element itself */
11328 if (content_element == EL_EMPTY_SPACE)
11330 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11335 if (!IN_LEV_FIELD(ex, ey))
11337 can_replace[xx][yy] = FALSE;
11338 complete_replace = FALSE;
11345 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11346 e = MovingOrBlocked2Element(ex, ey);
11348 is_empty = (IS_FREE(ex, ey) ||
11349 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11351 is_walkable = (is_empty || IS_WALKABLE(e));
11352 is_diggable = (is_empty || IS_DIGGABLE(e));
11353 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11354 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11355 is_removable = (is_diggable || is_collectible);
11357 can_replace[xx][yy] =
11358 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11359 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11360 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11361 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11362 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11363 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11364 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11366 if (!can_replace[xx][yy])
11367 complete_replace = FALSE;
11370 if (!change->only_if_complete || complete_replace)
11372 boolean something_has_changed = FALSE;
11374 if (change->only_if_complete && change->use_random_replace &&
11375 RND(100) < change->random_percentage)
11378 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11380 int ex = x + xx - 1;
11381 int ey = y + yy - 1;
11382 int content_element;
11384 if (can_replace[xx][yy] && (!change->use_random_replace ||
11385 RND(100) < change->random_percentage))
11387 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11388 RemoveMovingField(ex, ey);
11390 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11392 content_element = change->target_content.e[xx][yy];
11393 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11394 ce_value, ce_score);
11396 CreateElementFromChange(ex, ey, target_element);
11398 something_has_changed = TRUE;
11400 /* for symmetry reasons, freeze newly created border elements */
11401 if (ex != x || ey != y)
11402 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11406 if (something_has_changed)
11408 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11409 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11415 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11416 ce_value, ce_score);
11418 if (element == EL_DIAGONAL_GROWING ||
11419 element == EL_DIAGONAL_SHRINKING)
11421 target_element = Store[x][y];
11423 Store[x][y] = EL_EMPTY;
11426 CreateElementFromChange(x, y, target_element);
11428 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11429 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11432 /* this uses direct change before indirect change */
11433 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11438 #if USE_NEW_DELAYED_ACTION
11440 static void HandleElementChange(int x, int y, int page)
11442 int element = MovingOrBlocked2Element(x, y);
11443 struct ElementInfo *ei = &element_info[element];
11444 struct ElementChangeInfo *change = &ei->change_page[page];
11445 boolean handle_action_before_change = FALSE;
11448 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11449 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11452 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11453 x, y, element, element_info[element].token_name);
11454 printf("HandleElementChange(): This should never happen!\n");
11459 /* this can happen with classic bombs on walkable, changing elements */
11460 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11463 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11464 ChangeDelay[x][y] = 0;
11470 if (ChangeDelay[x][y] == 0) /* initialize element change */
11472 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11474 if (change->can_change)
11477 /* !!! not clear why graphic animation should be reset at all here !!! */
11478 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11479 #if USE_GFX_RESET_WHEN_NOT_MOVING
11480 /* when a custom element is about to change (for example by change delay),
11481 do not reset graphic animation when the custom element is moving */
11482 if (!IS_MOVING(x, y))
11485 ResetGfxAnimation(x, y);
11486 ResetRandomAnimationValue(x, y);
11490 if (change->pre_change_function)
11491 change->pre_change_function(x, y);
11495 ChangeDelay[x][y]--;
11497 if (ChangeDelay[x][y] != 0) /* continue element change */
11499 if (change->can_change)
11501 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11503 if (IS_ANIMATED(graphic))
11504 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11506 if (change->change_function)
11507 change->change_function(x, y);
11510 else /* finish element change */
11512 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11514 page = ChangePage[x][y];
11515 ChangePage[x][y] = -1;
11517 change = &ei->change_page[page];
11520 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11522 ChangeDelay[x][y] = 1; /* try change after next move step */
11523 ChangePage[x][y] = page; /* remember page to use for change */
11529 /* special case: set new level random seed before changing element */
11530 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11531 handle_action_before_change = TRUE;
11533 if (change->has_action && handle_action_before_change)
11534 ExecuteCustomElementAction(x, y, element, page);
11537 if (change->can_change)
11539 if (ChangeElement(x, y, element, page))
11541 if (change->post_change_function)
11542 change->post_change_function(x, y);
11546 if (change->has_action && !handle_action_before_change)
11547 ExecuteCustomElementAction(x, y, element, page);
11553 static void HandleElementChange(int x, int y, int page)
11555 int element = MovingOrBlocked2Element(x, y);
11556 struct ElementInfo *ei = &element_info[element];
11557 struct ElementChangeInfo *change = &ei->change_page[page];
11560 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11563 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11564 x, y, element, element_info[element].token_name);
11565 printf("HandleElementChange(): This should never happen!\n");
11570 /* this can happen with classic bombs on walkable, changing elements */
11571 if (!CAN_CHANGE(element))
11574 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11575 ChangeDelay[x][y] = 0;
11581 if (ChangeDelay[x][y] == 0) /* initialize element change */
11583 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11585 ResetGfxAnimation(x, y);
11586 ResetRandomAnimationValue(x, y);
11588 if (change->pre_change_function)
11589 change->pre_change_function(x, y);
11592 ChangeDelay[x][y]--;
11594 if (ChangeDelay[x][y] != 0) /* continue element change */
11596 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11598 if (IS_ANIMATED(graphic))
11599 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11601 if (change->change_function)
11602 change->change_function(x, y);
11604 else /* finish element change */
11606 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11608 page = ChangePage[x][y];
11609 ChangePage[x][y] = -1;
11611 change = &ei->change_page[page];
11614 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11616 ChangeDelay[x][y] = 1; /* try change after next move step */
11617 ChangePage[x][y] = page; /* remember page to use for change */
11622 if (ChangeElement(x, y, element, page))
11624 if (change->post_change_function)
11625 change->post_change_function(x, y);
11632 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11633 int trigger_element,
11635 int trigger_player,
11639 boolean change_done_any = FALSE;
11640 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11643 if (!(trigger_events[trigger_element][trigger_event]))
11647 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11648 trigger_event, recursion_loop_depth, recursion_loop_detected,
11649 recursion_loop_element, EL_NAME(recursion_loop_element));
11652 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11654 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11656 int element = EL_CUSTOM_START + i;
11657 boolean change_done = FALSE;
11660 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11661 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11664 for (p = 0; p < element_info[element].num_change_pages; p++)
11666 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11668 if (change->can_change_or_has_action &&
11669 change->has_event[trigger_event] &&
11670 change->trigger_side & trigger_side &&
11671 change->trigger_player & trigger_player &&
11672 change->trigger_page & trigger_page_bits &&
11673 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11675 change->actual_trigger_element = trigger_element;
11676 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11677 change->actual_trigger_player_bits = trigger_player;
11678 change->actual_trigger_side = trigger_side;
11679 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11680 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11683 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11684 element, EL_NAME(element), p);
11687 if ((change->can_change && !change_done) || change->has_action)
11691 SCAN_PLAYFIELD(x, y)
11693 if (Feld[x][y] == element)
11695 if (change->can_change && !change_done)
11697 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11698 /* if element already changed in this frame, not only prevent
11699 another element change (checked in ChangeElement()), but
11700 also prevent additional element actions for this element */
11702 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11703 !level.use_action_after_change_bug)
11708 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11709 element, EL_NAME(element), p);
11712 ChangeDelay[x][y] = 1;
11713 ChangeEvent[x][y] = trigger_event;
11715 HandleElementChange(x, y, p);
11717 #if USE_NEW_DELAYED_ACTION
11718 else if (change->has_action)
11720 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11721 /* if element already changed in this frame, not only prevent
11722 another element change (checked in ChangeElement()), but
11723 also prevent additional element actions for this element */
11725 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11726 !level.use_action_after_change_bug)
11732 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11733 element, EL_NAME(element), p);
11736 ExecuteCustomElementAction(x, y, element, p);
11737 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11740 if (change->has_action)
11742 ExecuteCustomElementAction(x, y, element, p);
11743 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11749 if (change->can_change)
11751 change_done = TRUE;
11752 change_done_any = TRUE;
11755 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11756 element, EL_NAME(element), p);
11765 RECURSION_LOOP_DETECTION_END();
11767 return change_done_any;
11770 static boolean CheckElementChangeExt(int x, int y,
11772 int trigger_element,
11774 int trigger_player,
11777 boolean change_done = FALSE;
11780 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11781 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11784 if (Feld[x][y] == EL_BLOCKED)
11786 Blocked2Moving(x, y, &x, &y);
11787 element = Feld[x][y];
11791 /* check if element has already changed */
11792 if (Feld[x][y] != element)
11795 /* check if element has already changed or is about to change after moving */
11796 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11797 Feld[x][y] != element) ||
11799 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11800 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11801 ChangePage[x][y] != -1)))
11806 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11807 trigger_event, recursion_loop_depth, recursion_loop_detected,
11808 recursion_loop_element, EL_NAME(recursion_loop_element));
11811 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11814 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11817 for (p = 0; p < element_info[element].num_change_pages; p++)
11819 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11821 /* check trigger element for all events where the element that is checked
11822 for changing interacts with a directly adjacent element -- this is
11823 different to element changes that affect other elements to change on the
11824 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11825 boolean check_trigger_element =
11826 (trigger_event == CE_TOUCHING_X ||
11827 trigger_event == CE_HITTING_X ||
11828 trigger_event == CE_HIT_BY_X ||
11830 /* this one was forgotten until 3.2.3 */
11831 trigger_event == CE_DIGGING_X);
11834 if (change->can_change_or_has_action &&
11835 change->has_event[trigger_event] &&
11836 change->trigger_side & trigger_side &&
11837 change->trigger_player & trigger_player &&
11838 (!check_trigger_element ||
11839 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11841 change->actual_trigger_element = trigger_element;
11842 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11843 change->actual_trigger_player_bits = trigger_player;
11844 change->actual_trigger_side = trigger_side;
11845 change->actual_trigger_ce_value = CustomValue[x][y];
11846 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11848 /* special case: trigger element not at (x,y) position for some events */
11849 if (check_trigger_element)
11861 { 0, 0 }, { 0, 0 }, { 0, 0 },
11865 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11866 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11868 change->actual_trigger_ce_value = CustomValue[xx][yy];
11869 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11872 if (change->can_change && !change_done)
11874 ChangeDelay[x][y] = 1;
11875 ChangeEvent[x][y] = trigger_event;
11877 HandleElementChange(x, y, p);
11879 change_done = TRUE;
11881 #if USE_NEW_DELAYED_ACTION
11882 else if (change->has_action)
11884 ExecuteCustomElementAction(x, y, element, p);
11885 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11888 if (change->has_action)
11890 ExecuteCustomElementAction(x, y, element, p);
11891 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11897 RECURSION_LOOP_DETECTION_END();
11899 return change_done;
11902 static void PlayPlayerSound(struct PlayerInfo *player)
11904 int jx = player->jx, jy = player->jy;
11905 int sound_element = player->artwork_element;
11906 int last_action = player->last_action_waiting;
11907 int action = player->action_waiting;
11909 if (player->is_waiting)
11911 if (action != last_action)
11912 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11914 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11918 if (action != last_action)
11919 StopSound(element_info[sound_element].sound[last_action]);
11921 if (last_action == ACTION_SLEEPING)
11922 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11926 static void PlayAllPlayersSound()
11930 for (i = 0; i < MAX_PLAYERS; i++)
11931 if (stored_player[i].active)
11932 PlayPlayerSound(&stored_player[i]);
11935 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11937 boolean last_waiting = player->is_waiting;
11938 int move_dir = player->MovDir;
11940 player->dir_waiting = move_dir;
11941 player->last_action_waiting = player->action_waiting;
11945 if (!last_waiting) /* not waiting -> waiting */
11947 player->is_waiting = TRUE;
11949 player->frame_counter_bored =
11951 game.player_boring_delay_fixed +
11952 GetSimpleRandom(game.player_boring_delay_random);
11953 player->frame_counter_sleeping =
11955 game.player_sleeping_delay_fixed +
11956 GetSimpleRandom(game.player_sleeping_delay_random);
11958 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11961 if (game.player_sleeping_delay_fixed +
11962 game.player_sleeping_delay_random > 0 &&
11963 player->anim_delay_counter == 0 &&
11964 player->post_delay_counter == 0 &&
11965 FrameCounter >= player->frame_counter_sleeping)
11966 player->is_sleeping = TRUE;
11967 else if (game.player_boring_delay_fixed +
11968 game.player_boring_delay_random > 0 &&
11969 FrameCounter >= player->frame_counter_bored)
11970 player->is_bored = TRUE;
11972 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11973 player->is_bored ? ACTION_BORING :
11976 if (player->is_sleeping && player->use_murphy)
11978 /* special case for sleeping Murphy when leaning against non-free tile */
11980 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11981 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11982 !IS_MOVING(player->jx - 1, player->jy)))
11983 move_dir = MV_LEFT;
11984 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11985 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11986 !IS_MOVING(player->jx + 1, player->jy)))
11987 move_dir = MV_RIGHT;
11989 player->is_sleeping = FALSE;
11991 player->dir_waiting = move_dir;
11994 if (player->is_sleeping)
11996 if (player->num_special_action_sleeping > 0)
11998 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12000 int last_special_action = player->special_action_sleeping;
12001 int num_special_action = player->num_special_action_sleeping;
12002 int special_action =
12003 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12004 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12005 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12006 last_special_action + 1 : ACTION_SLEEPING);
12007 int special_graphic =
12008 el_act_dir2img(player->artwork_element, special_action, move_dir);
12010 player->anim_delay_counter =
12011 graphic_info[special_graphic].anim_delay_fixed +
12012 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12013 player->post_delay_counter =
12014 graphic_info[special_graphic].post_delay_fixed +
12015 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12017 player->special_action_sleeping = special_action;
12020 if (player->anim_delay_counter > 0)
12022 player->action_waiting = player->special_action_sleeping;
12023 player->anim_delay_counter--;
12025 else if (player->post_delay_counter > 0)
12027 player->post_delay_counter--;
12031 else if (player->is_bored)
12033 if (player->num_special_action_bored > 0)
12035 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12037 int special_action =
12038 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12039 int special_graphic =
12040 el_act_dir2img(player->artwork_element, special_action, move_dir);
12042 player->anim_delay_counter =
12043 graphic_info[special_graphic].anim_delay_fixed +
12044 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12045 player->post_delay_counter =
12046 graphic_info[special_graphic].post_delay_fixed +
12047 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12049 player->special_action_bored = special_action;
12052 if (player->anim_delay_counter > 0)
12054 player->action_waiting = player->special_action_bored;
12055 player->anim_delay_counter--;
12057 else if (player->post_delay_counter > 0)
12059 player->post_delay_counter--;
12064 else if (last_waiting) /* waiting -> not waiting */
12066 player->is_waiting = FALSE;
12067 player->is_bored = FALSE;
12068 player->is_sleeping = FALSE;
12070 player->frame_counter_bored = -1;
12071 player->frame_counter_sleeping = -1;
12073 player->anim_delay_counter = 0;
12074 player->post_delay_counter = 0;
12076 player->dir_waiting = player->MovDir;
12077 player->action_waiting = ACTION_DEFAULT;
12079 player->special_action_bored = ACTION_DEFAULT;
12080 player->special_action_sleeping = ACTION_DEFAULT;
12084 static void CheckSingleStepMode(struct PlayerInfo *player)
12086 if (tape.single_step && tape.recording && !tape.pausing)
12088 /* as it is called "single step mode", just return to pause mode when the
12089 player stopped moving after one tile (or never starts moving at all) */
12090 if (!player->is_moving && !player->is_pushing)
12092 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12093 SnapField(player, 0, 0); /* stop snapping */
12098 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12100 int left = player_action & JOY_LEFT;
12101 int right = player_action & JOY_RIGHT;
12102 int up = player_action & JOY_UP;
12103 int down = player_action & JOY_DOWN;
12104 int button1 = player_action & JOY_BUTTON_1;
12105 int button2 = player_action & JOY_BUTTON_2;
12106 int dx = (left ? -1 : right ? 1 : 0);
12107 int dy = (up ? -1 : down ? 1 : 0);
12109 if (!player->active || tape.pausing)
12115 SnapField(player, dx, dy);
12119 DropElement(player);
12121 MovePlayer(player, dx, dy);
12124 CheckSingleStepMode(player);
12126 SetPlayerWaiting(player, FALSE);
12128 return player_action;
12132 /* no actions for this player (no input at player's configured device) */
12134 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12135 SnapField(player, 0, 0);
12136 CheckGravityMovementWhenNotMoving(player);
12138 if (player->MovPos == 0)
12139 SetPlayerWaiting(player, TRUE);
12141 if (player->MovPos == 0) /* needed for tape.playing */
12142 player->is_moving = FALSE;
12144 player->is_dropping = FALSE;
12145 player->is_dropping_pressed = FALSE;
12146 player->drop_pressed_delay = 0;
12148 CheckSingleStepMode(player);
12154 static void CheckLevelTime()
12158 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12159 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12161 if (level.native_em_level->lev->home == 0) /* all players at home */
12163 PlayerWins(local_player);
12165 AllPlayersGone = TRUE;
12167 level.native_em_level->lev->home = -1;
12170 if (level.native_em_level->ply[0]->alive == 0 &&
12171 level.native_em_level->ply[1]->alive == 0 &&
12172 level.native_em_level->ply[2]->alive == 0 &&
12173 level.native_em_level->ply[3]->alive == 0) /* all dead */
12174 AllPlayersGone = TRUE;
12176 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12178 if (game_sp.LevelSolved &&
12179 !game_sp.GameOver) /* game won */
12181 PlayerWins(local_player);
12183 game_sp.GameOver = TRUE;
12185 AllPlayersGone = TRUE;
12188 if (game_sp.GameOver) /* game lost */
12189 AllPlayersGone = TRUE;
12192 if (TimeFrames >= FRAMES_PER_SECOND)
12197 for (i = 0; i < MAX_PLAYERS; i++)
12199 struct PlayerInfo *player = &stored_player[i];
12201 if (SHIELD_ON(player))
12203 player->shield_normal_time_left--;
12205 if (player->shield_deadly_time_left > 0)
12206 player->shield_deadly_time_left--;
12210 if (!local_player->LevelSolved && !level.use_step_counter)
12218 if (TimeLeft <= 10 && setup.time_limit)
12219 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12222 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12223 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12225 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12227 /* (already called by UpdateAndDisplayGameControlValues() below) */
12228 // DisplayGameControlValues();
12230 DrawGameValue_Time(TimeLeft);
12233 if (!TimeLeft && setup.time_limit)
12235 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12236 level.native_em_level->lev->killed_out_of_time = TRUE;
12238 for (i = 0; i < MAX_PLAYERS; i++)
12239 KillPlayer(&stored_player[i]);
12243 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12245 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12247 /* (already called by UpdateAndDisplayGameControlValues() below) */
12248 // DisplayGameControlValues();
12251 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12252 DrawGameValue_Time(TimePlayed);
12255 level.native_em_level->lev->time =
12256 (game.no_time_limit ? TimePlayed : TimeLeft);
12259 if (tape.recording || tape.playing)
12260 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12264 UpdateAndDisplayGameControlValues();
12266 UpdateGameDoorValues();
12267 DrawGameDoorValues();
12271 void AdvanceFrameAndPlayerCounters(int player_nr)
12275 /* advance frame counters (global frame counter and time frame counter) */
12279 /* advance player counters (counters for move delay, move animation etc.) */
12280 for (i = 0; i < MAX_PLAYERS; i++)
12282 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12283 int move_delay_value = stored_player[i].move_delay_value;
12284 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12286 if (!advance_player_counters) /* not all players may be affected */
12289 #if USE_NEW_PLAYER_ANIM
12290 if (move_frames == 0) /* less than one move per game frame */
12292 int stepsize = TILEX / move_delay_value;
12293 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12294 int count = (stored_player[i].is_moving ?
12295 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12297 if (count % delay == 0)
12302 stored_player[i].Frame += move_frames;
12304 if (stored_player[i].MovPos != 0)
12305 stored_player[i].StepFrame += move_frames;
12307 if (stored_player[i].move_delay > 0)
12308 stored_player[i].move_delay--;
12310 /* due to bugs in previous versions, counter must count up, not down */
12311 if (stored_player[i].push_delay != -1)
12312 stored_player[i].push_delay++;
12314 if (stored_player[i].drop_delay > 0)
12315 stored_player[i].drop_delay--;
12317 if (stored_player[i].is_dropping_pressed)
12318 stored_player[i].drop_pressed_delay++;
12322 void StartGameActions(boolean init_network_game, boolean record_tape,
12325 unsigned int new_random_seed = InitRND(random_seed);
12328 TapeStartRecording(new_random_seed);
12330 #if defined(NETWORK_AVALIABLE)
12331 if (init_network_game)
12333 SendToServer_StartPlaying();
12344 static unsigned int game_frame_delay = 0;
12345 unsigned int game_frame_delay_value;
12346 byte *recorded_player_action;
12347 byte summarized_player_action = 0;
12348 byte tape_action[MAX_PLAYERS];
12351 /* detect endless loops, caused by custom element programming */
12352 if (recursion_loop_detected && recursion_loop_depth == 0)
12354 char *message = getStringCat3("Internal Error! Element ",
12355 EL_NAME(recursion_loop_element),
12356 " caused endless loop! Quit the game?");
12358 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12359 EL_NAME(recursion_loop_element));
12361 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12363 recursion_loop_detected = FALSE; /* if game should be continued */
12370 if (game.restart_level)
12371 StartGameActions(options.network, setup.autorecord, level.random_seed);
12373 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12374 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12376 if (level.native_em_level->lev->home == 0) /* all players at home */
12378 PlayerWins(local_player);
12380 AllPlayersGone = TRUE;
12382 level.native_em_level->lev->home = -1;
12385 if (level.native_em_level->ply[0]->alive == 0 &&
12386 level.native_em_level->ply[1]->alive == 0 &&
12387 level.native_em_level->ply[2]->alive == 0 &&
12388 level.native_em_level->ply[3]->alive == 0) /* all dead */
12389 AllPlayersGone = TRUE;
12391 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12393 if (game_sp.LevelSolved &&
12394 !game_sp.GameOver) /* game won */
12396 PlayerWins(local_player);
12398 game_sp.GameOver = TRUE;
12400 AllPlayersGone = TRUE;
12403 if (game_sp.GameOver) /* game lost */
12404 AllPlayersGone = TRUE;
12407 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12410 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12413 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12416 game_frame_delay_value =
12417 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12419 if (tape.playing && tape.warp_forward && !tape.pausing)
12420 game_frame_delay_value = 0;
12422 /* ---------- main game synchronization point ---------- */
12424 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12426 if (network_playing && !network_player_action_received)
12428 /* try to get network player actions in time */
12430 #if defined(NETWORK_AVALIABLE)
12431 /* last chance to get network player actions without main loop delay */
12432 HandleNetworking();
12435 /* game was quit by network peer */
12436 if (game_status != GAME_MODE_PLAYING)
12439 if (!network_player_action_received)
12440 return; /* failed to get network player actions in time */
12442 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12448 /* at this point we know that we really continue executing the game */
12450 network_player_action_received = FALSE;
12452 /* when playing tape, read previously recorded player input from tape data */
12453 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12456 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12461 if (tape.set_centered_player)
12463 game.centered_player_nr_next = tape.centered_player_nr_next;
12464 game.set_centered_player = TRUE;
12467 for (i = 0; i < MAX_PLAYERS; i++)
12469 summarized_player_action |= stored_player[i].action;
12472 if (!network_playing && (game.team_mode || tape.playing))
12473 stored_player[i].effective_action = stored_player[i].action;
12475 if (!network_playing)
12476 stored_player[i].effective_action = stored_player[i].action;
12480 #if defined(NETWORK_AVALIABLE)
12481 if (network_playing)
12482 SendToServer_MovePlayer(summarized_player_action);
12485 if (!options.network && !game.team_mode)
12486 local_player->effective_action = summarized_player_action;
12488 if (tape.recording &&
12490 setup.input_on_focus &&
12491 game.centered_player_nr != -1)
12493 for (i = 0; i < MAX_PLAYERS; i++)
12494 stored_player[i].effective_action =
12495 (i == game.centered_player_nr ? summarized_player_action : 0);
12498 if (recorded_player_action != NULL)
12499 for (i = 0; i < MAX_PLAYERS; i++)
12500 stored_player[i].effective_action = recorded_player_action[i];
12502 for (i = 0; i < MAX_PLAYERS; i++)
12504 tape_action[i] = stored_player[i].effective_action;
12507 /* (this may happen in the RND game engine if a player was not present on
12508 the playfield on level start, but appeared later from a custom element */
12509 if (tape.recording &&
12512 !tape.player_participates[i])
12513 tape.player_participates[i] = TRUE;
12515 /* (this can only happen in the R'n'D game engine) */
12516 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12517 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12521 /* only record actions from input devices, but not programmed actions */
12522 if (tape.recording)
12523 TapeRecordAction(tape_action);
12525 #if USE_NEW_PLAYER_ASSIGNMENTS
12527 if (game.team_mode)
12530 byte mapped_action[MAX_PLAYERS];
12532 #if DEBUG_PLAYER_ACTIONS
12534 for (i = 0; i < MAX_PLAYERS; i++)
12535 printf(" %d, ", stored_player[i].effective_action);
12538 for (i = 0; i < MAX_PLAYERS; i++)
12539 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12541 for (i = 0; i < MAX_PLAYERS; i++)
12542 stored_player[i].effective_action = mapped_action[i];
12544 #if DEBUG_PLAYER_ACTIONS
12546 for (i = 0; i < MAX_PLAYERS; i++)
12547 printf(" %d, ", stored_player[i].effective_action);
12551 #if DEBUG_PLAYER_ACTIONS
12555 for (i = 0; i < MAX_PLAYERS; i++)
12556 printf(" %d, ", stored_player[i].effective_action);
12563 printf("::: summarized_player_action == %d\n",
12564 local_player->effective_action);
12571 #if DEBUG_INIT_PLAYER
12574 printf("Player status (final):\n");
12576 for (i = 0; i < MAX_PLAYERS; i++)
12578 struct PlayerInfo *player = &stored_player[i];
12580 printf("- player %d: present == %d, connected == %d, active == %d",
12586 if (local_player == player)
12587 printf(" (local player)");
12597 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12599 GameActions_EM_Main();
12601 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12603 GameActions_SP_Main();
12611 void GameActions_EM_Main()
12613 byte effective_action[MAX_PLAYERS];
12614 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12617 for (i = 0; i < MAX_PLAYERS; i++)
12618 effective_action[i] = stored_player[i].effective_action;
12620 GameActions_EM(effective_action, warp_mode);
12624 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12627 void GameActions_SP_Main()
12629 byte effective_action[MAX_PLAYERS];
12630 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12633 for (i = 0; i < MAX_PLAYERS; i++)
12634 effective_action[i] = stored_player[i].effective_action;
12636 GameActions_SP(effective_action, warp_mode);
12640 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12643 void GameActions_RND()
12645 int magic_wall_x = 0, magic_wall_y = 0;
12646 int i, x, y, element, graphic;
12648 InitPlayfieldScanModeVars();
12650 #if USE_ONE_MORE_CHANGE_PER_FRAME
12651 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12653 SCAN_PLAYFIELD(x, y)
12655 ChangeCount[x][y] = 0;
12656 ChangeEvent[x][y] = -1;
12661 if (game.set_centered_player)
12663 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12665 /* switching to "all players" only possible if all players fit to screen */
12666 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12668 game.centered_player_nr_next = game.centered_player_nr;
12669 game.set_centered_player = FALSE;
12672 /* do not switch focus to non-existing (or non-active) player */
12673 if (game.centered_player_nr_next >= 0 &&
12674 !stored_player[game.centered_player_nr_next].active)
12676 game.centered_player_nr_next = game.centered_player_nr;
12677 game.set_centered_player = FALSE;
12681 if (game.set_centered_player &&
12682 ScreenMovPos == 0) /* screen currently aligned at tile position */
12686 if (game.centered_player_nr_next == -1)
12688 setScreenCenteredToAllPlayers(&sx, &sy);
12692 sx = stored_player[game.centered_player_nr_next].jx;
12693 sy = stored_player[game.centered_player_nr_next].jy;
12696 game.centered_player_nr = game.centered_player_nr_next;
12697 game.set_centered_player = FALSE;
12699 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12700 DrawGameDoorValues();
12703 for (i = 0; i < MAX_PLAYERS; i++)
12705 int actual_player_action = stored_player[i].effective_action;
12708 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12709 - rnd_equinox_tetrachloride 048
12710 - rnd_equinox_tetrachloride_ii 096
12711 - rnd_emanuel_schmieg 002
12712 - doctor_sloan_ww 001, 020
12714 if (stored_player[i].MovPos == 0)
12715 CheckGravityMovement(&stored_player[i]);
12718 /* overwrite programmed action with tape action */
12719 if (stored_player[i].programmed_action)
12720 actual_player_action = stored_player[i].programmed_action;
12722 PlayerActions(&stored_player[i], actual_player_action);
12724 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12727 ScrollScreen(NULL, SCROLL_GO_ON);
12729 /* for backwards compatibility, the following code emulates a fixed bug that
12730 occured when pushing elements (causing elements that just made their last
12731 pushing step to already (if possible) make their first falling step in the
12732 same game frame, which is bad); this code is also needed to use the famous
12733 "spring push bug" which is used in older levels and might be wanted to be
12734 used also in newer levels, but in this case the buggy pushing code is only
12735 affecting the "spring" element and no other elements */
12737 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12739 for (i = 0; i < MAX_PLAYERS; i++)
12741 struct PlayerInfo *player = &stored_player[i];
12742 int x = player->jx;
12743 int y = player->jy;
12745 if (player->active && player->is_pushing && player->is_moving &&
12747 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12748 Feld[x][y] == EL_SPRING))
12750 ContinueMoving(x, y);
12752 /* continue moving after pushing (this is actually a bug) */
12753 if (!IS_MOVING(x, y))
12754 Stop[x][y] = FALSE;
12760 debug_print_timestamp(0, "start main loop profiling");
12763 SCAN_PLAYFIELD(x, y)
12765 ChangeCount[x][y] = 0;
12766 ChangeEvent[x][y] = -1;
12768 /* this must be handled before main playfield loop */
12769 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12772 if (MovDelay[x][y] <= 0)
12776 #if USE_NEW_SNAP_DELAY
12777 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12780 if (MovDelay[x][y] <= 0)
12783 TEST_DrawLevelField(x, y);
12785 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12791 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12793 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12794 printf("GameActions(): This should never happen!\n");
12796 ChangePage[x][y] = -1;
12800 Stop[x][y] = FALSE;
12801 if (WasJustMoving[x][y] > 0)
12802 WasJustMoving[x][y]--;
12803 if (WasJustFalling[x][y] > 0)
12804 WasJustFalling[x][y]--;
12805 if (CheckCollision[x][y] > 0)
12806 CheckCollision[x][y]--;
12807 if (CheckImpact[x][y] > 0)
12808 CheckImpact[x][y]--;
12812 /* reset finished pushing action (not done in ContinueMoving() to allow
12813 continuous pushing animation for elements with zero push delay) */
12814 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12816 ResetGfxAnimation(x, y);
12817 TEST_DrawLevelField(x, y);
12821 if (IS_BLOCKED(x, y))
12825 Blocked2Moving(x, y, &oldx, &oldy);
12826 if (!IS_MOVING(oldx, oldy))
12828 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12829 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12830 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12831 printf("GameActions(): This should never happen!\n");
12838 debug_print_timestamp(0, "- time for pre-main loop:");
12841 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12842 SCAN_PLAYFIELD(x, y)
12844 element = Feld[x][y];
12845 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12850 int element2 = element;
12851 int graphic2 = graphic;
12853 int element2 = Feld[x][y];
12854 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12856 int last_gfx_frame = GfxFrame[x][y];
12858 if (graphic_info[graphic2].anim_global_sync)
12859 GfxFrame[x][y] = FrameCounter;
12860 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12861 GfxFrame[x][y] = CustomValue[x][y];
12862 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12863 GfxFrame[x][y] = element_info[element2].collect_score;
12864 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12865 GfxFrame[x][y] = ChangeDelay[x][y];
12867 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12868 DrawLevelGraphicAnimation(x, y, graphic2);
12871 ResetGfxFrame(x, y, TRUE);
12875 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12876 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12877 ResetRandomAnimationValue(x, y);
12881 SetRandomAnimationValue(x, y);
12885 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12888 #endif // -------------------- !!! TEST ONLY !!! --------------------
12891 debug_print_timestamp(0, "- time for TEST loop: -->");
12894 SCAN_PLAYFIELD(x, y)
12896 element = Feld[x][y];
12897 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12899 ResetGfxFrame(x, y, TRUE);
12901 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12902 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12903 ResetRandomAnimationValue(x, y);
12905 SetRandomAnimationValue(x, y);
12907 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12909 if (IS_INACTIVE(element))
12911 if (IS_ANIMATED(graphic))
12912 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12917 /* this may take place after moving, so 'element' may have changed */
12918 if (IS_CHANGING(x, y) &&
12919 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12921 int page = element_info[element].event_page_nr[CE_DELAY];
12924 HandleElementChange(x, y, page);
12926 if (CAN_CHANGE(element))
12927 HandleElementChange(x, y, page);
12929 if (HAS_ACTION(element))
12930 ExecuteCustomElementAction(x, y, element, page);
12933 element = Feld[x][y];
12934 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12937 #if 0 // ---------------------------------------------------------------------
12939 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12943 element = Feld[x][y];
12944 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12946 if (IS_ANIMATED(graphic) &&
12947 !IS_MOVING(x, y) &&
12949 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12951 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12952 TEST_DrawTwinkleOnField(x, y);
12954 else if (IS_MOVING(x, y))
12955 ContinueMoving(x, y);
12962 case EL_EM_EXIT_OPEN:
12963 case EL_SP_EXIT_OPEN:
12964 case EL_STEEL_EXIT_OPEN:
12965 case EL_EM_STEEL_EXIT_OPEN:
12966 case EL_SP_TERMINAL:
12967 case EL_SP_TERMINAL_ACTIVE:
12968 case EL_EXTRA_TIME:
12969 case EL_SHIELD_NORMAL:
12970 case EL_SHIELD_DEADLY:
12971 if (IS_ANIMATED(graphic))
12972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12975 case EL_DYNAMITE_ACTIVE:
12976 case EL_EM_DYNAMITE_ACTIVE:
12977 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12978 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12979 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12980 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12981 case EL_SP_DISK_RED_ACTIVE:
12982 CheckDynamite(x, y);
12985 case EL_AMOEBA_GROWING:
12986 AmoebeWaechst(x, y);
12989 case EL_AMOEBA_SHRINKING:
12990 AmoebaDisappearing(x, y);
12993 #if !USE_NEW_AMOEBA_CODE
12994 case EL_AMOEBA_WET:
12995 case EL_AMOEBA_DRY:
12996 case EL_AMOEBA_FULL:
12998 case EL_EMC_DRIPPER:
12999 AmoebeAbleger(x, y);
13003 case EL_GAME_OF_LIFE:
13008 case EL_EXIT_CLOSED:
13012 case EL_EM_EXIT_CLOSED:
13016 case EL_STEEL_EXIT_CLOSED:
13017 CheckExitSteel(x, y);
13020 case EL_EM_STEEL_EXIT_CLOSED:
13021 CheckExitSteelEM(x, y);
13024 case EL_SP_EXIT_CLOSED:
13028 case EL_EXPANDABLE_WALL_GROWING:
13029 case EL_EXPANDABLE_STEELWALL_GROWING:
13030 MauerWaechst(x, y);
13033 case EL_EXPANDABLE_WALL:
13034 case EL_EXPANDABLE_WALL_HORIZONTAL:
13035 case EL_EXPANDABLE_WALL_VERTICAL:
13036 case EL_EXPANDABLE_WALL_ANY:
13037 case EL_BD_EXPANDABLE_WALL:
13038 MauerAbleger(x, y);
13041 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
13042 case EL_EXPANDABLE_STEELWALL_VERTICAL:
13043 case EL_EXPANDABLE_STEELWALL_ANY:
13044 MauerAblegerStahl(x, y);
13048 CheckForDragon(x, y);
13054 case EL_ELEMENT_SNAPPING:
13055 case EL_DIAGONAL_SHRINKING:
13056 case EL_DIAGONAL_GROWING:
13059 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13061 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13066 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13067 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13072 #else // ---------------------------------------------------------------------
13074 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13078 element = Feld[x][y];
13079 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13081 if (IS_ANIMATED(graphic) &&
13082 !IS_MOVING(x, y) &&
13084 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13086 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13087 TEST_DrawTwinkleOnField(x, y);
13089 else if ((element == EL_ACID ||
13090 element == EL_EXIT_OPEN ||
13091 element == EL_EM_EXIT_OPEN ||
13092 element == EL_SP_EXIT_OPEN ||
13093 element == EL_STEEL_EXIT_OPEN ||
13094 element == EL_EM_STEEL_EXIT_OPEN ||
13095 element == EL_SP_TERMINAL ||
13096 element == EL_SP_TERMINAL_ACTIVE ||
13097 element == EL_EXTRA_TIME ||
13098 element == EL_SHIELD_NORMAL ||
13099 element == EL_SHIELD_DEADLY) &&
13100 IS_ANIMATED(graphic))
13101 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13102 else if (IS_MOVING(x, y))
13103 ContinueMoving(x, y);
13104 else if (IS_ACTIVE_BOMB(element))
13105 CheckDynamite(x, y);
13106 else if (element == EL_AMOEBA_GROWING)
13107 AmoebeWaechst(x, y);
13108 else if (element == EL_AMOEBA_SHRINKING)
13109 AmoebaDisappearing(x, y);
13111 #if !USE_NEW_AMOEBA_CODE
13112 else if (IS_AMOEBALIVE(element))
13113 AmoebeAbleger(x, y);
13116 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13118 else if (element == EL_EXIT_CLOSED)
13120 else if (element == EL_EM_EXIT_CLOSED)
13122 else if (element == EL_STEEL_EXIT_CLOSED)
13123 CheckExitSteel(x, y);
13124 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13125 CheckExitSteelEM(x, y);
13126 else if (element == EL_SP_EXIT_CLOSED)
13128 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13129 element == EL_EXPANDABLE_STEELWALL_GROWING)
13130 MauerWaechst(x, y);
13131 else if (element == EL_EXPANDABLE_WALL ||
13132 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13133 element == EL_EXPANDABLE_WALL_VERTICAL ||
13134 element == EL_EXPANDABLE_WALL_ANY ||
13135 element == EL_BD_EXPANDABLE_WALL)
13136 MauerAbleger(x, y);
13137 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13138 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13139 element == EL_EXPANDABLE_STEELWALL_ANY)
13140 MauerAblegerStahl(x, y);
13141 else if (element == EL_FLAMES)
13142 CheckForDragon(x, y);
13143 else if (element == EL_EXPLOSION)
13144 ; /* drawing of correct explosion animation is handled separately */
13145 else if (element == EL_ELEMENT_SNAPPING ||
13146 element == EL_DIAGONAL_SHRINKING ||
13147 element == EL_DIAGONAL_GROWING)
13149 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13151 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13153 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13154 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13156 #endif // ---------------------------------------------------------------------
13158 if (IS_BELT_ACTIVE(element))
13159 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13161 if (game.magic_wall_active)
13163 int jx = local_player->jx, jy = local_player->jy;
13165 /* play the element sound at the position nearest to the player */
13166 if ((element == EL_MAGIC_WALL_FULL ||
13167 element == EL_MAGIC_WALL_ACTIVE ||
13168 element == EL_MAGIC_WALL_EMPTYING ||
13169 element == EL_BD_MAGIC_WALL_FULL ||
13170 element == EL_BD_MAGIC_WALL_ACTIVE ||
13171 element == EL_BD_MAGIC_WALL_EMPTYING ||
13172 element == EL_DC_MAGIC_WALL_FULL ||
13173 element == EL_DC_MAGIC_WALL_ACTIVE ||
13174 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13175 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13184 debug_print_timestamp(0, "- time for MAIN loop: -->");
13187 #if USE_NEW_AMOEBA_CODE
13188 /* new experimental amoeba growth stuff */
13189 if (!(FrameCounter % 8))
13191 static unsigned int random = 1684108901;
13193 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13195 x = RND(lev_fieldx);
13196 y = RND(lev_fieldy);
13197 element = Feld[x][y];
13199 if (!IS_PLAYER(x,y) &&
13200 (element == EL_EMPTY ||
13201 CAN_GROW_INTO(element) ||
13202 element == EL_QUICKSAND_EMPTY ||
13203 element == EL_QUICKSAND_FAST_EMPTY ||
13204 element == EL_ACID_SPLASH_LEFT ||
13205 element == EL_ACID_SPLASH_RIGHT))
13207 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13208 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13209 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13210 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13211 Feld[x][y] = EL_AMOEBA_DROP;
13214 random = random * 129 + 1;
13220 if (game.explosions_delayed)
13223 game.explosions_delayed = FALSE;
13225 SCAN_PLAYFIELD(x, y)
13227 element = Feld[x][y];
13229 if (ExplodeField[x][y])
13230 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13231 else if (element == EL_EXPLOSION)
13232 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13234 ExplodeField[x][y] = EX_TYPE_NONE;
13237 game.explosions_delayed = TRUE;
13240 if (game.magic_wall_active)
13242 if (!(game.magic_wall_time_left % 4))
13244 int element = Feld[magic_wall_x][magic_wall_y];
13246 if (element == EL_BD_MAGIC_WALL_FULL ||
13247 element == EL_BD_MAGIC_WALL_ACTIVE ||
13248 element == EL_BD_MAGIC_WALL_EMPTYING)
13249 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13250 else if (element == EL_DC_MAGIC_WALL_FULL ||
13251 element == EL_DC_MAGIC_WALL_ACTIVE ||
13252 element == EL_DC_MAGIC_WALL_EMPTYING)
13253 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13255 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13258 if (game.magic_wall_time_left > 0)
13260 game.magic_wall_time_left--;
13262 if (!game.magic_wall_time_left)
13264 SCAN_PLAYFIELD(x, y)
13266 element = Feld[x][y];
13268 if (element == EL_MAGIC_WALL_ACTIVE ||
13269 element == EL_MAGIC_WALL_FULL)
13271 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13272 TEST_DrawLevelField(x, y);
13274 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13275 element == EL_BD_MAGIC_WALL_FULL)
13277 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13278 TEST_DrawLevelField(x, y);
13280 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13281 element == EL_DC_MAGIC_WALL_FULL)
13283 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13284 TEST_DrawLevelField(x, y);
13288 game.magic_wall_active = FALSE;
13293 if (game.light_time_left > 0)
13295 game.light_time_left--;
13297 if (game.light_time_left == 0)
13298 RedrawAllLightSwitchesAndInvisibleElements();
13301 if (game.timegate_time_left > 0)
13303 game.timegate_time_left--;
13305 if (game.timegate_time_left == 0)
13306 CloseAllOpenTimegates();
13309 if (game.lenses_time_left > 0)
13311 game.lenses_time_left--;
13313 if (game.lenses_time_left == 0)
13314 RedrawAllInvisibleElementsForLenses();
13317 if (game.magnify_time_left > 0)
13319 game.magnify_time_left--;
13321 if (game.magnify_time_left == 0)
13322 RedrawAllInvisibleElementsForMagnifier();
13325 for (i = 0; i < MAX_PLAYERS; i++)
13327 struct PlayerInfo *player = &stored_player[i];
13329 if (SHIELD_ON(player))
13331 if (player->shield_deadly_time_left)
13332 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13333 else if (player->shield_normal_time_left)
13334 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13338 #if USE_DELAYED_GFX_REDRAW
13339 SCAN_PLAYFIELD(x, y)
13342 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13344 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13345 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13348 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13349 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13351 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13352 DrawLevelField(x, y);
13354 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13355 DrawLevelFieldCrumbled(x, y);
13357 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13358 DrawLevelFieldCrumbledNeighbours(x, y);
13360 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13361 DrawTwinkleOnField(x, y);
13364 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13371 PlayAllPlayersSound();
13373 if (options.debug) /* calculate frames per second */
13375 static unsigned int fps_counter = 0;
13376 static int fps_frames = 0;
13377 unsigned int fps_delay_ms = Counter() - fps_counter;
13381 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13383 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13386 fps_counter = Counter();
13389 redraw_mask |= REDRAW_FPS;
13392 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13394 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13396 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13398 local_player->show_envelope = 0;
13402 debug_print_timestamp(0, "stop main loop profiling ");
13403 printf("----------------------------------------------------------\n");
13406 /* use random number generator in every frame to make it less predictable */
13407 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13411 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13413 int min_x = x, min_y = y, max_x = x, max_y = y;
13416 for (i = 0; i < MAX_PLAYERS; i++)
13418 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13420 if (!stored_player[i].active || &stored_player[i] == player)
13423 min_x = MIN(min_x, jx);
13424 min_y = MIN(min_y, jy);
13425 max_x = MAX(max_x, jx);
13426 max_y = MAX(max_y, jy);
13429 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13432 static boolean AllPlayersInVisibleScreen()
13436 for (i = 0; i < MAX_PLAYERS; i++)
13438 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13440 if (!stored_player[i].active)
13443 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13450 void ScrollLevel(int dx, int dy)
13453 /* (directly solved in BlitBitmap() now) */
13454 static Bitmap *bitmap_db_field2 = NULL;
13455 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13462 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13463 /* only horizontal XOR vertical scroll direction allowed */
13464 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13469 /* (directly solved in BlitBitmap() now) */
13470 if (bitmap_db_field2 == NULL)
13471 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13473 /* needed when blitting directly to same bitmap -- should not be needed with
13474 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13475 BlitBitmap(drawto_field, bitmap_db_field2,
13476 FX + TILEX * (dx == -1) - softscroll_offset,
13477 FY + TILEY * (dy == -1) - softscroll_offset,
13478 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13479 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13480 FX + TILEX * (dx == 1) - softscroll_offset,
13481 FY + TILEY * (dy == 1) - softscroll_offset);
13482 BlitBitmap(bitmap_db_field2, drawto_field,
13483 FX + TILEX * (dx == 1) - softscroll_offset,
13484 FY + TILEY * (dy == 1) - softscroll_offset,
13485 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13486 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13487 FX + TILEX * (dx == 1) - softscroll_offset,
13488 FY + TILEY * (dy == 1) - softscroll_offset);
13493 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13494 int xsize = (BX2 - BX1 + 1);
13495 int ysize = (BY2 - BY1 + 1);
13496 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13497 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13498 int step = (start < end ? +1 : -1);
13500 for (i = start; i != end; i += step)
13502 BlitBitmap(drawto_field, drawto_field,
13503 FX + TILEX * (dx != 0 ? i + step : 0),
13504 FY + TILEY * (dy != 0 ? i + step : 0),
13505 TILEX * (dx != 0 ? 1 : xsize),
13506 TILEY * (dy != 0 ? 1 : ysize),
13507 FX + TILEX * (dx != 0 ? i : 0),
13508 FY + TILEY * (dy != 0 ? i : 0));
13515 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13517 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13521 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13523 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13528 BlitBitmap(drawto_field, drawto_field,
13529 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13530 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13531 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13532 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13533 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13534 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13536 BlitBitmap(drawto_field, drawto_field,
13537 FX + TILEX * (dx == -1) - softscroll_offset,
13538 FY + TILEY * (dy == -1) - softscroll_offset,
13539 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13540 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13541 FX + TILEX * (dx == 1) - softscroll_offset,
13542 FY + TILEY * (dy == 1) - softscroll_offset);
13550 x = (dx == 1 ? BX1 : BX2);
13551 for (y = BY1; y <= BY2; y++)
13552 DrawScreenField(x, y);
13557 y = (dy == 1 ? BY1 : BY2);
13558 for (x = BX1; x <= BX2; x++)
13559 DrawScreenField(x, y);
13562 redraw_mask |= REDRAW_FIELD;
13565 static boolean canFallDown(struct PlayerInfo *player)
13567 int jx = player->jx, jy = player->jy;
13569 return (IN_LEV_FIELD(jx, jy + 1) &&
13570 (IS_FREE(jx, jy + 1) ||
13571 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13572 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13573 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13576 static boolean canPassField(int x, int y, int move_dir)
13578 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13579 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13580 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13581 int nextx = x + dx;
13582 int nexty = y + dy;
13583 int element = Feld[x][y];
13585 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13586 !CAN_MOVE(element) &&
13587 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13588 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13589 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13592 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13594 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13595 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13596 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13600 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13601 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13602 (IS_DIGGABLE(Feld[newx][newy]) ||
13603 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13604 canPassField(newx, newy, move_dir)));
13607 static void CheckGravityMovement(struct PlayerInfo *player)
13609 #if USE_PLAYER_GRAVITY
13610 if (player->gravity && !player->programmed_action)
13612 if (game.gravity && !player->programmed_action)
13615 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13616 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13617 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13618 int jx = player->jx, jy = player->jy;
13619 boolean player_is_moving_to_valid_field =
13620 (!player_is_snapping &&
13621 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13622 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13623 boolean player_can_fall_down = canFallDown(player);
13625 if (player_can_fall_down &&
13626 !player_is_moving_to_valid_field)
13627 player->programmed_action = MV_DOWN;
13631 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13633 return CheckGravityMovement(player);
13635 #if USE_PLAYER_GRAVITY
13636 if (player->gravity && !player->programmed_action)
13638 if (game.gravity && !player->programmed_action)
13641 int jx = player->jx, jy = player->jy;
13642 boolean field_under_player_is_free =
13643 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13644 boolean player_is_standing_on_valid_field =
13645 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13646 (IS_WALKABLE(Feld[jx][jy]) &&
13647 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13649 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13650 player->programmed_action = MV_DOWN;
13655 MovePlayerOneStep()
13656 -----------------------------------------------------------------------------
13657 dx, dy: direction (non-diagonal) to try to move the player to
13658 real_dx, real_dy: direction as read from input device (can be diagonal)
13661 boolean MovePlayerOneStep(struct PlayerInfo *player,
13662 int dx, int dy, int real_dx, int real_dy)
13664 int jx = player->jx, jy = player->jy;
13665 int new_jx = jx + dx, new_jy = jy + dy;
13666 #if !USE_FIXED_DONT_RUN_INTO
13670 boolean player_can_move = !player->cannot_move;
13672 if (!player->active || (!dx && !dy))
13673 return MP_NO_ACTION;
13675 player->MovDir = (dx < 0 ? MV_LEFT :
13676 dx > 0 ? MV_RIGHT :
13678 dy > 0 ? MV_DOWN : MV_NONE);
13680 if (!IN_LEV_FIELD(new_jx, new_jy))
13681 return MP_NO_ACTION;
13683 if (!player_can_move)
13685 if (player->MovPos == 0)
13687 player->is_moving = FALSE;
13688 player->is_digging = FALSE;
13689 player->is_collecting = FALSE;
13690 player->is_snapping = FALSE;
13691 player->is_pushing = FALSE;
13696 if (!options.network && game.centered_player_nr == -1 &&
13697 !AllPlayersInSight(player, new_jx, new_jy))
13698 return MP_NO_ACTION;
13700 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13701 return MP_NO_ACTION;
13704 #if !USE_FIXED_DONT_RUN_INTO
13705 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13707 /* (moved to DigField()) */
13708 if (player_can_move && DONT_RUN_INTO(element))
13710 if (element == EL_ACID && dx == 0 && dy == 1)
13712 SplashAcid(new_jx, new_jy);
13713 Feld[jx][jy] = EL_PLAYER_1;
13714 InitMovingField(jx, jy, MV_DOWN);
13715 Store[jx][jy] = EL_ACID;
13716 ContinueMoving(jx, jy);
13717 BuryPlayer(player);
13720 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13726 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13727 if (can_move != MP_MOVING)
13730 /* check if DigField() has caused relocation of the player */
13731 if (player->jx != jx || player->jy != jy)
13732 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13734 StorePlayer[jx][jy] = 0;
13735 player->last_jx = jx;
13736 player->last_jy = jy;
13737 player->jx = new_jx;
13738 player->jy = new_jy;
13739 StorePlayer[new_jx][new_jy] = player->element_nr;
13741 if (player->move_delay_value_next != -1)
13743 player->move_delay_value = player->move_delay_value_next;
13744 player->move_delay_value_next = -1;
13748 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13750 player->step_counter++;
13752 PlayerVisit[jx][jy] = FrameCounter;
13754 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13755 player->is_moving = TRUE;
13759 /* should better be called in MovePlayer(), but this breaks some tapes */
13760 ScrollPlayer(player, SCROLL_INIT);
13766 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13768 int jx = player->jx, jy = player->jy;
13769 int old_jx = jx, old_jy = jy;
13770 int moved = MP_NO_ACTION;
13772 if (!player->active)
13777 if (player->MovPos == 0)
13779 player->is_moving = FALSE;
13780 player->is_digging = FALSE;
13781 player->is_collecting = FALSE;
13782 player->is_snapping = FALSE;
13783 player->is_pushing = FALSE;
13789 if (player->move_delay > 0)
13792 player->move_delay = -1; /* set to "uninitialized" value */
13794 /* store if player is automatically moved to next field */
13795 player->is_auto_moving = (player->programmed_action != MV_NONE);
13797 /* remove the last programmed player action */
13798 player->programmed_action = 0;
13800 if (player->MovPos)
13802 /* should only happen if pre-1.2 tape recordings are played */
13803 /* this is only for backward compatibility */
13805 int original_move_delay_value = player->move_delay_value;
13808 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13812 /* scroll remaining steps with finest movement resolution */
13813 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13815 while (player->MovPos)
13817 ScrollPlayer(player, SCROLL_GO_ON);
13818 ScrollScreen(NULL, SCROLL_GO_ON);
13820 AdvanceFrameAndPlayerCounters(player->index_nr);
13826 player->move_delay_value = original_move_delay_value;
13829 player->is_active = FALSE;
13831 if (player->last_move_dir & MV_HORIZONTAL)
13833 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13834 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13838 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13839 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13842 #if USE_FIXED_BORDER_RUNNING_GFX
13843 if (!moved && !player->is_active)
13845 player->is_moving = FALSE;
13846 player->is_digging = FALSE;
13847 player->is_collecting = FALSE;
13848 player->is_snapping = FALSE;
13849 player->is_pushing = FALSE;
13857 if (moved & MP_MOVING && !ScreenMovPos &&
13858 (player->index_nr == game.centered_player_nr ||
13859 game.centered_player_nr == -1))
13861 if (moved & MP_MOVING && !ScreenMovPos &&
13862 (player == local_player || !options.network))
13865 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13866 int offset = game.scroll_delay_value;
13868 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13870 /* actual player has left the screen -- scroll in that direction */
13871 if (jx != old_jx) /* player has moved horizontally */
13872 scroll_x += (jx - old_jx);
13873 else /* player has moved vertically */
13874 scroll_y += (jy - old_jy);
13878 if (jx != old_jx) /* player has moved horizontally */
13880 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13881 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13882 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13884 /* don't scroll over playfield boundaries */
13885 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13886 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13888 /* don't scroll more than one field at a time */
13889 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13891 /* don't scroll against the player's moving direction */
13892 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13893 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13894 scroll_x = old_scroll_x;
13896 else /* player has moved vertically */
13898 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13899 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13900 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13902 /* don't scroll over playfield boundaries */
13903 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13904 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13906 /* don't scroll more than one field at a time */
13907 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13909 /* don't scroll against the player's moving direction */
13910 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13911 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13912 scroll_y = old_scroll_y;
13916 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13919 if (!options.network && game.centered_player_nr == -1 &&
13920 !AllPlayersInVisibleScreen())
13922 scroll_x = old_scroll_x;
13923 scroll_y = old_scroll_y;
13927 if (!options.network && !AllPlayersInVisibleScreen())
13929 scroll_x = old_scroll_x;
13930 scroll_y = old_scroll_y;
13935 ScrollScreen(player, SCROLL_INIT);
13936 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13941 player->StepFrame = 0;
13943 if (moved & MP_MOVING)
13945 if (old_jx != jx && old_jy == jy)
13946 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13947 else if (old_jx == jx && old_jy != jy)
13948 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13950 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13952 player->last_move_dir = player->MovDir;
13953 player->is_moving = TRUE;
13954 player->is_snapping = FALSE;
13955 player->is_switching = FALSE;
13956 player->is_dropping = FALSE;
13957 player->is_dropping_pressed = FALSE;
13958 player->drop_pressed_delay = 0;
13961 /* should better be called here than above, but this breaks some tapes */
13962 ScrollPlayer(player, SCROLL_INIT);
13967 CheckGravityMovementWhenNotMoving(player);
13969 player->is_moving = FALSE;
13971 /* at this point, the player is allowed to move, but cannot move right now
13972 (e.g. because of something blocking the way) -- ensure that the player
13973 is also allowed to move in the next frame (in old versions before 3.1.1,
13974 the player was forced to wait again for eight frames before next try) */
13976 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13977 player->move_delay = 0; /* allow direct movement in the next frame */
13980 if (player->move_delay == -1) /* not yet initialized by DigField() */
13981 player->move_delay = player->move_delay_value;
13983 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13985 TestIfPlayerTouchesBadThing(jx, jy);
13986 TestIfPlayerTouchesCustomElement(jx, jy);
13989 if (!player->active)
13990 RemovePlayer(player);
13995 void ScrollPlayer(struct PlayerInfo *player, int mode)
13997 int jx = player->jx, jy = player->jy;
13998 int last_jx = player->last_jx, last_jy = player->last_jy;
13999 int move_stepsize = TILEX / player->move_delay_value;
14001 #if USE_NEW_PLAYER_SPEED
14002 if (!player->active)
14005 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
14008 if (!player->active || player->MovPos == 0)
14012 if (mode == SCROLL_INIT)
14014 player->actual_frame_counter = FrameCounter;
14015 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14017 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
14018 Feld[last_jx][last_jy] == EL_EMPTY)
14020 int last_field_block_delay = 0; /* start with no blocking at all */
14021 int block_delay_adjustment = player->block_delay_adjustment;
14023 /* if player blocks last field, add delay for exactly one move */
14024 if (player->block_last_field)
14026 last_field_block_delay += player->move_delay_value;
14028 /* when blocking enabled, prevent moving up despite gravity */
14029 #if USE_PLAYER_GRAVITY
14030 if (player->gravity && player->MovDir == MV_UP)
14031 block_delay_adjustment = -1;
14033 if (game.gravity && player->MovDir == MV_UP)
14034 block_delay_adjustment = -1;
14038 /* add block delay adjustment (also possible when not blocking) */
14039 last_field_block_delay += block_delay_adjustment;
14041 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
14042 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14045 #if USE_NEW_PLAYER_SPEED
14046 if (player->MovPos != 0) /* player has not yet reached destination */
14052 else if (!FrameReached(&player->actual_frame_counter, 1))
14055 #if USE_NEW_PLAYER_SPEED
14056 if (player->MovPos != 0)
14058 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14059 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14061 /* before DrawPlayer() to draw correct player graphic for this case */
14062 if (player->MovPos == 0)
14063 CheckGravityMovement(player);
14066 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14067 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14069 /* before DrawPlayer() to draw correct player graphic for this case */
14070 if (player->MovPos == 0)
14071 CheckGravityMovement(player);
14074 if (player->MovPos == 0) /* player reached destination field */
14076 if (player->move_delay_reset_counter > 0)
14078 player->move_delay_reset_counter--;
14080 if (player->move_delay_reset_counter == 0)
14082 /* continue with normal speed after quickly moving through gate */
14083 HALVE_PLAYER_SPEED(player);
14085 /* be able to make the next move without delay */
14086 player->move_delay = 0;
14090 player->last_jx = jx;
14091 player->last_jy = jy;
14093 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14094 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14096 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14098 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14099 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14101 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14103 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14104 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14106 DrawPlayer(player); /* needed here only to cleanup last field */
14107 RemovePlayer(player);
14109 if (local_player->friends_still_needed == 0 ||
14110 IS_SP_ELEMENT(Feld[jx][jy]))
14111 PlayerWins(player);
14114 /* this breaks one level: "machine", level 000 */
14116 int move_direction = player->MovDir;
14117 int enter_side = MV_DIR_OPPOSITE(move_direction);
14118 int leave_side = move_direction;
14119 int old_jx = last_jx;
14120 int old_jy = last_jy;
14121 int old_element = Feld[old_jx][old_jy];
14122 int new_element = Feld[jx][jy];
14124 if (IS_CUSTOM_ELEMENT(old_element))
14125 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14127 player->index_bit, leave_side);
14129 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14130 CE_PLAYER_LEAVES_X,
14131 player->index_bit, leave_side);
14133 if (IS_CUSTOM_ELEMENT(new_element))
14134 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14135 player->index_bit, enter_side);
14137 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14138 CE_PLAYER_ENTERS_X,
14139 player->index_bit, enter_side);
14141 #if USE_FIX_CE_ACTION_WITH_PLAYER
14142 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14143 CE_MOVE_OF_X, move_direction);
14145 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14146 CE_MOVE_OF_X, move_direction);
14150 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14152 TestIfPlayerTouchesBadThing(jx, jy);
14153 TestIfPlayerTouchesCustomElement(jx, jy);
14155 /* needed because pushed element has not yet reached its destination,
14156 so it would trigger a change event at its previous field location */
14157 if (!player->is_pushing)
14158 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14160 if (!player->active)
14161 RemovePlayer(player);
14164 if (!local_player->LevelSolved && level.use_step_counter)
14174 if (TimeLeft <= 10 && setup.time_limit)
14175 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14178 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14180 DisplayGameControlValues();
14182 DrawGameValue_Time(TimeLeft);
14185 if (!TimeLeft && setup.time_limit)
14186 for (i = 0; i < MAX_PLAYERS; i++)
14187 KillPlayer(&stored_player[i]);
14190 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14192 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14194 DisplayGameControlValues();
14197 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14198 DrawGameValue_Time(TimePlayed);
14202 if (tape.single_step && tape.recording && !tape.pausing &&
14203 !player->programmed_action)
14204 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14208 void ScrollScreen(struct PlayerInfo *player, int mode)
14210 static unsigned int screen_frame_counter = 0;
14212 if (mode == SCROLL_INIT)
14214 /* set scrolling step size according to actual player's moving speed */
14215 ScrollStepSize = TILEX / player->move_delay_value;
14217 screen_frame_counter = FrameCounter;
14218 ScreenMovDir = player->MovDir;
14219 ScreenMovPos = player->MovPos;
14220 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14223 else if (!FrameReached(&screen_frame_counter, 1))
14228 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14229 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14230 redraw_mask |= REDRAW_FIELD;
14233 ScreenMovDir = MV_NONE;
14236 void TestIfPlayerTouchesCustomElement(int x, int y)
14238 static int xy[4][2] =
14245 static int trigger_sides[4][2] =
14247 /* center side border side */
14248 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14249 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14250 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14251 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14253 static int touch_dir[4] =
14255 MV_LEFT | MV_RIGHT,
14260 int center_element = Feld[x][y]; /* should always be non-moving! */
14263 for (i = 0; i < NUM_DIRECTIONS; i++)
14265 int xx = x + xy[i][0];
14266 int yy = y + xy[i][1];
14267 int center_side = trigger_sides[i][0];
14268 int border_side = trigger_sides[i][1];
14269 int border_element;
14271 if (!IN_LEV_FIELD(xx, yy))
14274 if (IS_PLAYER(x, y)) /* player found at center element */
14276 struct PlayerInfo *player = PLAYERINFO(x, y);
14278 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14279 border_element = Feld[xx][yy]; /* may be moving! */
14280 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14281 border_element = Feld[xx][yy];
14282 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14283 border_element = MovingOrBlocked2Element(xx, yy);
14285 continue; /* center and border element do not touch */
14287 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14288 player->index_bit, border_side);
14289 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14290 CE_PLAYER_TOUCHES_X,
14291 player->index_bit, border_side);
14293 #if USE_FIX_CE_ACTION_WITH_PLAYER
14295 /* use player element that is initially defined in the level playfield,
14296 not the player element that corresponds to the runtime player number
14297 (example: a level that contains EL_PLAYER_3 as the only player would
14298 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14299 int player_element = PLAYERINFO(x, y)->initial_element;
14301 CheckElementChangeBySide(xx, yy, border_element, player_element,
14302 CE_TOUCHING_X, border_side);
14306 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14308 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14310 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14312 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14313 continue; /* center and border element do not touch */
14316 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14317 player->index_bit, center_side);
14318 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14319 CE_PLAYER_TOUCHES_X,
14320 player->index_bit, center_side);
14322 #if USE_FIX_CE_ACTION_WITH_PLAYER
14324 /* use player element that is initially defined in the level playfield,
14325 not the player element that corresponds to the runtime player number
14326 (example: a level that contains EL_PLAYER_3 as the only player would
14327 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14328 int player_element = PLAYERINFO(xx, yy)->initial_element;
14330 CheckElementChangeBySide(x, y, center_element, player_element,
14331 CE_TOUCHING_X, center_side);
14340 #if USE_ELEMENT_TOUCHING_BUGFIX
14342 void TestIfElementTouchesCustomElement(int x, int y)
14344 static int xy[4][2] =
14351 static int trigger_sides[4][2] =
14353 /* center side border side */
14354 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14355 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14356 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14357 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14359 static int touch_dir[4] =
14361 MV_LEFT | MV_RIGHT,
14366 boolean change_center_element = FALSE;
14367 int center_element = Feld[x][y]; /* should always be non-moving! */
14368 int border_element_old[NUM_DIRECTIONS];
14371 for (i = 0; i < NUM_DIRECTIONS; i++)
14373 int xx = x + xy[i][0];
14374 int yy = y + xy[i][1];
14375 int border_element;
14377 border_element_old[i] = -1;
14379 if (!IN_LEV_FIELD(xx, yy))
14382 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14383 border_element = Feld[xx][yy]; /* may be moving! */
14384 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14385 border_element = Feld[xx][yy];
14386 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14387 border_element = MovingOrBlocked2Element(xx, yy);
14389 continue; /* center and border element do not touch */
14391 border_element_old[i] = border_element;
14394 for (i = 0; i < NUM_DIRECTIONS; i++)
14396 int xx = x + xy[i][0];
14397 int yy = y + xy[i][1];
14398 int center_side = trigger_sides[i][0];
14399 int border_element = border_element_old[i];
14401 if (border_element == -1)
14404 /* check for change of border element */
14405 CheckElementChangeBySide(xx, yy, border_element, center_element,
14406 CE_TOUCHING_X, center_side);
14408 /* (center element cannot be player, so we dont have to check this here) */
14411 for (i = 0; i < NUM_DIRECTIONS; i++)
14413 int xx = x + xy[i][0];
14414 int yy = y + xy[i][1];
14415 int border_side = trigger_sides[i][1];
14416 int border_element = border_element_old[i];
14418 if (border_element == -1)
14421 /* check for change of center element (but change it only once) */
14422 if (!change_center_element)
14423 change_center_element =
14424 CheckElementChangeBySide(x, y, center_element, border_element,
14425 CE_TOUCHING_X, border_side);
14427 #if USE_FIX_CE_ACTION_WITH_PLAYER
14428 if (IS_PLAYER(xx, yy))
14430 /* use player element that is initially defined in the level playfield,
14431 not the player element that corresponds to the runtime player number
14432 (example: a level that contains EL_PLAYER_3 as the only player would
14433 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14434 int player_element = PLAYERINFO(xx, yy)->initial_element;
14436 CheckElementChangeBySide(x, y, center_element, player_element,
14437 CE_TOUCHING_X, border_side);
14445 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14447 static int xy[4][2] =
14454 static int trigger_sides[4][2] =
14456 /* center side border side */
14457 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14458 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14459 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14460 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14462 static int touch_dir[4] =
14464 MV_LEFT | MV_RIGHT,
14469 boolean change_center_element = FALSE;
14470 int center_element = Feld[x][y]; /* should always be non-moving! */
14473 for (i = 0; i < NUM_DIRECTIONS; i++)
14475 int xx = x + xy[i][0];
14476 int yy = y + xy[i][1];
14477 int center_side = trigger_sides[i][0];
14478 int border_side = trigger_sides[i][1];
14479 int border_element;
14481 if (!IN_LEV_FIELD(xx, yy))
14484 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14485 border_element = Feld[xx][yy]; /* may be moving! */
14486 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14487 border_element = Feld[xx][yy];
14488 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14489 border_element = MovingOrBlocked2Element(xx, yy);
14491 continue; /* center and border element do not touch */
14493 /* check for change of center element (but change it only once) */
14494 if (!change_center_element)
14495 change_center_element =
14496 CheckElementChangeBySide(x, y, center_element, border_element,
14497 CE_TOUCHING_X, border_side);
14499 /* check for change of border element */
14500 CheckElementChangeBySide(xx, yy, border_element, center_element,
14501 CE_TOUCHING_X, center_side);
14507 void TestIfElementHitsCustomElement(int x, int y, int direction)
14509 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14510 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14511 int hitx = x + dx, hity = y + dy;
14512 int hitting_element = Feld[x][y];
14513 int touched_element;
14515 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14518 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14519 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14521 if (IN_LEV_FIELD(hitx, hity))
14523 int opposite_direction = MV_DIR_OPPOSITE(direction);
14524 int hitting_side = direction;
14525 int touched_side = opposite_direction;
14526 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14527 MovDir[hitx][hity] != direction ||
14528 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14534 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14535 CE_HITTING_X, touched_side);
14537 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14538 CE_HIT_BY_X, hitting_side);
14540 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14541 CE_HIT_BY_SOMETHING, opposite_direction);
14543 #if USE_FIX_CE_ACTION_WITH_PLAYER
14544 if (IS_PLAYER(hitx, hity))
14546 /* use player element that is initially defined in the level playfield,
14547 not the player element that corresponds to the runtime player number
14548 (example: a level that contains EL_PLAYER_3 as the only player would
14549 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14550 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14552 CheckElementChangeBySide(x, y, hitting_element, player_element,
14553 CE_HITTING_X, touched_side);
14559 /* "hitting something" is also true when hitting the playfield border */
14560 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14561 CE_HITTING_SOMETHING, direction);
14565 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14567 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14568 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14569 int hitx = x + dx, hity = y + dy;
14570 int hitting_element = Feld[x][y];
14571 int touched_element;
14573 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14574 !IS_FREE(hitx, hity) &&
14575 (!IS_MOVING(hitx, hity) ||
14576 MovDir[hitx][hity] != direction ||
14577 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14580 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14584 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14588 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14589 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14591 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14592 EP_CAN_SMASH_EVERYTHING, direction);
14594 if (IN_LEV_FIELD(hitx, hity))
14596 int opposite_direction = MV_DIR_OPPOSITE(direction);
14597 int hitting_side = direction;
14598 int touched_side = opposite_direction;
14600 int touched_element = MovingOrBlocked2Element(hitx, hity);
14603 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14604 MovDir[hitx][hity] != direction ||
14605 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14614 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14615 CE_SMASHED_BY_SOMETHING, opposite_direction);
14617 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14618 CE_OTHER_IS_SMASHING, touched_side);
14620 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14621 CE_OTHER_GETS_SMASHED, hitting_side);
14627 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14629 int i, kill_x = -1, kill_y = -1;
14631 int bad_element = -1;
14632 static int test_xy[4][2] =
14639 static int test_dir[4] =
14647 for (i = 0; i < NUM_DIRECTIONS; i++)
14649 int test_x, test_y, test_move_dir, test_element;
14651 test_x = good_x + test_xy[i][0];
14652 test_y = good_y + test_xy[i][1];
14654 if (!IN_LEV_FIELD(test_x, test_y))
14658 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14660 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14662 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14663 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14665 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14666 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14670 bad_element = test_element;
14676 if (kill_x != -1 || kill_y != -1)
14678 if (IS_PLAYER(good_x, good_y))
14680 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14682 if (player->shield_deadly_time_left > 0 &&
14683 !IS_INDESTRUCTIBLE(bad_element))
14684 Bang(kill_x, kill_y);
14685 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14686 KillPlayer(player);
14689 Bang(good_x, good_y);
14693 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14695 int i, kill_x = -1, kill_y = -1;
14696 int bad_element = Feld[bad_x][bad_y];
14697 static int test_xy[4][2] =
14704 static int touch_dir[4] =
14706 MV_LEFT | MV_RIGHT,
14711 static int test_dir[4] =
14719 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14722 for (i = 0; i < NUM_DIRECTIONS; i++)
14724 int test_x, test_y, test_move_dir, test_element;
14726 test_x = bad_x + test_xy[i][0];
14727 test_y = bad_y + test_xy[i][1];
14729 if (!IN_LEV_FIELD(test_x, test_y))
14733 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14735 test_element = Feld[test_x][test_y];
14737 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14738 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14740 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14741 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14743 /* good thing is player or penguin that does not move away */
14744 if (IS_PLAYER(test_x, test_y))
14746 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14748 if (bad_element == EL_ROBOT && player->is_moving)
14749 continue; /* robot does not kill player if he is moving */
14751 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14753 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14754 continue; /* center and border element do not touch */
14762 else if (test_element == EL_PENGUIN)
14772 if (kill_x != -1 || kill_y != -1)
14774 if (IS_PLAYER(kill_x, kill_y))
14776 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14778 if (player->shield_deadly_time_left > 0 &&
14779 !IS_INDESTRUCTIBLE(bad_element))
14780 Bang(bad_x, bad_y);
14781 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14782 KillPlayer(player);
14785 Bang(kill_x, kill_y);
14789 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14791 int bad_element = Feld[bad_x][bad_y];
14792 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14793 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14794 int test_x = bad_x + dx, test_y = bad_y + dy;
14795 int test_move_dir, test_element;
14796 int kill_x = -1, kill_y = -1;
14798 if (!IN_LEV_FIELD(test_x, test_y))
14802 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14804 test_element = Feld[test_x][test_y];
14806 if (test_move_dir != bad_move_dir)
14808 /* good thing can be player or penguin that does not move away */
14809 if (IS_PLAYER(test_x, test_y))
14811 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14813 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14814 player as being hit when he is moving towards the bad thing, because
14815 the "get hit by" condition would be lost after the player stops) */
14816 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14817 return; /* player moves away from bad thing */
14822 else if (test_element == EL_PENGUIN)
14829 if (kill_x != -1 || kill_y != -1)
14831 if (IS_PLAYER(kill_x, kill_y))
14833 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14835 if (player->shield_deadly_time_left > 0 &&
14836 !IS_INDESTRUCTIBLE(bad_element))
14837 Bang(bad_x, bad_y);
14838 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14839 KillPlayer(player);
14842 Bang(kill_x, kill_y);
14846 void TestIfPlayerTouchesBadThing(int x, int y)
14848 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14851 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14853 TestIfGoodThingHitsBadThing(x, y, move_dir);
14856 void TestIfBadThingTouchesPlayer(int x, int y)
14858 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14861 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14863 TestIfBadThingHitsGoodThing(x, y, move_dir);
14866 void TestIfFriendTouchesBadThing(int x, int y)
14868 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14871 void TestIfBadThingTouchesFriend(int x, int y)
14873 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14876 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14878 int i, kill_x = bad_x, kill_y = bad_y;
14879 static int xy[4][2] =
14887 for (i = 0; i < NUM_DIRECTIONS; i++)
14891 x = bad_x + xy[i][0];
14892 y = bad_y + xy[i][1];
14893 if (!IN_LEV_FIELD(x, y))
14896 element = Feld[x][y];
14897 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14898 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14906 if (kill_x != bad_x || kill_y != bad_y)
14907 Bang(bad_x, bad_y);
14910 void KillPlayer(struct PlayerInfo *player)
14912 int jx = player->jx, jy = player->jy;
14914 if (!player->active)
14918 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14919 player->killed, player->active, player->reanimated);
14922 /* the following code was introduced to prevent an infinite loop when calling
14924 -> CheckTriggeredElementChangeExt()
14925 -> ExecuteCustomElementAction()
14927 -> (infinitely repeating the above sequence of function calls)
14928 which occurs when killing the player while having a CE with the setting
14929 "kill player X when explosion of <player X>"; the solution using a new
14930 field "player->killed" was chosen for backwards compatibility, although
14931 clever use of the fields "player->active" etc. would probably also work */
14933 if (player->killed)
14937 player->killed = TRUE;
14939 /* remove accessible field at the player's position */
14940 Feld[jx][jy] = EL_EMPTY;
14942 /* deactivate shield (else Bang()/Explode() would not work right) */
14943 player->shield_normal_time_left = 0;
14944 player->shield_deadly_time_left = 0;
14947 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14948 player->killed, player->active, player->reanimated);
14954 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14955 player->killed, player->active, player->reanimated);
14958 #if USE_PLAYER_REANIMATION
14960 if (player->reanimated) /* killed player may have been reanimated */
14961 player->killed = player->reanimated = FALSE;
14963 BuryPlayer(player);
14965 if (player->killed) /* player may have been reanimated */
14966 BuryPlayer(player);
14969 BuryPlayer(player);
14973 static void KillPlayerUnlessEnemyProtected(int x, int y)
14975 if (!PLAYER_ENEMY_PROTECTED(x, y))
14976 KillPlayer(PLAYERINFO(x, y));
14979 static void KillPlayerUnlessExplosionProtected(int x, int y)
14981 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14982 KillPlayer(PLAYERINFO(x, y));
14985 void BuryPlayer(struct PlayerInfo *player)
14987 int jx = player->jx, jy = player->jy;
14989 if (!player->active)
14992 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14993 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14995 player->GameOver = TRUE;
14996 RemovePlayer(player);
14999 void RemovePlayer(struct PlayerInfo *player)
15001 int jx = player->jx, jy = player->jy;
15002 int i, found = FALSE;
15004 player->present = FALSE;
15005 player->active = FALSE;
15007 if (!ExplodeField[jx][jy])
15008 StorePlayer[jx][jy] = 0;
15010 if (player->is_moving)
15011 TEST_DrawLevelField(player->last_jx, player->last_jy);
15013 for (i = 0; i < MAX_PLAYERS; i++)
15014 if (stored_player[i].active)
15018 AllPlayersGone = TRUE;
15024 #if USE_NEW_SNAP_DELAY
15025 static void setFieldForSnapping(int x, int y, int element, int direction)
15027 struct ElementInfo *ei = &element_info[element];
15028 int direction_bit = MV_DIR_TO_BIT(direction);
15029 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
15030 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
15031 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
15033 Feld[x][y] = EL_ELEMENT_SNAPPING;
15034 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
15036 ResetGfxAnimation(x, y);
15038 GfxElement[x][y] = element;
15039 GfxAction[x][y] = action;
15040 GfxDir[x][y] = direction;
15041 GfxFrame[x][y] = -1;
15046 =============================================================================
15047 checkDiagonalPushing()
15048 -----------------------------------------------------------------------------
15049 check if diagonal input device direction results in pushing of object
15050 (by checking if the alternative direction is walkable, diggable, ...)
15051 =============================================================================
15054 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15055 int x, int y, int real_dx, int real_dy)
15057 int jx, jy, dx, dy, xx, yy;
15059 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
15062 /* diagonal direction: check alternative direction */
15067 xx = jx + (dx == 0 ? real_dx : 0);
15068 yy = jy + (dy == 0 ? real_dy : 0);
15070 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15074 =============================================================================
15076 -----------------------------------------------------------------------------
15077 x, y: field next to player (non-diagonal) to try to dig to
15078 real_dx, real_dy: direction as read from input device (can be diagonal)
15079 =============================================================================
15082 static int DigField(struct PlayerInfo *player,
15083 int oldx, int oldy, int x, int y,
15084 int real_dx, int real_dy, int mode)
15086 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15087 boolean player_was_pushing = player->is_pushing;
15088 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15089 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15090 int jx = oldx, jy = oldy;
15091 int dx = x - jx, dy = y - jy;
15092 int nextx = x + dx, nexty = y + dy;
15093 int move_direction = (dx == -1 ? MV_LEFT :
15094 dx == +1 ? MV_RIGHT :
15096 dy == +1 ? MV_DOWN : MV_NONE);
15097 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15098 int dig_side = MV_DIR_OPPOSITE(move_direction);
15099 int old_element = Feld[jx][jy];
15100 #if USE_FIXED_DONT_RUN_INTO
15101 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15107 if (is_player) /* function can also be called by EL_PENGUIN */
15109 if (player->MovPos == 0)
15111 player->is_digging = FALSE;
15112 player->is_collecting = FALSE;
15115 if (player->MovPos == 0) /* last pushing move finished */
15116 player->is_pushing = FALSE;
15118 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15120 player->is_switching = FALSE;
15121 player->push_delay = -1;
15123 return MP_NO_ACTION;
15127 #if !USE_FIXED_DONT_RUN_INTO
15128 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15129 return MP_NO_ACTION;
15132 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15133 old_element = Back[jx][jy];
15135 /* in case of element dropped at player position, check background */
15136 else if (Back[jx][jy] != EL_EMPTY &&
15137 game.engine_version >= VERSION_IDENT(2,2,0,0))
15138 old_element = Back[jx][jy];
15140 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15141 return MP_NO_ACTION; /* field has no opening in this direction */
15143 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15144 return MP_NO_ACTION; /* field has no opening in this direction */
15146 #if USE_FIXED_DONT_RUN_INTO
15147 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15151 Feld[jx][jy] = player->artwork_element;
15152 InitMovingField(jx, jy, MV_DOWN);
15153 Store[jx][jy] = EL_ACID;
15154 ContinueMoving(jx, jy);
15155 BuryPlayer(player);
15157 return MP_DONT_RUN_INTO;
15161 #if USE_FIXED_DONT_RUN_INTO
15162 if (player_can_move && DONT_RUN_INTO(element))
15164 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15166 return MP_DONT_RUN_INTO;
15170 #if USE_FIXED_DONT_RUN_INTO
15171 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15172 return MP_NO_ACTION;
15175 #if !USE_FIXED_DONT_RUN_INTO
15176 element = Feld[x][y];
15179 collect_count = element_info[element].collect_count_initial;
15181 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15182 return MP_NO_ACTION;
15184 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15185 player_can_move = player_can_move_or_snap;
15187 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15188 game.engine_version >= VERSION_IDENT(2,2,0,0))
15190 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15191 player->index_bit, dig_side);
15192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15193 player->index_bit, dig_side);
15195 if (element == EL_DC_LANDMINE)
15198 if (Feld[x][y] != element) /* field changed by snapping */
15201 return MP_NO_ACTION;
15204 #if USE_PLAYER_GRAVITY
15205 if (player->gravity && is_player && !player->is_auto_moving &&
15206 canFallDown(player) && move_direction != MV_DOWN &&
15207 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15208 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15210 if (game.gravity && is_player && !player->is_auto_moving &&
15211 canFallDown(player) && move_direction != MV_DOWN &&
15212 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15213 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15216 if (player_can_move &&
15217 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15219 int sound_element = SND_ELEMENT(element);
15220 int sound_action = ACTION_WALKING;
15222 if (IS_RND_GATE(element))
15224 if (!player->key[RND_GATE_NR(element)])
15225 return MP_NO_ACTION;
15227 else if (IS_RND_GATE_GRAY(element))
15229 if (!player->key[RND_GATE_GRAY_NR(element)])
15230 return MP_NO_ACTION;
15232 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15234 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15235 return MP_NO_ACTION;
15237 else if (element == EL_EXIT_OPEN ||
15238 element == EL_EM_EXIT_OPEN ||
15240 element == EL_EM_EXIT_OPENING ||
15242 element == EL_STEEL_EXIT_OPEN ||
15243 element == EL_EM_STEEL_EXIT_OPEN ||
15245 element == EL_EM_STEEL_EXIT_OPENING ||
15247 element == EL_SP_EXIT_OPEN ||
15248 element == EL_SP_EXIT_OPENING)
15250 sound_action = ACTION_PASSING; /* player is passing exit */
15252 else if (element == EL_EMPTY)
15254 sound_action = ACTION_MOVING; /* nothing to walk on */
15257 /* play sound from background or player, whatever is available */
15258 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15259 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15261 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15263 else if (player_can_move &&
15264 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15266 if (!ACCESS_FROM(element, opposite_direction))
15267 return MP_NO_ACTION; /* field not accessible from this direction */
15269 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15270 return MP_NO_ACTION;
15272 if (IS_EM_GATE(element))
15274 if (!player->key[EM_GATE_NR(element)])
15275 return MP_NO_ACTION;
15277 else if (IS_EM_GATE_GRAY(element))
15279 if (!player->key[EM_GATE_GRAY_NR(element)])
15280 return MP_NO_ACTION;
15282 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15284 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15285 return MP_NO_ACTION;
15287 else if (IS_EMC_GATE(element))
15289 if (!player->key[EMC_GATE_NR(element)])
15290 return MP_NO_ACTION;
15292 else if (IS_EMC_GATE_GRAY(element))
15294 if (!player->key[EMC_GATE_GRAY_NR(element)])
15295 return MP_NO_ACTION;
15297 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15299 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15300 return MP_NO_ACTION;
15302 else if (element == EL_DC_GATE_WHITE ||
15303 element == EL_DC_GATE_WHITE_GRAY ||
15304 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15306 if (player->num_white_keys == 0)
15307 return MP_NO_ACTION;
15309 player->num_white_keys--;
15311 else if (IS_SP_PORT(element))
15313 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15314 element == EL_SP_GRAVITY_PORT_RIGHT ||
15315 element == EL_SP_GRAVITY_PORT_UP ||
15316 element == EL_SP_GRAVITY_PORT_DOWN)
15317 #if USE_PLAYER_GRAVITY
15318 player->gravity = !player->gravity;
15320 game.gravity = !game.gravity;
15322 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15323 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15324 element == EL_SP_GRAVITY_ON_PORT_UP ||
15325 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15326 #if USE_PLAYER_GRAVITY
15327 player->gravity = TRUE;
15329 game.gravity = TRUE;
15331 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15332 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15333 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15334 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15335 #if USE_PLAYER_GRAVITY
15336 player->gravity = FALSE;
15338 game.gravity = FALSE;
15342 /* automatically move to the next field with double speed */
15343 player->programmed_action = move_direction;
15345 if (player->move_delay_reset_counter == 0)
15347 player->move_delay_reset_counter = 2; /* two double speed steps */
15349 DOUBLE_PLAYER_SPEED(player);
15352 PlayLevelSoundAction(x, y, ACTION_PASSING);
15354 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15358 if (mode != DF_SNAP)
15360 GfxElement[x][y] = GFX_ELEMENT(element);
15361 player->is_digging = TRUE;
15364 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15366 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15367 player->index_bit, dig_side);
15369 if (mode == DF_SNAP)
15371 #if USE_NEW_SNAP_DELAY
15372 if (level.block_snap_field)
15373 setFieldForSnapping(x, y, element, move_direction);
15375 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15377 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15380 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15381 player->index_bit, dig_side);
15384 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15388 if (is_player && mode != DF_SNAP)
15390 GfxElement[x][y] = element;
15391 player->is_collecting = TRUE;
15394 if (element == EL_SPEED_PILL)
15396 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15398 else if (element == EL_EXTRA_TIME && level.time > 0)
15400 TimeLeft += level.extra_time;
15403 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15405 DisplayGameControlValues();
15407 DrawGameValue_Time(TimeLeft);
15410 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15412 player->shield_normal_time_left += level.shield_normal_time;
15413 if (element == EL_SHIELD_DEADLY)
15414 player->shield_deadly_time_left += level.shield_deadly_time;
15416 else if (element == EL_DYNAMITE ||
15417 element == EL_EM_DYNAMITE ||
15418 element == EL_SP_DISK_RED)
15420 if (player->inventory_size < MAX_INVENTORY_SIZE)
15421 player->inventory_element[player->inventory_size++] = element;
15423 DrawGameDoorValues();
15425 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15427 player->dynabomb_count++;
15428 player->dynabombs_left++;
15430 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15432 player->dynabomb_size++;
15434 else if (element == EL_DYNABOMB_INCREASE_POWER)
15436 player->dynabomb_xl = TRUE;
15438 else if (IS_KEY(element))
15440 player->key[KEY_NR(element)] = TRUE;
15442 DrawGameDoorValues();
15444 else if (element == EL_DC_KEY_WHITE)
15446 player->num_white_keys++;
15448 /* display white keys? */
15449 /* DrawGameDoorValues(); */
15451 else if (IS_ENVELOPE(element))
15453 player->show_envelope = element;
15455 else if (element == EL_EMC_LENSES)
15457 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15459 RedrawAllInvisibleElementsForLenses();
15461 else if (element == EL_EMC_MAGNIFIER)
15463 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15465 RedrawAllInvisibleElementsForMagnifier();
15467 else if (IS_DROPPABLE(element) ||
15468 IS_THROWABLE(element)) /* can be collected and dropped */
15472 if (collect_count == 0)
15473 player->inventory_infinite_element = element;
15475 for (i = 0; i < collect_count; i++)
15476 if (player->inventory_size < MAX_INVENTORY_SIZE)
15477 player->inventory_element[player->inventory_size++] = element;
15479 DrawGameDoorValues();
15481 else if (collect_count > 0)
15483 local_player->gems_still_needed -= collect_count;
15484 if (local_player->gems_still_needed < 0)
15485 local_player->gems_still_needed = 0;
15488 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15490 DisplayGameControlValues();
15492 DrawGameValue_Emeralds(local_player->gems_still_needed);
15496 RaiseScoreElement(element);
15497 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15500 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15501 player->index_bit, dig_side);
15503 if (mode == DF_SNAP)
15505 #if USE_NEW_SNAP_DELAY
15506 if (level.block_snap_field)
15507 setFieldForSnapping(x, y, element, move_direction);
15509 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15511 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15514 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15515 player->index_bit, dig_side);
15518 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15520 if (mode == DF_SNAP && element != EL_BD_ROCK)
15521 return MP_NO_ACTION;
15523 if (CAN_FALL(element) && dy)
15524 return MP_NO_ACTION;
15526 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15527 !(element == EL_SPRING && level.use_spring_bug))
15528 return MP_NO_ACTION;
15530 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15531 ((move_direction & MV_VERTICAL &&
15532 ((element_info[element].move_pattern & MV_LEFT &&
15533 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15534 (element_info[element].move_pattern & MV_RIGHT &&
15535 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15536 (move_direction & MV_HORIZONTAL &&
15537 ((element_info[element].move_pattern & MV_UP &&
15538 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15539 (element_info[element].move_pattern & MV_DOWN &&
15540 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15541 return MP_NO_ACTION;
15543 /* do not push elements already moving away faster than player */
15544 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15545 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15546 return MP_NO_ACTION;
15548 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15550 if (player->push_delay_value == -1 || !player_was_pushing)
15551 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15553 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15555 if (player->push_delay_value == -1)
15556 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15558 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15560 if (!player->is_pushing)
15561 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15564 player->is_pushing = TRUE;
15565 player->is_active = TRUE;
15567 if (!(IN_LEV_FIELD(nextx, nexty) &&
15568 (IS_FREE(nextx, nexty) ||
15569 (IS_SB_ELEMENT(element) &&
15570 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15571 (IS_CUSTOM_ELEMENT(element) &&
15572 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15573 return MP_NO_ACTION;
15575 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15576 return MP_NO_ACTION;
15578 if (player->push_delay == -1) /* new pushing; restart delay */
15579 player->push_delay = 0;
15581 if (player->push_delay < player->push_delay_value &&
15582 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15583 element != EL_SPRING && element != EL_BALLOON)
15585 /* make sure that there is no move delay before next try to push */
15586 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15587 player->move_delay = 0;
15589 return MP_NO_ACTION;
15592 if (IS_CUSTOM_ELEMENT(element) &&
15593 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15595 if (!DigFieldByCE(nextx, nexty, element))
15596 return MP_NO_ACTION;
15599 if (IS_SB_ELEMENT(element))
15601 if (element == EL_SOKOBAN_FIELD_FULL)
15603 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15604 local_player->sokobanfields_still_needed++;
15607 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15609 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15610 local_player->sokobanfields_still_needed--;
15613 Feld[x][y] = EL_SOKOBAN_OBJECT;
15615 if (Back[x][y] == Back[nextx][nexty])
15616 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15617 else if (Back[x][y] != 0)
15618 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15621 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15625 if (local_player->sokobanfields_still_needed == 0 &&
15626 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15628 if (local_player->sokobanfields_still_needed == 0 &&
15629 game.emulation == EMU_SOKOBAN)
15632 PlayerWins(player);
15634 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15638 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15640 InitMovingField(x, y, move_direction);
15641 GfxAction[x][y] = ACTION_PUSHING;
15643 if (mode == DF_SNAP)
15644 ContinueMoving(x, y);
15646 MovPos[x][y] = (dx != 0 ? dx : dy);
15648 Pushed[x][y] = TRUE;
15649 Pushed[nextx][nexty] = TRUE;
15651 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15652 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15654 player->push_delay_value = -1; /* get new value later */
15656 /* check for element change _after_ element has been pushed */
15657 if (game.use_change_when_pushing_bug)
15659 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15660 player->index_bit, dig_side);
15661 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15662 player->index_bit, dig_side);
15665 else if (IS_SWITCHABLE(element))
15667 if (PLAYER_SWITCHING(player, x, y))
15669 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15670 player->index_bit, dig_side);
15675 player->is_switching = TRUE;
15676 player->switch_x = x;
15677 player->switch_y = y;
15679 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15681 if (element == EL_ROBOT_WHEEL)
15683 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15687 game.robot_wheel_active = TRUE;
15689 TEST_DrawLevelField(x, y);
15691 else if (element == EL_SP_TERMINAL)
15695 SCAN_PLAYFIELD(xx, yy)
15697 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15699 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15700 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15703 else if (IS_BELT_SWITCH(element))
15705 ToggleBeltSwitch(x, y);
15707 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15708 element == EL_SWITCHGATE_SWITCH_DOWN ||
15709 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15710 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15712 ToggleSwitchgateSwitch(x, y);
15714 else if (element == EL_LIGHT_SWITCH ||
15715 element == EL_LIGHT_SWITCH_ACTIVE)
15717 ToggleLightSwitch(x, y);
15719 else if (element == EL_TIMEGATE_SWITCH ||
15720 element == EL_DC_TIMEGATE_SWITCH)
15722 ActivateTimegateSwitch(x, y);
15724 else if (element == EL_BALLOON_SWITCH_LEFT ||
15725 element == EL_BALLOON_SWITCH_RIGHT ||
15726 element == EL_BALLOON_SWITCH_UP ||
15727 element == EL_BALLOON_SWITCH_DOWN ||
15728 element == EL_BALLOON_SWITCH_NONE ||
15729 element == EL_BALLOON_SWITCH_ANY)
15731 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15732 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15733 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15734 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15735 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15738 else if (element == EL_LAMP)
15740 Feld[x][y] = EL_LAMP_ACTIVE;
15741 local_player->lights_still_needed--;
15743 ResetGfxAnimation(x, y);
15744 TEST_DrawLevelField(x, y);
15746 else if (element == EL_TIME_ORB_FULL)
15748 Feld[x][y] = EL_TIME_ORB_EMPTY;
15750 if (level.time > 0 || level.use_time_orb_bug)
15752 TimeLeft += level.time_orb_time;
15753 game.no_time_limit = FALSE;
15756 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15758 DisplayGameControlValues();
15760 DrawGameValue_Time(TimeLeft);
15764 ResetGfxAnimation(x, y);
15765 TEST_DrawLevelField(x, y);
15767 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15768 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15772 game.ball_state = !game.ball_state;
15774 SCAN_PLAYFIELD(xx, yy)
15776 int e = Feld[xx][yy];
15778 if (game.ball_state)
15780 if (e == EL_EMC_MAGIC_BALL)
15781 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15782 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15783 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15787 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15788 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15789 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15790 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15795 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15796 player->index_bit, dig_side);
15798 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15799 player->index_bit, dig_side);
15801 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15802 player->index_bit, dig_side);
15808 if (!PLAYER_SWITCHING(player, x, y))
15810 player->is_switching = TRUE;
15811 player->switch_x = x;
15812 player->switch_y = y;
15814 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15815 player->index_bit, dig_side);
15816 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15817 player->index_bit, dig_side);
15819 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15820 player->index_bit, dig_side);
15821 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15822 player->index_bit, dig_side);
15825 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15826 player->index_bit, dig_side);
15827 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15828 player->index_bit, dig_side);
15830 return MP_NO_ACTION;
15833 player->push_delay = -1;
15835 if (is_player) /* function can also be called by EL_PENGUIN */
15837 if (Feld[x][y] != element) /* really digged/collected something */
15839 player->is_collecting = !player->is_digging;
15840 player->is_active = TRUE;
15847 static boolean DigFieldByCE(int x, int y, int digging_element)
15849 int element = Feld[x][y];
15851 if (!IS_FREE(x, y))
15853 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15854 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15857 /* no element can dig solid indestructible elements */
15858 if (IS_INDESTRUCTIBLE(element) &&
15859 !IS_DIGGABLE(element) &&
15860 !IS_COLLECTIBLE(element))
15863 if (AmoebaNr[x][y] &&
15864 (element == EL_AMOEBA_FULL ||
15865 element == EL_BD_AMOEBA ||
15866 element == EL_AMOEBA_GROWING))
15868 AmoebaCnt[AmoebaNr[x][y]]--;
15869 AmoebaCnt2[AmoebaNr[x][y]]--;
15872 if (IS_MOVING(x, y))
15873 RemoveMovingField(x, y);
15877 TEST_DrawLevelField(x, y);
15880 /* if digged element was about to explode, prevent the explosion */
15881 ExplodeField[x][y] = EX_TYPE_NONE;
15883 PlayLevelSoundAction(x, y, action);
15886 Store[x][y] = EL_EMPTY;
15889 /* this makes it possible to leave the removed element again */
15890 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15891 Store[x][y] = element;
15893 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15895 int move_leave_element = element_info[digging_element].move_leave_element;
15897 /* this makes it possible to leave the removed element again */
15898 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15899 element : move_leave_element);
15906 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15908 int jx = player->jx, jy = player->jy;
15909 int x = jx + dx, y = jy + dy;
15910 int snap_direction = (dx == -1 ? MV_LEFT :
15911 dx == +1 ? MV_RIGHT :
15913 dy == +1 ? MV_DOWN : MV_NONE);
15914 boolean can_continue_snapping = (level.continuous_snapping &&
15915 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15917 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15920 if (!player->active || !IN_LEV_FIELD(x, y))
15928 if (player->MovPos == 0)
15929 player->is_pushing = FALSE;
15931 player->is_snapping = FALSE;
15933 if (player->MovPos == 0)
15935 player->is_moving = FALSE;
15936 player->is_digging = FALSE;
15937 player->is_collecting = FALSE;
15943 #if USE_NEW_CONTINUOUS_SNAPPING
15944 /* prevent snapping with already pressed snap key when not allowed */
15945 if (player->is_snapping && !can_continue_snapping)
15948 if (player->is_snapping)
15952 player->MovDir = snap_direction;
15954 if (player->MovPos == 0)
15956 player->is_moving = FALSE;
15957 player->is_digging = FALSE;
15958 player->is_collecting = FALSE;
15961 player->is_dropping = FALSE;
15962 player->is_dropping_pressed = FALSE;
15963 player->drop_pressed_delay = 0;
15965 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15968 player->is_snapping = TRUE;
15969 player->is_active = TRUE;
15971 if (player->MovPos == 0)
15973 player->is_moving = FALSE;
15974 player->is_digging = FALSE;
15975 player->is_collecting = FALSE;
15978 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15979 TEST_DrawLevelField(player->last_jx, player->last_jy);
15981 TEST_DrawLevelField(x, y);
15986 static boolean DropElement(struct PlayerInfo *player)
15988 int old_element, new_element;
15989 int dropx = player->jx, dropy = player->jy;
15990 int drop_direction = player->MovDir;
15991 int drop_side = drop_direction;
15993 int drop_element = get_next_dropped_element(player);
15995 int drop_element = (player->inventory_size > 0 ?
15996 player->inventory_element[player->inventory_size - 1] :
15997 player->inventory_infinite_element != EL_UNDEFINED ?
15998 player->inventory_infinite_element :
15999 player->dynabombs_left > 0 ?
16000 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
16004 player->is_dropping_pressed = TRUE;
16006 /* do not drop an element on top of another element; when holding drop key
16007 pressed without moving, dropped element must move away before the next
16008 element can be dropped (this is especially important if the next element
16009 is dynamite, which can be placed on background for historical reasons) */
16010 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
16013 if (IS_THROWABLE(drop_element))
16015 dropx += GET_DX_FROM_DIR(drop_direction);
16016 dropy += GET_DY_FROM_DIR(drop_direction);
16018 if (!IN_LEV_FIELD(dropx, dropy))
16022 old_element = Feld[dropx][dropy]; /* old element at dropping position */
16023 new_element = drop_element; /* default: no change when dropping */
16025 /* check if player is active, not moving and ready to drop */
16026 if (!player->active || player->MovPos || player->drop_delay > 0)
16029 /* check if player has anything that can be dropped */
16030 if (new_element == EL_UNDEFINED)
16033 /* check if drop key was pressed long enough for EM style dynamite */
16034 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
16037 /* check if anything can be dropped at the current position */
16038 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
16041 /* collected custom elements can only be dropped on empty fields */
16042 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16045 if (old_element != EL_EMPTY)
16046 Back[dropx][dropy] = old_element; /* store old element on this field */
16048 ResetGfxAnimation(dropx, dropy);
16049 ResetRandomAnimationValue(dropx, dropy);
16051 if (player->inventory_size > 0 ||
16052 player->inventory_infinite_element != EL_UNDEFINED)
16054 if (player->inventory_size > 0)
16056 player->inventory_size--;
16058 DrawGameDoorValues();
16060 if (new_element == EL_DYNAMITE)
16061 new_element = EL_DYNAMITE_ACTIVE;
16062 else if (new_element == EL_EM_DYNAMITE)
16063 new_element = EL_EM_DYNAMITE_ACTIVE;
16064 else if (new_element == EL_SP_DISK_RED)
16065 new_element = EL_SP_DISK_RED_ACTIVE;
16068 Feld[dropx][dropy] = new_element;
16070 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16071 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16072 el2img(Feld[dropx][dropy]), 0);
16074 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16076 /* needed if previous element just changed to "empty" in the last frame */
16077 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16079 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16080 player->index_bit, drop_side);
16081 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16083 player->index_bit, drop_side);
16085 TestIfElementTouchesCustomElement(dropx, dropy);
16087 else /* player is dropping a dyna bomb */
16089 player->dynabombs_left--;
16091 Feld[dropx][dropy] = new_element;
16093 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16094 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16095 el2img(Feld[dropx][dropy]), 0);
16097 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16100 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16101 InitField_WithBug1(dropx, dropy, FALSE);
16103 new_element = Feld[dropx][dropy]; /* element might have changed */
16105 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16106 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16109 int move_direction;
16113 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16114 MovDir[dropx][dropy] = drop_direction;
16117 move_direction = MovDir[dropx][dropy];
16118 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16119 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16122 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16124 #if USE_FIX_IMPACT_COLLISION
16125 /* do not cause impact style collision by dropping elements that can fall */
16126 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16128 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16132 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16133 player->is_dropping = TRUE;
16135 player->drop_pressed_delay = 0;
16136 player->is_dropping_pressed = FALSE;
16138 player->drop_x = dropx;
16139 player->drop_y = dropy;
16144 /* ------------------------------------------------------------------------- */
16145 /* game sound playing functions */
16146 /* ------------------------------------------------------------------------- */
16148 static int *loop_sound_frame = NULL;
16149 static int *loop_sound_volume = NULL;
16151 void InitPlayLevelSound()
16153 int num_sounds = getSoundListSize();
16155 checked_free(loop_sound_frame);
16156 checked_free(loop_sound_volume);
16158 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16159 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16162 static void PlayLevelSound(int x, int y, int nr)
16164 int sx = SCREENX(x), sy = SCREENY(y);
16165 int volume, stereo_position;
16166 int max_distance = 8;
16167 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16169 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16170 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16173 if (!IN_LEV_FIELD(x, y) ||
16174 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16175 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16178 volume = SOUND_MAX_VOLUME;
16180 if (!IN_SCR_FIELD(sx, sy))
16182 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16183 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16185 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16188 stereo_position = (SOUND_MAX_LEFT +
16189 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16190 (SCR_FIELDX + 2 * max_distance));
16192 if (IS_LOOP_SOUND(nr))
16194 /* This assures that quieter loop sounds do not overwrite louder ones,
16195 while restarting sound volume comparison with each new game frame. */
16197 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16200 loop_sound_volume[nr] = volume;
16201 loop_sound_frame[nr] = FrameCounter;
16204 PlaySoundExt(nr, volume, stereo_position, type);
16207 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16209 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16210 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16211 y < LEVELY(BY1) ? LEVELY(BY1) :
16212 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16216 static void PlayLevelSoundAction(int x, int y, int action)
16218 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16221 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16223 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16225 if (sound_effect != SND_UNDEFINED)
16226 PlayLevelSound(x, y, sound_effect);
16229 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16232 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16234 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16235 PlayLevelSound(x, y, sound_effect);
16238 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16240 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16242 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16243 PlayLevelSound(x, y, sound_effect);
16246 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16248 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16250 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16251 StopSound(sound_effect);
16254 static void PlayLevelMusic()
16256 if (levelset.music[level_nr] != MUS_UNDEFINED)
16257 PlayMusic(levelset.music[level_nr]); /* from config file */
16259 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16262 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16264 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16265 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16266 int x = xx - 1 - offset;
16267 int y = yy - 1 - offset;
16272 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16276 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16280 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16284 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16288 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16292 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16296 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16299 case SAMPLE_android_clone:
16300 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16303 case SAMPLE_android_move:
16304 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16307 case SAMPLE_spring:
16308 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16312 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16316 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16319 case SAMPLE_eater_eat:
16320 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16324 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16327 case SAMPLE_collect:
16328 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16331 case SAMPLE_diamond:
16332 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16335 case SAMPLE_squash:
16336 /* !!! CHECK THIS !!! */
16338 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16340 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16344 case SAMPLE_wonderfall:
16345 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16349 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16353 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16357 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16361 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16365 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16369 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16372 case SAMPLE_wonder:
16373 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16377 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16380 case SAMPLE_exit_open:
16381 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16384 case SAMPLE_exit_leave:
16385 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16388 case SAMPLE_dynamite:
16389 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16393 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16397 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16401 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16405 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16409 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16413 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16417 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16422 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16424 int element = map_element_SP_to_RND(element_sp);
16425 int action = map_action_SP_to_RND(action_sp);
16426 int offset = (setup.sp_show_border_elements ? 0 : 1);
16427 int x = xx - offset;
16428 int y = yy - offset;
16431 printf("::: %d -> %d\n", element_sp, action_sp);
16434 PlayLevelSoundElementAction(x, y, element, action);
16437 void RaiseScore(int value)
16439 local_player->score += value;
16442 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16444 DisplayGameControlValues();
16446 DrawGameValue_Score(local_player->score);
16450 void RaiseScoreElement(int element)
16455 case EL_BD_DIAMOND:
16456 case EL_EMERALD_YELLOW:
16457 case EL_EMERALD_RED:
16458 case EL_EMERALD_PURPLE:
16459 case EL_SP_INFOTRON:
16460 RaiseScore(level.score[SC_EMERALD]);
16463 RaiseScore(level.score[SC_DIAMOND]);
16466 RaiseScore(level.score[SC_CRYSTAL]);
16469 RaiseScore(level.score[SC_PEARL]);
16472 case EL_BD_BUTTERFLY:
16473 case EL_SP_ELECTRON:
16474 RaiseScore(level.score[SC_BUG]);
16477 case EL_BD_FIREFLY:
16478 case EL_SP_SNIKSNAK:
16479 RaiseScore(level.score[SC_SPACESHIP]);
16482 case EL_DARK_YAMYAM:
16483 RaiseScore(level.score[SC_YAMYAM]);
16486 RaiseScore(level.score[SC_ROBOT]);
16489 RaiseScore(level.score[SC_PACMAN]);
16492 RaiseScore(level.score[SC_NUT]);
16495 case EL_EM_DYNAMITE:
16496 case EL_SP_DISK_RED:
16497 case EL_DYNABOMB_INCREASE_NUMBER:
16498 case EL_DYNABOMB_INCREASE_SIZE:
16499 case EL_DYNABOMB_INCREASE_POWER:
16500 RaiseScore(level.score[SC_DYNAMITE]);
16502 case EL_SHIELD_NORMAL:
16503 case EL_SHIELD_DEADLY:
16504 RaiseScore(level.score[SC_SHIELD]);
16506 case EL_EXTRA_TIME:
16507 RaiseScore(level.extra_time_score);
16521 case EL_DC_KEY_WHITE:
16522 RaiseScore(level.score[SC_KEY]);
16525 RaiseScore(element_info[element].collect_score);
16530 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16532 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16535 /* closing door required in case of envelope style request dialogs */
16537 CloseDoor(DOOR_CLOSE_1);
16540 #if defined(NETWORK_AVALIABLE)
16541 if (options.network)
16542 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16551 FadeSkipNextFadeIn();
16553 fading = fading_none;
16557 OpenDoor(DOOR_CLOSE_1);
16560 game_status = GAME_MODE_MAIN;
16563 DrawAndFadeInMainMenu(REDRAW_FIELD);
16571 FadeOut(REDRAW_FIELD);
16574 game_status = GAME_MODE_MAIN;
16576 DrawAndFadeInMainMenu(REDRAW_FIELD);
16580 else /* continue playing the game */
16582 if (tape.playing && tape.deactivate_display)
16583 TapeDeactivateDisplayOff(TRUE);
16585 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16587 if (tape.playing && tape.deactivate_display)
16588 TapeDeactivateDisplayOn();
16592 void RequestQuitGame(boolean ask_if_really_quit)
16594 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16595 boolean skip_request = AllPlayersGone || quick_quit;
16597 RequestQuitGameExt(skip_request, quick_quit,
16598 "Do you really want to quit the game?");
16602 /* ------------------------------------------------------------------------- */
16603 /* random generator functions */
16604 /* ------------------------------------------------------------------------- */
16606 unsigned int InitEngineRandom_RND(int seed)
16608 game.num_random_calls = 0;
16611 unsigned int rnd_seed = InitEngineRandom(seed);
16613 printf("::: START RND: %d\n", rnd_seed);
16618 return InitEngineRandom(seed);
16624 unsigned int RND(int max)
16628 game.num_random_calls++;
16630 return GetEngineRandom(max);
16637 /* ------------------------------------------------------------------------- */
16638 /* game engine snapshot handling functions */
16639 /* ------------------------------------------------------------------------- */
16641 struct EngineSnapshotInfo
16643 /* runtime values for custom element collect score */
16644 int collect_score[NUM_CUSTOM_ELEMENTS];
16646 /* runtime values for group element choice position */
16647 int choice_pos[NUM_GROUP_ELEMENTS];
16649 /* runtime values for belt position animations */
16650 int belt_graphic[4][NUM_BELT_PARTS];
16651 int belt_anim_mode[4][NUM_BELT_PARTS];
16654 static struct EngineSnapshotInfo engine_snapshot_rnd;
16655 static char *snapshot_level_identifier = NULL;
16656 static int snapshot_level_nr = -1;
16658 static void SaveEngineSnapshotValues_RND()
16660 static int belt_base_active_element[4] =
16662 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16663 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16664 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16665 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16669 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16671 int element = EL_CUSTOM_START + i;
16673 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16676 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16678 int element = EL_GROUP_START + i;
16680 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16683 for (i = 0; i < 4; i++)
16685 for (j = 0; j < NUM_BELT_PARTS; j++)
16687 int element = belt_base_active_element[i] + j;
16688 int graphic = el2img(element);
16689 int anim_mode = graphic_info[graphic].anim_mode;
16691 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16692 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16697 static void LoadEngineSnapshotValues_RND()
16699 unsigned int num_random_calls = game.num_random_calls;
16702 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16704 int element = EL_CUSTOM_START + i;
16706 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16709 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16711 int element = EL_GROUP_START + i;
16713 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16716 for (i = 0; i < 4; i++)
16718 for (j = 0; j < NUM_BELT_PARTS; j++)
16720 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16721 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16723 graphic_info[graphic].anim_mode = anim_mode;
16727 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16729 InitRND(tape.random_seed);
16730 for (i = 0; i < num_random_calls; i++)
16734 if (game.num_random_calls != num_random_calls)
16736 Error(ERR_INFO, "number of random calls out of sync");
16737 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16738 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16739 Error(ERR_EXIT, "this should not happen -- please debug");
16743 void FreeEngineSnapshot()
16745 FreeEngineSnapshotBuffers();
16747 setString(&snapshot_level_identifier, NULL);
16748 snapshot_level_nr = -1;
16751 void SaveEngineSnapshot()
16753 /* do not save snapshots from editor */
16754 if (level_editor_test_game)
16757 /* free previous snapshot buffers, if needed */
16758 FreeEngineSnapshotBuffers();
16761 /* copy some special values to a structure better suited for the snapshot */
16763 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16764 SaveEngineSnapshotValues_RND();
16765 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16766 SaveEngineSnapshotValues_EM();
16767 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16768 SaveEngineSnapshotValues_SP();
16770 /* save values stored in special snapshot structure */
16772 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16773 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16774 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16775 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16776 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16777 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16779 /* copy some special values to a structure better suited for the snapshot */
16781 SaveEngineSnapshotValues_RND();
16782 SaveEngineSnapshotValues_EM();
16783 SaveEngineSnapshotValues_SP();
16785 /* save values stored in special snapshot structure */
16787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16788 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16789 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16792 /* save further RND engine values */
16794 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16795 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16796 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16798 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16799 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16800 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16801 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16804 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16805 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16806 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16807 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16810 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16811 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16813 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16815 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16817 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16818 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16820 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16821 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16822 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16823 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16824 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16825 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16826 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16827 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16828 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16829 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16830 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16831 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16832 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16833 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16834 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16835 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16836 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16837 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16839 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16840 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16842 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16843 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16844 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16846 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16847 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16849 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16850 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16851 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16852 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16853 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16855 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16856 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16858 /* save level identification information */
16860 setString(&snapshot_level_identifier, leveldir_current->identifier);
16861 snapshot_level_nr = level_nr;
16864 ListNode *node = engine_snapshot_list_rnd;
16867 while (node != NULL)
16869 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16874 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16878 void LoadEngineSnapshot()
16880 /* restore generically stored snapshot buffers */
16882 LoadEngineSnapshotBuffers();
16884 /* restore special values from snapshot structure */
16887 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16888 LoadEngineSnapshotValues_RND();
16889 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16890 LoadEngineSnapshotValues_EM();
16891 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16892 LoadEngineSnapshotValues_SP();
16894 LoadEngineSnapshotValues_RND();
16895 LoadEngineSnapshotValues_EM();
16896 LoadEngineSnapshotValues_SP();
16900 printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16904 // needed if tile size was different when saving and loading engine snapshot
16905 if (local_player->present)
16907 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
16908 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
16909 local_player->jx - MIDPOSX);
16911 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
16912 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
16913 local_player->jy - MIDPOSY);
16918 printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
16922 boolean CheckEngineSnapshot()
16924 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16925 snapshot_level_nr == level_nr);
16929 /* ---------- new game button stuff ---------------------------------------- */
16937 } gamebutton_info[NUM_GAME_BUTTONS] =
16940 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16941 GAME_CTRL_ID_STOP, "stop game"
16944 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16945 GAME_CTRL_ID_PAUSE, "pause game"
16948 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16949 GAME_CTRL_ID_PLAY, "play game"
16952 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16953 SOUND_CTRL_ID_MUSIC, "background music on/off"
16956 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16957 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16960 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16961 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16964 IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
16965 GAME_CTRL_ID_SAVE, "save game"
16968 IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
16969 GAME_CTRL_ID_LOAD, "load game"
16973 void CreateGameButtons()
16977 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16979 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16980 struct Rect *pos = gamebutton_info[i].pos;
16981 struct GadgetInfo *gi;
16984 unsigned int event_mask;
16985 int base_x = (tape.show_game_buttons ? VX : DX);
16986 int base_y = (tape.show_game_buttons ? VY : DY);
16987 int gd_x = gfx->src_x;
16988 int gd_y = gfx->src_y;
16989 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16990 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16991 int gd_xa = gfx->src_x + gfx->active_xoffset;
16992 int gd_ya = gfx->src_y + gfx->active_yoffset;
16993 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16994 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16997 if (gfx->bitmap == NULL)
16999 game_gadget[id] = NULL;
17004 if (id == GAME_CTRL_ID_STOP ||
17005 id == GAME_CTRL_ID_PAUSE ||
17006 id == GAME_CTRL_ID_PLAY ||
17007 id == GAME_CTRL_ID_SAVE ||
17008 id == GAME_CTRL_ID_LOAD)
17010 button_type = GD_TYPE_NORMAL_BUTTON;
17012 event_mask = GD_EVENT_RELEASED;
17016 button_type = GD_TYPE_CHECK_BUTTON;
17018 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
17019 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
17020 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
17021 event_mask = GD_EVENT_PRESSED;
17024 gi = CreateGadget(GDI_CUSTOM_ID, id,
17025 GDI_INFO_TEXT, gamebutton_info[i].infotext,
17026 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
17027 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
17028 GDI_WIDTH, gfx->width,
17029 GDI_HEIGHT, gfx->height,
17030 GDI_TYPE, button_type,
17031 GDI_STATE, GD_BUTTON_UNPRESSED,
17032 GDI_CHECKED, checked,
17033 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
17034 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
17035 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
17036 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
17037 GDI_DIRECT_DRAW, FALSE,
17038 GDI_EVENT_MASK, event_mask,
17039 GDI_CALLBACK_ACTION, HandleGameButtons,
17043 Error(ERR_EXIT, "cannot create gadget");
17045 game_gadget[id] = gi;
17049 void FreeGameButtons()
17053 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17054 FreeGadget(game_gadget[i]);
17057 void MapGameButtons()
17061 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17062 MapGadget(game_gadget[i]);
17065 void UnmapGameButtons()
17069 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17070 UnmapGadget(game_gadget[i]);
17073 void RedrawGameButtons()
17077 for (i = 0; i < NUM_GAME_BUTTONS; i++)
17078 RedrawGadget(game_gadget[i]);
17080 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
17081 redraw_mask &= ~REDRAW_ALL;
17084 static void HandleGameButtonsExt(int id)
17086 boolean handle_game_buttons =
17087 (game_status == GAME_MODE_PLAYING ||
17088 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17090 if (!handle_game_buttons)
17095 case GAME_CTRL_ID_STOP:
17096 if (game_status == GAME_MODE_MAIN)
17102 RequestQuitGame(TRUE);
17106 case GAME_CTRL_ID_PAUSE:
17107 if (options.network && game_status == GAME_MODE_PLAYING)
17109 #if defined(NETWORK_AVALIABLE)
17111 SendToServer_ContinuePlaying();
17113 SendToServer_PausePlaying();
17117 TapeTogglePause(TAPE_TOGGLE_MANUAL);
17120 case GAME_CTRL_ID_PLAY:
17121 if (game_status == GAME_MODE_MAIN)
17123 StartGameActions(options.network, setup.autorecord, level.random_seed);
17125 else if (tape.pausing)
17127 #if defined(NETWORK_AVALIABLE)
17128 if (options.network)
17129 SendToServer_ContinuePlaying();
17133 tape.pausing = FALSE;
17134 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17139 case SOUND_CTRL_ID_MUSIC:
17140 if (setup.sound_music)
17142 setup.sound_music = FALSE;
17146 else if (audio.music_available)
17148 setup.sound = setup.sound_music = TRUE;
17150 SetAudioMode(setup.sound);
17156 case SOUND_CTRL_ID_LOOPS:
17157 if (setup.sound_loops)
17158 setup.sound_loops = FALSE;
17159 else if (audio.loops_available)
17161 setup.sound = setup.sound_loops = TRUE;
17163 SetAudioMode(setup.sound);
17167 case SOUND_CTRL_ID_SIMPLE:
17168 if (setup.sound_simple)
17169 setup.sound_simple = FALSE;
17170 else if (audio.sound_available)
17172 setup.sound = setup.sound_simple = TRUE;
17174 SetAudioMode(setup.sound);
17178 case GAME_CTRL_ID_SAVE:
17182 case GAME_CTRL_ID_LOAD:
17191 static void HandleGameButtons(struct GadgetInfo *gi)
17193 HandleGameButtonsExt(gi->custom_id);
17196 void HandleSoundButtonKeys(Key key)
17199 if (key == setup.shortcut.sound_simple)
17200 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17201 else if (key == setup.shortcut.sound_loops)
17202 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17203 else if (key == setup.shortcut.sound_music)
17204 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17206 if (key == setup.shortcut.sound_simple)
17207 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17208 else if (key == setup.shortcut.sound_loops)
17209 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17210 else if (key == setup.shortcut.sound_music)
17211 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);