1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_CONTROL_LEVEL_NUMBER 0
135 #define GAME_CONTROL_GEMS 1
136 #define GAME_CONTROL_INVENTORY_COUNT 2
137 #define GAME_CONTROL_INVENTORY_FIRST_1 3
138 #define GAME_CONTROL_INVENTORY_FIRST_2 4
139 #define GAME_CONTROL_INVENTORY_FIRST_3 5
140 #define GAME_CONTROL_INVENTORY_FIRST_4 6
141 #define GAME_CONTROL_INVENTORY_FIRST_5 7
142 #define GAME_CONTROL_INVENTORY_FIRST_6 8
143 #define GAME_CONTROL_INVENTORY_FIRST_7 9
144 #define GAME_CONTROL_INVENTORY_FIRST_8 10
145 #define GAME_CONTROL_INVENTORY_LAST_1 11
146 #define GAME_CONTROL_INVENTORY_LAST_2 12
147 #define GAME_CONTROL_INVENTORY_LAST_3 13
148 #define GAME_CONTROL_INVENTORY_LAST_4 14
149 #define GAME_CONTROL_INVENTORY_LAST_5 15
150 #define GAME_CONTROL_INVENTORY_LAST_6 16
151 #define GAME_CONTROL_INVENTORY_LAST_7 17
152 #define GAME_CONTROL_INVENTORY_LAST_8 18
153 #define GAME_CONTROL_KEY_1 19
154 #define GAME_CONTROL_KEY_2 20
155 #define GAME_CONTROL_KEY_3 21
156 #define GAME_CONTROL_KEY_4 22
157 #define GAME_CONTROL_KEY_5 23
158 #define GAME_CONTROL_KEY_6 24
159 #define GAME_CONTROL_KEY_7 25
160 #define GAME_CONTROL_KEY_8 26
161 #define GAME_CONTROL_KEY_WHITE 27
162 #define GAME_CONTROL_KEY_WHITE_COUNT 28
163 #define GAME_CONTROL_SCORE 29
164 #define GAME_CONTROL_TIME 30
165 #define GAME_CONTROL_TIME_HH 31
166 #define GAME_CONTROL_TIME_MM 32
167 #define GAME_CONTROL_TIME_SS 33
168 #define GAME_CONTROL_SHIELD_NORMAL 34
169 #define GAME_CONTROL_SHIELD_NORMAL_TIME 35
170 #define GAME_CONTROL_SHIELD_DEADLY 36
171 #define GAME_CONTROL_SHIELD_DEADLY_TIME 37
172 #define GAME_CONTROL_EXIT 38
173 #define GAME_CONTROL_EM_EXIT 39
174 #define GAME_CONTROL_SP_EXIT 40
175 #define GAME_CONTROL_STEEL_EXIT 41
176 #define GAME_CONTROL_EM_STEEL_EXIT 42
177 #define GAME_CONTROL_EMC_MAGIC_BALL 43
178 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH 44
179 #define GAME_CONTROL_LIGHT_SWITCH 45
180 #define GAME_CONTROL_LIGHT_SWITCH_TIME 46
181 #define GAME_CONTROL_TIMEGATE_SWITCH 47
182 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME 48
183 #define GAME_CONTROL_SWITCHGATE_SWITCH 49
184 #define GAME_CONTROL_EMC_LENSES 50
185 #define GAME_CONTROL_EMC_LENSES_TIME 51
186 #define GAME_CONTROL_EMC_MAGNIFIER 52
187 #define GAME_CONTROL_EMC_MAGNIFIER_TIME 53
188 #define GAME_CONTROL_BALLOON_SWITCH 54
189 #define GAME_CONTROL_DYNABOMB_NUMBER 55
190 #define GAME_CONTROL_DYNABOMB_SIZE 56
191 #define GAME_CONTROL_DYNABOMB_POWER 57
192 #define GAME_CONTROL_PENGUINS 58
193 #define GAME_CONTROL_SOKOBAN_OBJECTS 59
194 #define GAME_CONTROL_SOKOBAN_FIELDS 60
195 #define GAME_CONTROL_ROBOT_WHEEL 61
196 #define GAME_CONTROL_CONVEYOR_BELT_1 62
197 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 63
198 #define GAME_CONTROL_CONVEYOR_BELT_2 64
199 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 65
200 #define GAME_CONTROL_CONVEYOR_BELT_3 66
201 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 67
202 #define GAME_CONTROL_CONVEYOR_BELT_4 68
203 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 69
204 #define GAME_CONTROL_MAGIC_WALL 70
205 #define GAME_CONTROL_MAGIC_WALL_TIME 71
206 #define GAME_CONTROL_BD_MAGIC_WALL 72
207 #define GAME_CONTROL_DC_MAGIC_WALL 73
208 #define GAME_CONTROL_PLAYER_NAME 74
209 #define GAME_CONTROL_LEVEL_NAME 75
210 #define GAME_CONTROL_LEVEL_AUTHOR 76
212 #define NUM_GAME_CONTROLS 77
214 int game_control_value[NUM_GAME_CONTROLS];
215 int last_game_control_value[NUM_GAME_CONTROLS];
217 struct GameControlInfo
221 struct TextPosInfo *pos;
225 static struct GameControlInfo game_controls[] =
228 GAME_CONTROL_LEVEL_NUMBER,
229 &game.panel.level_number,
238 GAME_CONTROL_INVENTORY_COUNT,
239 &game.panel.inventory_count,
243 GAME_CONTROL_INVENTORY_FIRST_1,
244 &game.panel.inventory_first_1,
248 GAME_CONTROL_INVENTORY_FIRST_2,
249 &game.panel.inventory_first_2,
253 GAME_CONTROL_INVENTORY_FIRST_3,
254 &game.panel.inventory_first_3,
258 GAME_CONTROL_INVENTORY_FIRST_4,
259 &game.panel.inventory_first_4,
263 GAME_CONTROL_INVENTORY_FIRST_5,
264 &game.panel.inventory_first_5,
268 GAME_CONTROL_INVENTORY_FIRST_6,
269 &game.panel.inventory_first_6,
273 GAME_CONTROL_INVENTORY_FIRST_7,
274 &game.panel.inventory_first_7,
278 GAME_CONTROL_INVENTORY_FIRST_8,
279 &game.panel.inventory_first_8,
283 GAME_CONTROL_INVENTORY_LAST_1,
284 &game.panel.inventory_last_1,
288 GAME_CONTROL_INVENTORY_LAST_2,
289 &game.panel.inventory_last_2,
293 GAME_CONTROL_INVENTORY_LAST_3,
294 &game.panel.inventory_last_3,
298 GAME_CONTROL_INVENTORY_LAST_4,
299 &game.panel.inventory_last_4,
303 GAME_CONTROL_INVENTORY_LAST_5,
304 &game.panel.inventory_last_5,
308 GAME_CONTROL_INVENTORY_LAST_6,
309 &game.panel.inventory_last_6,
313 GAME_CONTROL_INVENTORY_LAST_7,
314 &game.panel.inventory_last_7,
318 GAME_CONTROL_INVENTORY_LAST_8,
319 &game.panel.inventory_last_8,
363 GAME_CONTROL_KEY_WHITE,
364 &game.panel.key_white,
368 GAME_CONTROL_KEY_WHITE_COUNT,
369 &game.panel.key_white_count,
383 GAME_CONTROL_TIME_HH,
388 GAME_CONTROL_TIME_MM,
393 GAME_CONTROL_TIME_SS,
398 GAME_CONTROL_SHIELD_NORMAL,
399 &game.panel.shield_normal,
403 GAME_CONTROL_SHIELD_NORMAL_TIME,
404 &game.panel.shield_normal_time,
408 GAME_CONTROL_SHIELD_DEADLY,
409 &game.panel.shield_deadly,
413 GAME_CONTROL_SHIELD_DEADLY_TIME,
414 &game.panel.shield_deadly_time,
423 GAME_CONTROL_EM_EXIT,
428 GAME_CONTROL_SP_EXIT,
433 GAME_CONTROL_STEEL_EXIT,
434 &game.panel.steel_exit,
438 GAME_CONTROL_EM_STEEL_EXIT,
439 &game.panel.em_steel_exit,
443 GAME_CONTROL_EMC_MAGIC_BALL,
444 &game.panel.emc_magic_ball,
448 GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
449 &game.panel.emc_magic_ball_switch,
453 GAME_CONTROL_LIGHT_SWITCH,
454 &game.panel.light_switch,
458 GAME_CONTROL_LIGHT_SWITCH_TIME,
459 &game.panel.light_switch_time,
463 GAME_CONTROL_TIMEGATE_SWITCH,
464 &game.panel.timegate_switch,
468 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
469 &game.panel.timegate_switch_time,
473 GAME_CONTROL_SWITCHGATE_SWITCH,
474 &game.panel.switchgate_switch,
478 GAME_CONTROL_EMC_LENSES,
479 &game.panel.emc_lenses,
483 GAME_CONTROL_EMC_LENSES_TIME,
484 &game.panel.emc_lenses_time,
488 GAME_CONTROL_EMC_MAGNIFIER,
489 &game.panel.emc_magnifier,
493 GAME_CONTROL_EMC_MAGNIFIER_TIME,
494 &game.panel.emc_magnifier_time,
498 GAME_CONTROL_BALLOON_SWITCH,
499 &game.panel.balloon_switch,
503 GAME_CONTROL_DYNABOMB_NUMBER,
504 &game.panel.dynabomb_number,
508 GAME_CONTROL_DYNABOMB_SIZE,
509 &game.panel.dynabomb_size,
513 GAME_CONTROL_DYNABOMB_POWER,
514 &game.panel.dynabomb_power,
518 GAME_CONTROL_PENGUINS,
519 &game.panel.penguins,
523 GAME_CONTROL_SOKOBAN_OBJECTS,
524 &game.panel.sokoban_objects,
528 GAME_CONTROL_SOKOBAN_FIELDS,
529 &game.panel.sokoban_fields,
533 GAME_CONTROL_ROBOT_WHEEL,
534 &game.panel.robot_wheel,
538 GAME_CONTROL_CONVEYOR_BELT_1,
539 &game.panel.conveyor_belt_1,
543 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
544 &game.panel.conveyor_belt_1_switch,
548 GAME_CONTROL_CONVEYOR_BELT_2,
549 &game.panel.conveyor_belt_2,
553 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
554 &game.panel.conveyor_belt_2_switch,
558 GAME_CONTROL_CONVEYOR_BELT_3,
559 &game.panel.conveyor_belt_3,
563 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
564 &game.panel.conveyor_belt_3_switch,
568 GAME_CONTROL_CONVEYOR_BELT_4,
569 &game.panel.conveyor_belt_4,
573 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
574 &game.panel.conveyor_belt_4_switch,
578 GAME_CONTROL_MAGIC_WALL,
579 &game.panel.magic_wall,
583 GAME_CONTROL_MAGIC_WALL_TIME,
584 &game.panel.magic_wall_time,
588 GAME_CONTROL_BD_MAGIC_WALL,
589 &game.panel.bd_magic_wall,
593 GAME_CONTROL_DC_MAGIC_WALL,
594 &game.panel.dc_magic_wall,
598 GAME_CONTROL_PLAYER_NAME,
599 &game.panel.player_name,
603 GAME_CONTROL_LEVEL_NAME,
604 &game.panel.level_name,
608 GAME_CONTROL_LEVEL_AUTHOR,
609 &game.panel.level_author,
622 /* values for delayed check of falling and moving elements and for collision */
623 #define CHECK_DELAY_MOVING 3
624 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
625 #define CHECK_DELAY_COLLISION 2
626 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
628 /* values for initial player move delay (initial delay counter value) */
629 #define INITIAL_MOVE_DELAY_OFF -1
630 #define INITIAL_MOVE_DELAY_ON 0
632 /* values for player movement speed (which is in fact a delay value) */
633 #define MOVE_DELAY_MIN_SPEED 32
634 #define MOVE_DELAY_NORMAL_SPEED 8
635 #define MOVE_DELAY_HIGH_SPEED 4
636 #define MOVE_DELAY_MAX_SPEED 1
638 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
639 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
641 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
642 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
644 /* values for other actions */
645 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
646 #define MOVE_STEPSIZE_MIN (1)
647 #define MOVE_STEPSIZE_MAX (TILEX)
649 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
650 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
652 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
654 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
655 RND(element_info[e].push_delay_random))
656 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
657 RND(element_info[e].drop_delay_random))
658 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
659 RND(element_info[e].move_delay_random))
660 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
661 (element_info[e].move_delay_random))
662 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
663 RND(element_info[e].ce_value_random_initial))
664 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
665 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
666 RND((c)->delay_random * (c)->delay_frames))
667 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
668 RND((c)->delay_random))
671 #define GET_VALID_RUNTIME_ELEMENT(e) \
672 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
674 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
675 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
676 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
677 (be) + (e) - EL_SELF)
679 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
680 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
681 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
682 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
683 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
684 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
685 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
686 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
687 RESOLVED_REFERENCE_ELEMENT(be, e) : \
690 #define CAN_GROW_INTO(e) \
691 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
693 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
694 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
697 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
698 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
699 (CAN_MOVE_INTO_ACID(e) && \
700 Feld[x][y] == EL_ACID) || \
703 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
704 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
705 (CAN_MOVE_INTO_ACID(e) && \
706 Feld[x][y] == EL_ACID) || \
709 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
710 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
712 (CAN_MOVE_INTO_ACID(e) && \
713 Feld[x][y] == EL_ACID) || \
714 (DONT_COLLIDE_WITH(e) && \
716 !PLAYER_ENEMY_PROTECTED(x, y))))
718 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
719 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
721 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
722 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
724 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
725 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
727 #define ANDROID_CAN_CLONE_FIELD(x, y) \
728 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
729 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
731 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
732 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
734 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
735 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
737 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
738 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
740 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
741 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
743 #define PIG_CAN_ENTER_FIELD(e, x, y) \
744 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
746 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
747 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
748 Feld[x][y] == EL_EM_EXIT_OPEN || \
749 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
750 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
751 IS_FOOD_PENGUIN(Feld[x][y])))
752 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
753 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
755 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
756 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
758 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
759 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
761 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
762 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
763 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
765 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
767 #define CE_ENTER_FIELD_COND(e, x, y) \
768 (!IS_PLAYER(x, y) && \
769 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
771 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
772 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
774 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
775 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
777 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
778 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
779 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
780 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
782 /* game button identifiers */
783 #define GAME_CTRL_ID_STOP 0
784 #define GAME_CTRL_ID_PAUSE 1
785 #define GAME_CTRL_ID_PLAY 2
786 #define SOUND_CTRL_ID_MUSIC 3
787 #define SOUND_CTRL_ID_LOOPS 4
788 #define SOUND_CTRL_ID_SIMPLE 5
790 #define NUM_GAME_BUTTONS 6
793 /* forward declaration for internal use */
795 static void CreateField(int, int, int);
797 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
798 static void AdvanceFrameAndPlayerCounters(int);
800 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
801 static boolean MovePlayer(struct PlayerInfo *, int, int);
802 static void ScrollPlayer(struct PlayerInfo *, int);
803 static void ScrollScreen(struct PlayerInfo *, int);
805 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
807 static void InitBeltMovement(void);
808 static void CloseAllOpenTimegates(void);
809 static void CheckGravityMovement(struct PlayerInfo *);
810 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
811 static void KillPlayerUnlessEnemyProtected(int, int);
812 static void KillPlayerUnlessExplosionProtected(int, int);
814 static void TestIfPlayerTouchesCustomElement(int, int);
815 static void TestIfElementTouchesCustomElement(int, int);
816 static void TestIfElementHitsCustomElement(int, int, int);
818 static void TestIfElementSmashesCustomElement(int, int, int);
821 static void HandleElementChange(int, int, int);
822 static void ExecuteCustomElementAction(int, int, int, int);
823 static boolean ChangeElement(int, int, int, int);
825 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
826 #define CheckTriggeredElementChange(x, y, e, ev) \
827 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
828 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
829 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
830 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
831 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
832 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
833 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
835 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
836 #define CheckElementChange(x, y, e, te, ev) \
837 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
838 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
839 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
840 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
841 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
843 static void PlayLevelSound(int, int, int);
844 static void PlayLevelSoundNearest(int, int, int);
845 static void PlayLevelSoundAction(int, int, int);
846 static void PlayLevelSoundElementAction(int, int, int, int);
847 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
848 static void PlayLevelSoundActionIfLoop(int, int, int);
849 static void StopLevelSoundActionIfLoop(int, int, int);
850 static void PlayLevelMusic();
852 static void MapGameButtons();
853 static void HandleGameButtons(struct GadgetInfo *);
855 int AmoebeNachbarNr(int, int);
856 void AmoebeUmwandeln(int, int);
857 void ContinueMoving(int, int);
859 void InitMovDir(int, int);
860 void InitAmoebaNr(int, int);
861 int NewHiScore(void);
863 void TestIfGoodThingHitsBadThing(int, int, int);
864 void TestIfBadThingHitsGoodThing(int, int, int);
865 void TestIfPlayerTouchesBadThing(int, int);
866 void TestIfPlayerRunsIntoBadThing(int, int, int);
867 void TestIfBadThingTouchesPlayer(int, int);
868 void TestIfBadThingRunsIntoPlayer(int, int, int);
869 void TestIfFriendTouchesBadThing(int, int);
870 void TestIfBadThingTouchesFriend(int, int);
871 void TestIfBadThingTouchesOtherBadThing(int, int);
873 void KillPlayer(struct PlayerInfo *);
874 void BuryPlayer(struct PlayerInfo *);
875 void RemovePlayer(struct PlayerInfo *);
877 boolean SnapField(struct PlayerInfo *, int, int);
878 boolean DropElement(struct PlayerInfo *);
880 static int getInvisibleActiveFromInvisibleElement(int);
881 static int getInvisibleFromInvisibleActiveElement(int);
883 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
885 /* for detection of endless loops, caused by custom element programming */
886 /* (using maximal playfield width x 10 is just a rough approximation) */
887 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
889 #define RECURSION_LOOP_DETECTION_START(e, rc) \
891 if (recursion_loop_detected) \
894 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
896 recursion_loop_detected = TRUE; \
897 recursion_loop_element = (e); \
900 recursion_loop_depth++; \
903 #define RECURSION_LOOP_DETECTION_END() \
905 recursion_loop_depth--; \
908 static int recursion_loop_depth;
909 static boolean recursion_loop_detected;
910 static boolean recursion_loop_element;
913 /* ------------------------------------------------------------------------- */
914 /* definition of elements that automatically change to other elements after */
915 /* a specified time, eventually calling a function when changing */
916 /* ------------------------------------------------------------------------- */
918 /* forward declaration for changer functions */
919 static void InitBuggyBase(int, int);
920 static void WarnBuggyBase(int, int);
922 static void InitTrap(int, int);
923 static void ActivateTrap(int, int);
924 static void ChangeActiveTrap(int, int);
926 static void InitRobotWheel(int, int);
927 static void RunRobotWheel(int, int);
928 static void StopRobotWheel(int, int);
930 static void InitTimegateWheel(int, int);
931 static void RunTimegateWheel(int, int);
933 static void InitMagicBallDelay(int, int);
934 static void ActivateMagicBall(int, int);
936 struct ChangingElementInfo
941 void (*pre_change_function)(int x, int y);
942 void (*change_function)(int x, int y);
943 void (*post_change_function)(int x, int y);
946 static struct ChangingElementInfo change_delay_list[] =
981 EL_STEEL_EXIT_OPENING,
989 EL_STEEL_EXIT_CLOSING,
990 EL_STEEL_EXIT_CLOSED,
1017 EL_EM_STEEL_EXIT_OPENING,
1018 EL_EM_STEEL_EXIT_OPEN,
1025 EL_EM_STEEL_EXIT_CLOSING,
1029 EL_EM_STEEL_EXIT_CLOSED,
1053 EL_SWITCHGATE_OPENING,
1061 EL_SWITCHGATE_CLOSING,
1062 EL_SWITCHGATE_CLOSED,
1069 EL_TIMEGATE_OPENING,
1077 EL_TIMEGATE_CLOSING,
1086 EL_ACID_SPLASH_LEFT,
1094 EL_ACID_SPLASH_RIGHT,
1103 EL_SP_BUGGY_BASE_ACTIVATING,
1110 EL_SP_BUGGY_BASE_ACTIVATING,
1111 EL_SP_BUGGY_BASE_ACTIVE,
1118 EL_SP_BUGGY_BASE_ACTIVE,
1142 EL_ROBOT_WHEEL_ACTIVE,
1150 EL_TIMEGATE_SWITCH_ACTIVE,
1158 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1159 EL_DC_TIMEGATE_SWITCH,
1166 EL_EMC_MAGIC_BALL_ACTIVE,
1167 EL_EMC_MAGIC_BALL_ACTIVE,
1174 EL_EMC_SPRING_BUMPER_ACTIVE,
1175 EL_EMC_SPRING_BUMPER,
1182 EL_DIAGONAL_SHRINKING,
1190 EL_DIAGONAL_GROWING,
1211 int push_delay_fixed, push_delay_random;
1215 { EL_SPRING, 0, 0 },
1216 { EL_BALLOON, 0, 0 },
1218 { EL_SOKOBAN_OBJECT, 2, 0 },
1219 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1220 { EL_SATELLITE, 2, 0 },
1221 { EL_SP_DISK_YELLOW, 2, 0 },
1223 { EL_UNDEFINED, 0, 0 },
1231 move_stepsize_list[] =
1233 { EL_AMOEBA_DROP, 2 },
1234 { EL_AMOEBA_DROPPING, 2 },
1235 { EL_QUICKSAND_FILLING, 1 },
1236 { EL_QUICKSAND_EMPTYING, 1 },
1237 { EL_QUICKSAND_FAST_FILLING, 2 },
1238 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1239 { EL_MAGIC_WALL_FILLING, 2 },
1240 { EL_MAGIC_WALL_EMPTYING, 2 },
1241 { EL_BD_MAGIC_WALL_FILLING, 2 },
1242 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1243 { EL_DC_MAGIC_WALL_FILLING, 2 },
1244 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1246 { EL_UNDEFINED, 0 },
1254 collect_count_list[] =
1257 { EL_BD_DIAMOND, 1 },
1258 { EL_EMERALD_YELLOW, 1 },
1259 { EL_EMERALD_RED, 1 },
1260 { EL_EMERALD_PURPLE, 1 },
1262 { EL_SP_INFOTRON, 1 },
1266 { EL_UNDEFINED, 0 },
1274 access_direction_list[] =
1276 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1277 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1278 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1279 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1280 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1281 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1282 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1283 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1284 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1285 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1286 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1288 { EL_SP_PORT_LEFT, MV_RIGHT },
1289 { EL_SP_PORT_RIGHT, MV_LEFT },
1290 { EL_SP_PORT_UP, MV_DOWN },
1291 { EL_SP_PORT_DOWN, MV_UP },
1292 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1293 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1294 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1295 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1296 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1297 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1298 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1299 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1300 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1301 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1302 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1303 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1304 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1305 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1306 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1308 { EL_UNDEFINED, MV_NONE }
1311 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1313 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1314 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1315 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1316 IS_JUST_CHANGING(x, y))
1318 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1320 /* static variables for playfield scan mode (scanning forward or backward) */
1321 static int playfield_scan_start_x = 0;
1322 static int playfield_scan_start_y = 0;
1323 static int playfield_scan_delta_x = 1;
1324 static int playfield_scan_delta_y = 1;
1326 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1327 (y) >= 0 && (y) <= lev_fieldy - 1; \
1328 (y) += playfield_scan_delta_y) \
1329 for ((x) = playfield_scan_start_x; \
1330 (x) >= 0 && (x) <= lev_fieldx - 1; \
1331 (x) += playfield_scan_delta_x)
1334 void DEBUG_SetMaximumDynamite()
1338 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1339 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1340 local_player->inventory_element[local_player->inventory_size++] =
1345 static void InitPlayfieldScanModeVars()
1347 if (game.use_reverse_scan_direction)
1349 playfield_scan_start_x = lev_fieldx - 1;
1350 playfield_scan_start_y = lev_fieldy - 1;
1352 playfield_scan_delta_x = -1;
1353 playfield_scan_delta_y = -1;
1357 playfield_scan_start_x = 0;
1358 playfield_scan_start_y = 0;
1360 playfield_scan_delta_x = 1;
1361 playfield_scan_delta_y = 1;
1365 static void InitPlayfieldScanMode(int mode)
1367 game.use_reverse_scan_direction =
1368 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1370 InitPlayfieldScanModeVars();
1373 static int get_move_delay_from_stepsize(int move_stepsize)
1376 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1378 /* make sure that stepsize value is always a power of 2 */
1379 move_stepsize = (1 << log_2(move_stepsize));
1381 return TILEX / move_stepsize;
1384 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1387 int player_nr = player->index_nr;
1388 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1389 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1391 /* do no immediately change move delay -- the player might just be moving */
1392 player->move_delay_value_next = move_delay;
1394 /* information if player can move must be set separately */
1395 player->cannot_move = cannot_move;
1399 player->move_delay = game.initial_move_delay[player_nr];
1400 player->move_delay_value = game.initial_move_delay_value[player_nr];
1402 player->move_delay_value_next = -1;
1404 player->move_delay_reset_counter = 0;
1408 void GetPlayerConfig()
1410 GameFrameDelay = setup.game_frame_delay;
1412 if (!audio.sound_available)
1413 setup.sound_simple = FALSE;
1415 if (!audio.loops_available)
1416 setup.sound_loops = FALSE;
1418 if (!audio.music_available)
1419 setup.sound_music = FALSE;
1421 if (!video.fullscreen_available)
1422 setup.fullscreen = FALSE;
1424 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1426 SetAudioMode(setup.sound);
1430 int GetElementFromGroupElement(int element)
1432 if (IS_GROUP_ELEMENT(element))
1434 struct ElementGroupInfo *group = element_info[element].group;
1435 int last_anim_random_frame = gfx.anim_random_frame;
1438 if (group->choice_mode == ANIM_RANDOM)
1439 gfx.anim_random_frame = RND(group->num_elements_resolved);
1441 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1442 group->choice_mode, 0,
1445 if (group->choice_mode == ANIM_RANDOM)
1446 gfx.anim_random_frame = last_anim_random_frame;
1448 group->choice_pos++;
1450 element = group->element_resolved[element_pos];
1456 static void InitPlayerField(int x, int y, int element, boolean init_game)
1458 if (element == EL_SP_MURPHY)
1462 if (stored_player[0].present)
1464 Feld[x][y] = EL_SP_MURPHY_CLONE;
1470 stored_player[0].use_murphy = TRUE;
1472 if (!level.use_artwork_element[0])
1473 stored_player[0].artwork_element = EL_SP_MURPHY;
1476 Feld[x][y] = EL_PLAYER_1;
1482 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1483 int jx = player->jx, jy = player->jy;
1485 player->present = TRUE;
1487 player->block_last_field = (element == EL_SP_MURPHY ?
1488 level.sp_block_last_field :
1489 level.block_last_field);
1491 /* ---------- initialize player's last field block delay --------------- */
1493 /* always start with reliable default value (no adjustment needed) */
1494 player->block_delay_adjustment = 0;
1496 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1497 if (player->block_last_field && element == EL_SP_MURPHY)
1498 player->block_delay_adjustment = 1;
1500 /* special case 2: in game engines before 3.1.1, blocking was different */
1501 if (game.use_block_last_field_bug)
1502 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1504 if (!options.network || player->connected)
1506 player->active = TRUE;
1508 /* remove potentially duplicate players */
1509 if (StorePlayer[jx][jy] == Feld[x][y])
1510 StorePlayer[jx][jy] = 0;
1512 StorePlayer[x][y] = Feld[x][y];
1516 printf("Player %d activated.\n", player->element_nr);
1517 printf("[Local player is %d and currently %s.]\n",
1518 local_player->element_nr,
1519 local_player->active ? "active" : "not active");
1523 Feld[x][y] = EL_EMPTY;
1525 player->jx = player->last_jx = x;
1526 player->jy = player->last_jy = y;
1530 static void InitField(int x, int y, boolean init_game)
1532 int element = Feld[x][y];
1541 InitPlayerField(x, y, element, init_game);
1544 case EL_SOKOBAN_FIELD_PLAYER:
1545 element = Feld[x][y] = EL_PLAYER_1;
1546 InitField(x, y, init_game);
1548 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1549 InitField(x, y, init_game);
1552 case EL_SOKOBAN_FIELD_EMPTY:
1553 local_player->sokobanfields_still_needed++;
1557 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1558 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1559 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1560 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1561 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1562 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1563 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1564 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1565 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1566 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1575 case EL_SPACESHIP_RIGHT:
1576 case EL_SPACESHIP_UP:
1577 case EL_SPACESHIP_LEFT:
1578 case EL_SPACESHIP_DOWN:
1579 case EL_BD_BUTTERFLY:
1580 case EL_BD_BUTTERFLY_RIGHT:
1581 case EL_BD_BUTTERFLY_UP:
1582 case EL_BD_BUTTERFLY_LEFT:
1583 case EL_BD_BUTTERFLY_DOWN:
1585 case EL_BD_FIREFLY_RIGHT:
1586 case EL_BD_FIREFLY_UP:
1587 case EL_BD_FIREFLY_LEFT:
1588 case EL_BD_FIREFLY_DOWN:
1589 case EL_PACMAN_RIGHT:
1591 case EL_PACMAN_LEFT:
1592 case EL_PACMAN_DOWN:
1594 case EL_YAMYAM_LEFT:
1595 case EL_YAMYAM_RIGHT:
1597 case EL_YAMYAM_DOWN:
1598 case EL_DARK_YAMYAM:
1601 case EL_SP_SNIKSNAK:
1602 case EL_SP_ELECTRON:
1611 case EL_AMOEBA_FULL:
1616 case EL_AMOEBA_DROP:
1617 if (y == lev_fieldy - 1)
1619 Feld[x][y] = EL_AMOEBA_GROWING;
1620 Store[x][y] = EL_AMOEBA_WET;
1624 case EL_DYNAMITE_ACTIVE:
1625 case EL_SP_DISK_RED_ACTIVE:
1626 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1627 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1628 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1629 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1630 MovDelay[x][y] = 96;
1633 case EL_EM_DYNAMITE_ACTIVE:
1634 MovDelay[x][y] = 32;
1638 local_player->lights_still_needed++;
1642 local_player->friends_still_needed++;
1647 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1650 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1651 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1652 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1653 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1654 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1655 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1656 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1657 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1658 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1659 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1660 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1661 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1664 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1665 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1666 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1668 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1670 game.belt_dir[belt_nr] = belt_dir;
1671 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1673 else /* more than one switch -- set it like the first switch */
1675 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1680 #if !USE_BOTH_SWITCHGATE_SWITCHES
1681 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1683 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1686 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1688 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1692 case EL_LIGHT_SWITCH_ACTIVE:
1694 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1697 case EL_INVISIBLE_STEELWALL:
1698 case EL_INVISIBLE_WALL:
1699 case EL_INVISIBLE_SAND:
1700 if (game.light_time_left > 0 ||
1701 game.lenses_time_left > 0)
1702 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1705 case EL_EMC_MAGIC_BALL:
1706 if (game.ball_state)
1707 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1710 case EL_EMC_MAGIC_BALL_SWITCH:
1711 if (game.ball_state)
1712 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1716 if (IS_CUSTOM_ELEMENT(element))
1718 if (CAN_MOVE(element))
1721 #if USE_NEW_CUSTOM_VALUE
1722 if (!element_info[element].use_last_ce_value || init_game)
1723 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1726 else if (IS_GROUP_ELEMENT(element))
1728 Feld[x][y] = GetElementFromGroupElement(element);
1730 InitField(x, y, init_game);
1737 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1740 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1742 InitField(x, y, init_game);
1744 /* not needed to call InitMovDir() -- already done by InitField()! */
1745 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1746 CAN_MOVE(Feld[x][y]))
1750 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1752 int old_element = Feld[x][y];
1754 InitField(x, y, init_game);
1756 /* not needed to call InitMovDir() -- already done by InitField()! */
1757 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1758 CAN_MOVE(old_element) &&
1759 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1762 /* this case is in fact a combination of not less than three bugs:
1763 first, it calls InitMovDir() for elements that can move, although this is
1764 already done by InitField(); then, it checks the element that was at this
1765 field _before_ the call to InitField() (which can change it); lastly, it
1766 was not called for "mole with direction" elements, which were treated as
1767 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1773 static int get_key_element_from_nr(int key_nr)
1775 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1776 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1777 EL_EM_KEY_1 : EL_KEY_1);
1779 return key_base_element + key_nr;
1782 static int get_next_drop_element(struct PlayerInfo *player)
1784 return (player->inventory_size > 0 ?
1785 player->inventory_element[player->inventory_size - 1] :
1786 player->inventory_infinite_element != EL_UNDEFINED ?
1787 player->inventory_infinite_element :
1788 player->dynabombs_left > 0 ?
1789 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1793 static int get_drop_element_from_pos(struct PlayerInfo *player, int pos)
1795 /* pos >= 0: get element from bottom of the stack;
1796 pos < 0: get element from top of the stack */
1800 int min_inventory_size = -pos;
1801 int inventory_pos = player->inventory_size - min_inventory_size;
1802 int min_dynabombs_left = min_inventory_size - player->inventory_size;
1804 return (player->inventory_size >= min_inventory_size ?
1805 player->inventory_element[inventory_pos] :
1806 player->inventory_infinite_element != EL_UNDEFINED ?
1807 player->inventory_infinite_element :
1808 player->dynabombs_left >= min_dynabombs_left ?
1809 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1814 int min_dynabombs_left = pos + 1;
1815 int min_inventory_size = pos + 1 - player->dynabombs_left;
1816 int inventory_pos = pos - player->dynabombs_left;
1818 return (player->inventory_infinite_element != EL_UNDEFINED ?
1819 player->inventory_infinite_element :
1820 player->dynabombs_left >= min_dynabombs_left ?
1821 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1822 player->inventory_size >= min_inventory_size ?
1823 player->inventory_element[inventory_pos] :
1828 void InitGameControlValues()
1832 for (i = 0; game_controls[i].nr != -1; i++)
1834 int nr = game_controls[i].nr;
1835 int type = game_controls[i].type;
1836 struct TextPosInfo *pos = game_controls[i].pos;
1838 /* force update of game controls after initialization */
1839 game_control_value[nr] = last_game_control_value[nr] = -1;
1841 /* determine panel value width for later calculation of alignment */
1842 if (type == TYPE_INTEGER || type == TYPE_STRING)
1844 pos->width = pos->size * getFontWidth(pos->font);
1845 pos->height = getFontHeight(pos->font);
1847 else if (type == TYPE_ELEMENT)
1849 pos->width = pos->size;
1850 pos->height = pos->size;
1855 void UpdateGameControlValues()
1858 int time = (level.time == 0 ? TimePlayed : TimeLeft);
1859 int score = (local_player->LevelSolved ? local_player->score_final :
1860 local_player->score);
1862 game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1863 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1865 game_control_value[GAME_CONTROL_INVENTORY_COUNT] = 0;
1866 for (i = 0; i < MAX_NUM_KEYS; i++)
1867 game_control_value[GAME_CONTROL_KEY_1 + i] = EL_EMPTY;
1868 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_EMPTY;
1869 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1871 if (game.centered_player_nr == -1)
1873 for (i = 0; i < MAX_PLAYERS; i++)
1875 for (k = 0; k < MAX_NUM_KEYS; k++)
1876 if (stored_player[i].key[k])
1877 game_control_value[GAME_CONTROL_KEY_1 + k] =
1878 get_key_element_from_nr(k);
1880 game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1881 stored_player[i].inventory_size;
1883 if (stored_player[i].num_white_keys > 0)
1884 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1886 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1887 stored_player[i].num_white_keys;
1892 int player_nr = game.centered_player_nr;
1894 for (k = 0; k < MAX_NUM_KEYS; k++)
1895 if (stored_player[player_nr].key[k])
1896 game_control_value[GAME_CONTROL_KEY_1 + k] =
1897 get_key_element_from_nr(k);
1899 game_control_value[GAME_CONTROL_INVENTORY_COUNT] +=
1900 stored_player[player_nr].inventory_size;
1902 if (stored_player[player_nr].num_white_keys > 0)
1903 game_control_value[GAME_CONTROL_KEY_WHITE] = EL_DC_KEY_WHITE;
1905 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1906 stored_player[player_nr].num_white_keys;
1909 for (i = 0; i < 8; i++)
1911 game_control_value[GAME_CONTROL_INVENTORY_FIRST_1 + i] =
1912 get_drop_element_from_pos(local_player, i);
1913 game_control_value[GAME_CONTROL_INVENTORY_LAST_1 + i] =
1914 get_drop_element_from_pos(local_player, -i - 1);
1917 game_control_value[GAME_CONTROL_SCORE] = score;
1919 game_control_value[GAME_CONTROL_TIME] = time;
1921 game_control_value[GAME_CONTROL_TIME_HH] = time / 3600;
1922 game_control_value[GAME_CONTROL_TIME_MM] = (time / 60) % 60;
1923 game_control_value[GAME_CONTROL_TIME_SS] = time % 60;
1925 game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1926 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1928 game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1929 local_player->shield_normal_time_left;
1930 game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1931 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1933 game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1934 local_player->shield_deadly_time_left;
1936 if (local_player->gems_still_needed > 0 ||
1937 local_player->sokobanfields_still_needed > 0 ||
1938 local_player->lights_still_needed > 0)
1940 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_CLOSED;
1941 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_CLOSED;
1942 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_CLOSED;
1943 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_CLOSED;
1944 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1948 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_OPEN;
1949 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_OPEN;
1950 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_OPEN;
1951 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_OPEN;
1952 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1956 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1957 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1959 game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1960 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1961 game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1963 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1964 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1965 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1966 game.timegate_time_left;
1969 game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1971 game_control_value[GAME_CONTROL_EMC_LENSES] =
1972 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1973 game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1975 game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1976 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1977 game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1979 game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1980 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
1981 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1982 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
1983 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
1984 EL_BALLOON_SWITCH_NONE);
1986 game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1987 local_player->dynabomb_count;
1988 game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1989 local_player->dynabomb_size;
1990 game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1991 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1993 game_control_value[GAME_CONTROL_PENGUINS] =
1994 local_player->friends_still_needed;
1996 game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1997 local_player->sokobanfields_still_needed;
1998 game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1999 local_player->sokobanfields_still_needed;
2002 game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
2005 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
2006 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
2007 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
2008 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
2009 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
2010 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
2011 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
2012 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
2015 game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
2016 game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
2017 game.magic_wall_time_left;
2018 game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
2019 game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
2021 game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
2022 game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
2023 game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
2026 void DisplayGameControlValues()
2030 for (i = 0; game_controls[i].nr != -1; i++)
2032 int nr = game_controls[i].nr;
2033 int type = game_controls[i].type;
2034 struct TextPosInfo *pos = game_controls[i].pos;
2035 int value = game_control_value[nr];
2036 int last_value = last_game_control_value[nr];
2037 int size = pos->size;
2038 int font = pos->font;
2040 if (value == last_value)
2043 last_game_control_value[nr] = value;
2046 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2049 if (PANEL_DEACTIVATED(pos))
2052 if (type == TYPE_INTEGER)
2054 if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
2056 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2058 if (use_dynamic_size) /* use dynamic number of digits */
2060 int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
2061 int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
2062 int size2 = size1 + 1;
2063 int font1 = pos->font;
2064 int font2 = pos->font_alt;
2066 size = (value < value_change ? size1 : size2);
2067 font = (value < value_change ? font1 : font2);
2069 /* clear background if value just changed its size (dynamic digits) */
2070 if ((last_value < value_change) != (value < value_change))
2072 int width1 = size1 * getFontWidth(font1);
2073 int width2 = size2 * getFontWidth(font2);
2074 int max_width = MAX(width1, width2);
2075 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2077 pos->width = max_width;
2079 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2080 max_width, max_height);
2084 pos->width = size * getFontWidth(font);
2087 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2089 else if (type == TYPE_ELEMENT)
2091 int dst_x = PANEL_XPOS(pos);
2092 int dst_y = PANEL_YPOS(pos);
2094 if (value == EL_UNDEFINED || value == EL_EMPTY)
2096 int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2097 int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2099 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2100 size, size, dst_x, dst_y);
2104 int graphic = el2doorimg(value);
2106 DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size);
2109 else if (type == TYPE_STRING)
2111 char *s = (nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name :
2112 nr == GAME_CONTROL_LEVEL_NAME ? level.name :
2113 nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2117 char *s_cut = getStringCopyN(s, size);
2119 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2125 redraw_mask |= REDRAW_DOOR_1;
2129 void DrawGameValue_Emeralds(int value)
2131 struct TextPosInfo *pos = &game.panel.gems;
2133 int font_nr = pos->font;
2135 int font_nr = FONT_TEXT_2;
2137 int font_width = getFontWidth(font_nr);
2138 int chars = pos->size;
2141 return; /* !!! USE NEW STUFF !!! */
2144 if (PANEL_DEACTIVATED(pos))
2147 pos->width = chars * font_width;
2149 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2152 void DrawGameValue_Dynamite(int value)
2154 struct TextPosInfo *pos = &game.panel.inventory_count;
2156 int font_nr = pos->font;
2158 int font_nr = FONT_TEXT_2;
2160 int font_width = getFontWidth(font_nr);
2161 int chars = pos->size;
2164 return; /* !!! USE NEW STUFF !!! */
2167 if (PANEL_DEACTIVATED(pos))
2170 pos->width = chars * font_width;
2172 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2175 void DrawGameValue_Score(int value)
2177 struct TextPosInfo *pos = &game.panel.score;
2179 int font_nr = pos->font;
2181 int font_nr = FONT_TEXT_2;
2183 int font_width = getFontWidth(font_nr);
2184 int chars = pos->size;
2187 return; /* !!! USE NEW STUFF !!! */
2190 if (PANEL_DEACTIVATED(pos))
2193 pos->width = chars * font_width;
2195 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2198 void DrawGameValue_Time(int value)
2200 struct TextPosInfo *pos = &game.panel.time;
2201 static int last_value = -1;
2204 int chars = pos->size;
2206 int font1_nr = pos->font;
2207 int font2_nr = pos->font_alt;
2209 int font1_nr = FONT_TEXT_2;
2210 int font2_nr = FONT_TEXT_1;
2212 int font_nr = font1_nr;
2213 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2216 return; /* !!! USE NEW STUFF !!! */
2219 if (PANEL_DEACTIVATED(pos))
2222 if (use_dynamic_chars) /* use dynamic number of chars */
2224 chars = (value < 1000 ? chars1 : chars2);
2225 font_nr = (value < 1000 ? font1_nr : font2_nr);
2228 /* clear background if value just changed its size (dynamic chars only) */
2229 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2231 int width1 = chars1 * getFontWidth(font1_nr);
2232 int width2 = chars2 * getFontWidth(font2_nr);
2233 int max_width = MAX(width1, width2);
2234 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2236 pos->width = max_width;
2238 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2239 max_width, max_height);
2242 pos->width = chars * getFontWidth(font_nr);
2244 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2249 void DrawGameValue_Level(int value)
2251 struct TextPosInfo *pos = &game.panel.level_number;
2254 int chars = pos->size;
2256 int font1_nr = pos->font;
2257 int font2_nr = pos->font_alt;
2259 int font1_nr = FONT_TEXT_2;
2260 int font2_nr = FONT_TEXT_1;
2262 int font_nr = font1_nr;
2263 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2266 return; /* !!! USE NEW STUFF !!! */
2269 if (PANEL_DEACTIVATED(pos))
2272 if (use_dynamic_chars) /* use dynamic number of chars */
2274 chars = (level_nr < 100 ? chars1 : chars2);
2275 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2278 pos->width = chars * getFontWidth(font_nr);
2280 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2283 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2286 struct TextPosInfo *pos = &game.panel.keys;
2289 int base_key_graphic = EL_KEY_1;
2294 return; /* !!! USE NEW STUFF !!! */
2298 if (PANEL_DEACTIVATED(pos))
2303 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2304 base_key_graphic = EL_EM_KEY_1;
2308 pos->width = 4 * MINI_TILEX;
2312 for (i = 0; i < MAX_NUM_KEYS; i++)
2314 /* currently only 4 of 8 possible keys are displayed */
2315 for (i = 0; i < STD_NUM_KEYS; i++)
2319 struct TextPosInfo *pos = &game.panel.key[i];
2321 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2322 int src_y = DOOR_GFX_PAGEY1 + 123;
2324 int dst_x = PANEL_XPOS(pos);
2325 int dst_y = PANEL_YPOS(pos);
2327 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2328 int dst_y = PANEL_YPOS(pos);
2332 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2333 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2335 int graphic = el2edimg(element);
2339 if (PANEL_DEACTIVATED(pos))
2344 /* masked blit with tiles from half-size scaled bitmap does not work yet
2345 (no mask bitmap created for these sizes after loading and scaling) --
2346 solution: load without creating mask, scale, then create final mask */
2348 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2349 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2354 int graphic = el2edimg(base_key_graphic + i);
2359 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2361 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2362 dst_x - src_x, dst_y - src_y);
2363 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2369 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2371 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2372 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2375 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2377 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2378 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2386 void DrawGameValue_Emeralds(int value)
2388 int font_nr = FONT_TEXT_2;
2389 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2391 if (PANEL_DEACTIVATED(game.panel.gems))
2394 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2397 void DrawGameValue_Dynamite(int value)
2399 int font_nr = FONT_TEXT_2;
2400 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2402 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2405 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2408 void DrawGameValue_Score(int value)
2410 int font_nr = FONT_TEXT_2;
2411 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2413 if (PANEL_DEACTIVATED(game.panel.score))
2416 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2419 void DrawGameValue_Time(int value)
2421 int font1_nr = FONT_TEXT_2;
2423 int font2_nr = FONT_TEXT_1;
2425 int font2_nr = FONT_LEVEL_NUMBER;
2427 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2428 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2430 if (PANEL_DEACTIVATED(game.panel.time))
2433 /* clear background if value just changed its size */
2434 if (value == 999 || value == 1000)
2435 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2438 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2440 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2443 void DrawGameValue_Level(int value)
2445 int font1_nr = FONT_TEXT_2;
2447 int font2_nr = FONT_TEXT_1;
2449 int font2_nr = FONT_LEVEL_NUMBER;
2452 if (PANEL_DEACTIVATED(game.panel.level))
2456 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2458 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2461 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2463 int base_key_graphic = EL_KEY_1;
2466 if (PANEL_DEACTIVATED(game.panel.keys))
2469 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2470 base_key_graphic = EL_EM_KEY_1;
2472 /* currently only 4 of 8 possible keys are displayed */
2473 for (i = 0; i < STD_NUM_KEYS; i++)
2475 int x = XX_KEYS + i * MINI_TILEX;
2479 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2481 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2482 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2488 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2491 int key[MAX_NUM_KEYS];
2494 /* prevent EM engine from updating time/score values parallel to GameWon() */
2495 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2496 local_player->LevelSolved)
2499 for (i = 0; i < MAX_NUM_KEYS; i++)
2500 key[i] = key_bits & (1 << i);
2502 DrawGameValue_Level(level_nr);
2504 DrawGameValue_Emeralds(emeralds);
2505 DrawGameValue_Dynamite(dynamite);
2506 DrawGameValue_Score(score);
2507 DrawGameValue_Time(time);
2509 DrawGameValue_Keys(key);
2512 void DrawGameDoorValues()
2514 UpdateGameControlValues();
2515 DisplayGameControlValues();
2518 void DrawGameDoorValues_OLD()
2520 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2521 int dynamite_value = 0;
2522 int score_value = (local_player->LevelSolved ? local_player->score_final :
2523 local_player->score);
2524 int gems_value = local_player->gems_still_needed;
2528 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2530 DrawGameDoorValues_EM();
2535 if (game.centered_player_nr == -1)
2537 for (i = 0; i < MAX_PLAYERS; i++)
2539 for (j = 0; j < MAX_NUM_KEYS; j++)
2540 if (stored_player[i].key[j])
2541 key_bits |= (1 << j);
2543 dynamite_value += stored_player[i].inventory_size;
2548 int player_nr = game.centered_player_nr;
2550 for (i = 0; i < MAX_NUM_KEYS; i++)
2551 if (stored_player[player_nr].key[i])
2552 key_bits |= (1 << i);
2554 dynamite_value = stored_player[player_nr].inventory_size;
2557 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2563 =============================================================================
2565 -----------------------------------------------------------------------------
2566 initialize game engine due to level / tape version number
2567 =============================================================================
2570 static void InitGameEngine()
2572 int i, j, k, l, x, y;
2574 /* set game engine from tape file when re-playing, else from level file */
2575 game.engine_version = (tape.playing ? tape.engine_version :
2576 level.game_version);
2578 /* ---------------------------------------------------------------------- */
2579 /* set flags for bugs and changes according to active game engine version */
2580 /* ---------------------------------------------------------------------- */
2583 Summary of bugfix/change:
2584 Fixed handling for custom elements that change when pushed by the player.
2586 Fixed/changed in version:
2590 Before 3.1.0, custom elements that "change when pushing" changed directly
2591 after the player started pushing them (until then handled in "DigField()").
2592 Since 3.1.0, these custom elements are not changed until the "pushing"
2593 move of the element is finished (now handled in "ContinueMoving()").
2595 Affected levels/tapes:
2596 The first condition is generally needed for all levels/tapes before version
2597 3.1.0, which might use the old behaviour before it was changed; known tapes
2598 that are affected are some tapes from the level set "Walpurgis Gardens" by
2600 The second condition is an exception from the above case and is needed for
2601 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2602 above (including some development versions of 3.1.0), but before it was
2603 known that this change would break tapes like the above and was fixed in
2604 3.1.1, so that the changed behaviour was active although the engine version
2605 while recording maybe was before 3.1.0. There is at least one tape that is
2606 affected by this exception, which is the tape for the one-level set "Bug
2607 Machine" by Juergen Bonhagen.
2610 game.use_change_when_pushing_bug =
2611 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2613 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2614 tape.game_version < VERSION_IDENT(3,1,1,0)));
2617 Summary of bugfix/change:
2618 Fixed handling for blocking the field the player leaves when moving.
2620 Fixed/changed in version:
2624 Before 3.1.1, when "block last field when moving" was enabled, the field
2625 the player is leaving when moving was blocked for the time of the move,
2626 and was directly unblocked afterwards. This resulted in the last field
2627 being blocked for exactly one less than the number of frames of one player
2628 move. Additionally, even when blocking was disabled, the last field was
2629 blocked for exactly one frame.
2630 Since 3.1.1, due to changes in player movement handling, the last field
2631 is not blocked at all when blocking is disabled. When blocking is enabled,
2632 the last field is blocked for exactly the number of frames of one player
2633 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2634 last field is blocked for exactly one more than the number of frames of
2637 Affected levels/tapes:
2638 (!!! yet to be determined -- probably many !!!)
2641 game.use_block_last_field_bug =
2642 (game.engine_version < VERSION_IDENT(3,1,1,0));
2645 Summary of bugfix/change:
2646 Changed behaviour of CE changes with multiple changes per single frame.
2648 Fixed/changed in version:
2652 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2653 This resulted in race conditions where CEs seem to behave strange in some
2654 situations (where triggered CE changes were just skipped because there was
2655 already a CE change on that tile in the playfield in that engine frame).
2656 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2657 (The number of changes per frame must be limited in any case, because else
2658 it is easily possible to define CE changes that would result in an infinite
2659 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2660 should be set large enough so that it would only be reached in cases where
2661 the corresponding CE change conditions run into a loop. Therefore, it seems
2662 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2663 maximal number of change pages for custom elements.)
2665 Affected levels/tapes:
2669 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2670 game.max_num_changes_per_frame = 1;
2672 game.max_num_changes_per_frame =
2673 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2676 /* ---------------------------------------------------------------------- */
2678 /* default scan direction: scan playfield from top/left to bottom/right */
2679 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2681 /* dynamically adjust element properties according to game engine version */
2682 InitElementPropertiesEngine(game.engine_version);
2685 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2686 printf(" tape version == %06d [%s] [file: %06d]\n",
2687 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2689 printf(" => game.engine_version == %06d\n", game.engine_version);
2692 /* ---------- initialize player's initial move delay --------------------- */
2694 /* dynamically adjust player properties according to level information */
2695 for (i = 0; i < MAX_PLAYERS; i++)
2696 game.initial_move_delay_value[i] =
2697 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2699 /* dynamically adjust player properties according to game engine version */
2700 for (i = 0; i < MAX_PLAYERS; i++)
2701 game.initial_move_delay[i] =
2702 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2703 game.initial_move_delay_value[i] : 0);
2705 /* ---------- initialize player's initial push delay --------------------- */
2707 /* dynamically adjust player properties according to game engine version */
2708 game.initial_push_delay_value =
2709 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2711 /* ---------- initialize changing elements ------------------------------- */
2713 /* initialize changing elements information */
2714 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2716 struct ElementInfo *ei = &element_info[i];
2718 /* this pointer might have been changed in the level editor */
2719 ei->change = &ei->change_page[0];
2721 if (!IS_CUSTOM_ELEMENT(i))
2723 ei->change->target_element = EL_EMPTY_SPACE;
2724 ei->change->delay_fixed = 0;
2725 ei->change->delay_random = 0;
2726 ei->change->delay_frames = 1;
2729 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2731 ei->has_change_event[j] = FALSE;
2733 ei->event_page_nr[j] = 0;
2734 ei->event_page[j] = &ei->change_page[0];
2738 /* add changing elements from pre-defined list */
2739 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2741 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2742 struct ElementInfo *ei = &element_info[ch_delay->element];
2744 ei->change->target_element = ch_delay->target_element;
2745 ei->change->delay_fixed = ch_delay->change_delay;
2747 ei->change->pre_change_function = ch_delay->pre_change_function;
2748 ei->change->change_function = ch_delay->change_function;
2749 ei->change->post_change_function = ch_delay->post_change_function;
2751 ei->change->can_change = TRUE;
2752 ei->change->can_change_or_has_action = TRUE;
2754 ei->has_change_event[CE_DELAY] = TRUE;
2756 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2757 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2760 /* ---------- initialize internal run-time variables ------------- */
2762 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2764 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2766 for (j = 0; j < ei->num_change_pages; j++)
2768 ei->change_page[j].can_change_or_has_action =
2769 (ei->change_page[j].can_change |
2770 ei->change_page[j].has_action);
2774 /* add change events from custom element configuration */
2775 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2777 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2779 for (j = 0; j < ei->num_change_pages; j++)
2781 if (!ei->change_page[j].can_change_or_has_action)
2784 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2786 /* only add event page for the first page found with this event */
2787 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2789 ei->has_change_event[k] = TRUE;
2791 ei->event_page_nr[k] = j;
2792 ei->event_page[k] = &ei->change_page[j];
2798 /* ---------- initialize run-time trigger player and element ------------- */
2800 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2802 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2804 for (j = 0; j < ei->num_change_pages; j++)
2806 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2807 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2808 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2809 ei->change_page[j].actual_trigger_ce_value = 0;
2810 ei->change_page[j].actual_trigger_ce_score = 0;
2814 /* ---------- initialize trigger events ---------------------------------- */
2816 /* initialize trigger events information */
2817 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2818 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2819 trigger_events[i][j] = FALSE;
2821 /* add trigger events from element change event properties */
2822 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2824 struct ElementInfo *ei = &element_info[i];
2826 for (j = 0; j < ei->num_change_pages; j++)
2828 if (!ei->change_page[j].can_change_or_has_action)
2831 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2833 int trigger_element = ei->change_page[j].trigger_element;
2835 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2837 if (ei->change_page[j].has_event[k])
2839 if (IS_GROUP_ELEMENT(trigger_element))
2841 struct ElementGroupInfo *group =
2842 element_info[trigger_element].group;
2844 for (l = 0; l < group->num_elements_resolved; l++)
2845 trigger_events[group->element_resolved[l]][k] = TRUE;
2847 else if (trigger_element == EL_ANY_ELEMENT)
2848 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2849 trigger_events[l][k] = TRUE;
2851 trigger_events[trigger_element][k] = TRUE;
2858 /* ---------- initialize push delay -------------------------------------- */
2860 /* initialize push delay values to default */
2861 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2863 if (!IS_CUSTOM_ELEMENT(i))
2865 /* set default push delay values (corrected since version 3.0.7-1) */
2866 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2868 element_info[i].push_delay_fixed = 2;
2869 element_info[i].push_delay_random = 8;
2873 element_info[i].push_delay_fixed = 8;
2874 element_info[i].push_delay_random = 8;
2879 /* set push delay value for certain elements from pre-defined list */
2880 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2882 int e = push_delay_list[i].element;
2884 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2885 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2888 /* set push delay value for Supaplex elements for newer engine versions */
2889 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2891 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2893 if (IS_SP_ELEMENT(i))
2895 /* set SP push delay to just enough to push under a falling zonk */
2896 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2898 element_info[i].push_delay_fixed = delay;
2899 element_info[i].push_delay_random = 0;
2904 /* ---------- initialize move stepsize ----------------------------------- */
2906 /* initialize move stepsize values to default */
2907 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2908 if (!IS_CUSTOM_ELEMENT(i))
2909 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2911 /* set move stepsize value for certain elements from pre-defined list */
2912 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2914 int e = move_stepsize_list[i].element;
2916 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2919 /* ---------- initialize collect score ----------------------------------- */
2921 /* initialize collect score values for custom elements from initial value */
2922 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2923 if (IS_CUSTOM_ELEMENT(i))
2924 element_info[i].collect_score = element_info[i].collect_score_initial;
2926 /* ---------- initialize collect count ----------------------------------- */
2928 /* initialize collect count values for non-custom elements */
2929 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2930 if (!IS_CUSTOM_ELEMENT(i))
2931 element_info[i].collect_count_initial = 0;
2933 /* add collect count values for all elements from pre-defined list */
2934 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2935 element_info[collect_count_list[i].element].collect_count_initial =
2936 collect_count_list[i].count;
2938 /* ---------- initialize access direction -------------------------------- */
2940 /* initialize access direction values to default (access from every side) */
2941 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2942 if (!IS_CUSTOM_ELEMENT(i))
2943 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2945 /* set access direction value for certain elements from pre-defined list */
2946 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2947 element_info[access_direction_list[i].element].access_direction =
2948 access_direction_list[i].direction;
2950 /* ---------- initialize explosion content ------------------------------- */
2951 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2953 if (IS_CUSTOM_ELEMENT(i))
2956 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2958 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2960 element_info[i].content.e[x][y] =
2961 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2962 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2963 i == EL_PLAYER_3 ? EL_EMERALD :
2964 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2965 i == EL_MOLE ? EL_EMERALD_RED :
2966 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2967 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2968 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2969 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2970 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2971 i == EL_WALL_EMERALD ? EL_EMERALD :
2972 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2973 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2974 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2975 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2976 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2977 i == EL_WALL_PEARL ? EL_PEARL :
2978 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2983 /* ---------- initialize recursion detection ------------------------------ */
2984 recursion_loop_depth = 0;
2985 recursion_loop_detected = FALSE;
2986 recursion_loop_element = EL_UNDEFINED;
2988 /* ---------- initialize graphics engine ---------------------------------- */
2989 game.scroll_delay_value =
2990 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2991 setup.scroll_delay ? setup.scroll_delay_value : 0);
2992 game.scroll_delay_value =
2993 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2996 int get_num_special_action(int element, int action_first, int action_last)
2998 int num_special_action = 0;
3001 for (i = action_first; i <= action_last; i++)
3003 boolean found = FALSE;
3005 for (j = 0; j < NUM_DIRECTIONS; j++)
3006 if (el_act_dir2img(element, i, j) !=
3007 el_act_dir2img(element, ACTION_DEFAULT, j))
3011 num_special_action++;
3016 return num_special_action;
3021 =============================================================================
3023 -----------------------------------------------------------------------------
3024 initialize and start new game
3025 =============================================================================
3030 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3031 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3032 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3034 boolean do_fading = (game_status == GAME_MODE_MAIN);
3038 game_status = GAME_MODE_PLAYING;
3041 InitGameControlValues();
3043 /* don't play tapes over network */
3044 network_playing = (options.network && !tape.playing);
3046 for (i = 0; i < MAX_PLAYERS; i++)
3048 struct PlayerInfo *player = &stored_player[i];
3050 player->index_nr = i;
3051 player->index_bit = (1 << i);
3052 player->element_nr = EL_PLAYER_1 + i;
3054 player->present = FALSE;
3055 player->active = FALSE;
3056 player->killed = FALSE;
3059 player->effective_action = 0;
3060 player->programmed_action = 0;
3063 player->score_final = 0;
3065 player->gems_still_needed = level.gems_needed;
3066 player->sokobanfields_still_needed = 0;
3067 player->lights_still_needed = 0;
3068 player->friends_still_needed = 0;
3070 for (j = 0; j < MAX_NUM_KEYS; j++)
3071 player->key[j] = FALSE;
3073 player->num_white_keys = 0;
3075 player->dynabomb_count = 0;
3076 player->dynabomb_size = 1;
3077 player->dynabombs_left = 0;
3078 player->dynabomb_xl = FALSE;
3080 player->MovDir = MV_NONE;
3083 player->GfxDir = MV_NONE;
3084 player->GfxAction = ACTION_DEFAULT;
3086 player->StepFrame = 0;
3088 player->use_murphy = FALSE;
3089 player->artwork_element =
3090 (level.use_artwork_element[i] ? level.artwork_element[i] :
3091 player->element_nr);
3093 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3094 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3096 player->gravity = level.initial_player_gravity[i];
3098 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3100 player->actual_frame_counter = 0;
3102 player->step_counter = 0;
3104 player->last_move_dir = MV_NONE;
3106 player->is_active = FALSE;
3108 player->is_waiting = FALSE;
3109 player->is_moving = FALSE;
3110 player->is_auto_moving = FALSE;
3111 player->is_digging = FALSE;
3112 player->is_snapping = FALSE;
3113 player->is_collecting = FALSE;
3114 player->is_pushing = FALSE;
3115 player->is_switching = FALSE;
3116 player->is_dropping = FALSE;
3117 player->is_dropping_pressed = FALSE;
3119 player->is_bored = FALSE;
3120 player->is_sleeping = FALSE;
3122 player->frame_counter_bored = -1;
3123 player->frame_counter_sleeping = -1;
3125 player->anim_delay_counter = 0;
3126 player->post_delay_counter = 0;
3128 player->dir_waiting = MV_NONE;
3129 player->action_waiting = ACTION_DEFAULT;
3130 player->last_action_waiting = ACTION_DEFAULT;
3131 player->special_action_bored = ACTION_DEFAULT;
3132 player->special_action_sleeping = ACTION_DEFAULT;
3134 player->switch_x = -1;
3135 player->switch_y = -1;
3137 player->drop_x = -1;
3138 player->drop_y = -1;
3140 player->show_envelope = 0;
3142 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3144 player->push_delay = -1; /* initialized when pushing starts */
3145 player->push_delay_value = game.initial_push_delay_value;
3147 player->drop_delay = 0;
3148 player->drop_pressed_delay = 0;
3150 player->last_jx = -1;
3151 player->last_jy = -1;
3155 player->shield_normal_time_left = 0;
3156 player->shield_deadly_time_left = 0;
3158 player->inventory_infinite_element = EL_UNDEFINED;
3159 player->inventory_size = 0;
3161 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3162 SnapField(player, 0, 0);
3164 player->LevelSolved = FALSE;
3165 player->GameOver = FALSE;
3167 player->LevelSolved_GameWon = FALSE;
3168 player->LevelSolved_GameEnd = FALSE;
3169 player->LevelSolved_PanelOff = FALSE;
3170 player->LevelSolved_SaveTape = FALSE;
3171 player->LevelSolved_SaveScore = FALSE;
3174 network_player_action_received = FALSE;
3176 #if defined(NETWORK_AVALIABLE)
3177 /* initial null action */
3178 if (network_playing)
3179 SendToServer_MovePlayer(MV_NONE);
3188 TimeLeft = level.time;
3191 ScreenMovDir = MV_NONE;
3195 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3197 AllPlayersGone = FALSE;
3199 game.yamyam_content_nr = 0;
3200 game.magic_wall_active = FALSE;
3201 game.magic_wall_time_left = 0;
3202 game.light_time_left = 0;
3203 game.timegate_time_left = 0;
3204 game.switchgate_pos = 0;
3205 game.wind_direction = level.wind_direction_initial;
3207 #if !USE_PLAYER_GRAVITY
3208 game.gravity = FALSE;
3209 game.explosions_delayed = TRUE;
3212 game.lenses_time_left = 0;
3213 game.magnify_time_left = 0;
3215 game.ball_state = level.ball_state_initial;
3216 game.ball_content_nr = 0;
3218 game.envelope_active = FALSE;
3220 /* set focus to local player for network games, else to all players */
3221 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3222 game.centered_player_nr_next = game.centered_player_nr;
3223 game.set_centered_player = FALSE;
3225 if (network_playing && tape.recording)
3227 /* store client dependent player focus when recording network games */
3228 tape.centered_player_nr_next = game.centered_player_nr_next;
3229 tape.set_centered_player = TRUE;
3232 for (i = 0; i < NUM_BELTS; i++)
3234 game.belt_dir[i] = MV_NONE;
3235 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3238 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3239 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3241 SCAN_PLAYFIELD(x, y)
3243 Feld[x][y] = level.field[x][y];
3244 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3245 ChangeDelay[x][y] = 0;
3246 ChangePage[x][y] = -1;
3247 #if USE_NEW_CUSTOM_VALUE
3248 CustomValue[x][y] = 0; /* initialized in InitField() */
3250 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3252 WasJustMoving[x][y] = 0;
3253 WasJustFalling[x][y] = 0;
3254 CheckCollision[x][y] = 0;
3255 CheckImpact[x][y] = 0;
3257 Pushed[x][y] = FALSE;
3259 ChangeCount[x][y] = 0;
3260 ChangeEvent[x][y] = -1;
3262 ExplodePhase[x][y] = 0;
3263 ExplodeDelay[x][y] = 0;
3264 ExplodeField[x][y] = EX_TYPE_NONE;
3266 RunnerVisit[x][y] = 0;
3267 PlayerVisit[x][y] = 0;
3270 GfxRandom[x][y] = INIT_GFX_RANDOM();
3271 GfxElement[x][y] = EL_UNDEFINED;
3272 GfxAction[x][y] = ACTION_DEFAULT;
3273 GfxDir[x][y] = MV_NONE;
3276 SCAN_PLAYFIELD(x, y)
3278 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3280 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3282 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3285 InitField(x, y, TRUE);
3290 for (i = 0; i < MAX_PLAYERS; i++)
3292 struct PlayerInfo *player = &stored_player[i];
3294 /* set number of special actions for bored and sleeping animation */
3295 player->num_special_action_bored =
3296 get_num_special_action(player->artwork_element,
3297 ACTION_BORING_1, ACTION_BORING_LAST);
3298 player->num_special_action_sleeping =
3299 get_num_special_action(player->artwork_element,
3300 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3303 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3304 emulate_sb ? EMU_SOKOBAN :
3305 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3307 #if USE_NEW_ALL_SLIPPERY
3308 /* initialize type of slippery elements */
3309 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311 if (!IS_CUSTOM_ELEMENT(i))
3313 /* default: elements slip down either to the left or right randomly */
3314 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3316 /* SP style elements prefer to slip down on the left side */
3317 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3318 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3320 /* BD style elements prefer to slip down on the left side */
3321 if (game.emulation == EMU_BOULDERDASH)
3322 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3327 /* initialize explosion and ignition delay */
3328 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3330 if (!IS_CUSTOM_ELEMENT(i))
3333 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3334 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3335 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3336 int last_phase = (num_phase + 1) * delay;
3337 int half_phase = (num_phase / 2) * delay;
3339 element_info[i].explosion_delay = last_phase - 1;
3340 element_info[i].ignition_delay = half_phase;
3342 if (i == EL_BLACK_ORB)
3343 element_info[i].ignition_delay = 1;
3347 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3348 element_info[i].explosion_delay = 1;
3350 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3351 element_info[i].ignition_delay = 1;
3355 /* correct non-moving belts to start moving left */
3356 for (i = 0; i < NUM_BELTS; i++)
3357 if (game.belt_dir[i] == MV_NONE)
3358 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3360 /* check if any connected player was not found in playfield */
3361 for (i = 0; i < MAX_PLAYERS; i++)
3363 struct PlayerInfo *player = &stored_player[i];
3365 if (player->connected && !player->present)
3367 for (j = 0; j < MAX_PLAYERS; j++)
3369 struct PlayerInfo *some_player = &stored_player[j];
3370 int jx = some_player->jx, jy = some_player->jy;
3372 /* assign first free player found that is present in the playfield */
3373 if (some_player->present && !some_player->connected)
3375 player->present = TRUE;
3376 player->active = TRUE;
3378 some_player->present = FALSE;
3379 some_player->active = FALSE;
3381 player->artwork_element = some_player->artwork_element;
3383 player->block_last_field = some_player->block_last_field;
3384 player->block_delay_adjustment = some_player->block_delay_adjustment;
3386 StorePlayer[jx][jy] = player->element_nr;
3387 player->jx = player->last_jx = jx;
3388 player->jy = player->last_jy = jy;
3398 /* when playing a tape, eliminate all players who do not participate */
3400 for (i = 0; i < MAX_PLAYERS; i++)
3402 if (stored_player[i].active && !tape.player_participates[i])
3404 struct PlayerInfo *player = &stored_player[i];
3405 int jx = player->jx, jy = player->jy;
3407 player->active = FALSE;
3408 StorePlayer[jx][jy] = 0;
3409 Feld[jx][jy] = EL_EMPTY;
3413 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3415 /* when in single player mode, eliminate all but the first active player */
3417 for (i = 0; i < MAX_PLAYERS; i++)
3419 if (stored_player[i].active)
3421 for (j = i + 1; j < MAX_PLAYERS; j++)
3423 if (stored_player[j].active)
3425 struct PlayerInfo *player = &stored_player[j];
3426 int jx = player->jx, jy = player->jy;
3428 player->active = FALSE;
3429 player->present = FALSE;
3431 StorePlayer[jx][jy] = 0;
3432 Feld[jx][jy] = EL_EMPTY;
3439 /* when recording the game, store which players take part in the game */
3442 for (i = 0; i < MAX_PLAYERS; i++)
3443 if (stored_player[i].active)
3444 tape.player_participates[i] = TRUE;
3449 for (i = 0; i < MAX_PLAYERS; i++)
3451 struct PlayerInfo *player = &stored_player[i];
3453 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3458 if (local_player == player)
3459 printf("Player %d is local player.\n", i+1);
3463 if (BorderElement == EL_EMPTY)
3466 SBX_Right = lev_fieldx - SCR_FIELDX;
3468 SBY_Lower = lev_fieldy - SCR_FIELDY;
3473 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3475 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3478 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3479 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3481 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3482 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3484 /* if local player not found, look for custom element that might create
3485 the player (make some assumptions about the right custom element) */
3486 if (!local_player->present)
3488 int start_x = 0, start_y = 0;
3489 int found_rating = 0;
3490 int found_element = EL_UNDEFINED;
3491 int player_nr = local_player->index_nr;
3493 SCAN_PLAYFIELD(x, y)
3495 int element = Feld[x][y];
3500 if (level.use_start_element[player_nr] &&
3501 level.start_element[player_nr] == element &&
3508 found_element = element;
3511 if (!IS_CUSTOM_ELEMENT(element))
3514 if (CAN_CHANGE(element))
3516 for (i = 0; i < element_info[element].num_change_pages; i++)
3518 /* check for player created from custom element as single target */
3519 content = element_info[element].change_page[i].target_element;
3520 is_player = ELEM_IS_PLAYER(content);
3522 if (is_player && (found_rating < 3 ||
3523 (found_rating == 3 && element < found_element)))
3529 found_element = element;
3534 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3536 /* check for player created from custom element as explosion content */
3537 content = element_info[element].content.e[xx][yy];
3538 is_player = ELEM_IS_PLAYER(content);
3540 if (is_player && (found_rating < 2 ||
3541 (found_rating == 2 && element < found_element)))
3543 start_x = x + xx - 1;
3544 start_y = y + yy - 1;
3547 found_element = element;
3550 if (!CAN_CHANGE(element))
3553 for (i = 0; i < element_info[element].num_change_pages; i++)
3555 /* check for player created from custom element as extended target */
3557 element_info[element].change_page[i].target_content.e[xx][yy];
3559 is_player = ELEM_IS_PLAYER(content);
3561 if (is_player && (found_rating < 1 ||
3562 (found_rating == 1 && element < found_element)))
3564 start_x = x + xx - 1;
3565 start_y = y + yy - 1;
3568 found_element = element;
3574 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3575 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3578 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3579 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3584 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3585 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3586 local_player->jx - MIDPOSX);
3588 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3589 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3590 local_player->jy - MIDPOSY);
3595 if (!game.restart_level)
3596 CloseDoor(DOOR_CLOSE_1);
3599 if (level_editor_test_game)
3600 FadeSkipNextFadeIn();
3602 FadeSetEnterScreen();
3604 if (level_editor_test_game)
3605 fading = fading_none;
3607 fading = menu.destination;
3611 FadeOut(REDRAW_FIELD);
3614 FadeOut(REDRAW_FIELD);
3617 /* !!! FIX THIS (START) !!! */
3618 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3620 InitGameEngine_EM();
3622 /* blit playfield from scroll buffer to normal back buffer for fading in */
3623 BlitScreenToBitmap_EM(backbuffer);
3630 /* after drawing the level, correct some elements */
3631 if (game.timegate_time_left == 0)
3632 CloseAllOpenTimegates();
3634 /* blit playfield from scroll buffer to normal back buffer for fading in */
3635 if (setup.soft_scrolling)
3636 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3638 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3640 /* !!! FIX THIS (END) !!! */
3643 FadeIn(REDRAW_FIELD);
3646 FadeIn(REDRAW_FIELD);
3651 if (!game.restart_level)
3653 /* copy default game door content to main double buffer */
3654 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3655 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3658 SetPanelBackground();
3659 SetDrawBackgroundMask(REDRAW_DOOR_1);
3661 DrawGameDoorValues();
3663 if (!game.restart_level)
3667 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3668 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3669 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3673 /* copy actual game door content to door double buffer for OpenDoor() */
3674 BlitBitmap(drawto, bitmap_db_door,
3675 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3677 OpenDoor(DOOR_OPEN_ALL);
3679 PlaySound(SND_GAME_STARTING);
3681 if (setup.sound_music)
3684 KeyboardAutoRepeatOffUnlessAutoplay();
3688 for (i = 0; i < MAX_PLAYERS; i++)
3689 printf("Player %d %sactive.\n",
3690 i + 1, (stored_player[i].active ? "" : "not "));
3701 game.restart_level = FALSE;
3704 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3706 /* this is used for non-R'n'D game engines to update certain engine values */
3708 /* needed to determine if sounds are played within the visible screen area */
3709 scroll_x = actual_scroll_x;
3710 scroll_y = actual_scroll_y;
3713 void InitMovDir(int x, int y)
3715 int i, element = Feld[x][y];
3716 static int xy[4][2] =
3723 static int direction[3][4] =
3725 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3726 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3727 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3736 Feld[x][y] = EL_BUG;
3737 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3740 case EL_SPACESHIP_RIGHT:
3741 case EL_SPACESHIP_UP:
3742 case EL_SPACESHIP_LEFT:
3743 case EL_SPACESHIP_DOWN:
3744 Feld[x][y] = EL_SPACESHIP;
3745 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3748 case EL_BD_BUTTERFLY_RIGHT:
3749 case EL_BD_BUTTERFLY_UP:
3750 case EL_BD_BUTTERFLY_LEFT:
3751 case EL_BD_BUTTERFLY_DOWN:
3752 Feld[x][y] = EL_BD_BUTTERFLY;
3753 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3756 case EL_BD_FIREFLY_RIGHT:
3757 case EL_BD_FIREFLY_UP:
3758 case EL_BD_FIREFLY_LEFT:
3759 case EL_BD_FIREFLY_DOWN:
3760 Feld[x][y] = EL_BD_FIREFLY;
3761 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3764 case EL_PACMAN_RIGHT:
3766 case EL_PACMAN_LEFT:
3767 case EL_PACMAN_DOWN:
3768 Feld[x][y] = EL_PACMAN;
3769 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3772 case EL_YAMYAM_LEFT:
3773 case EL_YAMYAM_RIGHT:
3775 case EL_YAMYAM_DOWN:
3776 Feld[x][y] = EL_YAMYAM;
3777 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3780 case EL_SP_SNIKSNAK:
3781 MovDir[x][y] = MV_UP;
3784 case EL_SP_ELECTRON:
3785 MovDir[x][y] = MV_LEFT;
3792 Feld[x][y] = EL_MOLE;
3793 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3797 if (IS_CUSTOM_ELEMENT(element))
3799 struct ElementInfo *ei = &element_info[element];
3800 int move_direction_initial = ei->move_direction_initial;
3801 int move_pattern = ei->move_pattern;
3803 if (move_direction_initial == MV_START_PREVIOUS)
3805 if (MovDir[x][y] != MV_NONE)
3808 move_direction_initial = MV_START_AUTOMATIC;
3811 if (move_direction_initial == MV_START_RANDOM)
3812 MovDir[x][y] = 1 << RND(4);
3813 else if (move_direction_initial & MV_ANY_DIRECTION)
3814 MovDir[x][y] = move_direction_initial;
3815 else if (move_pattern == MV_ALL_DIRECTIONS ||
3816 move_pattern == MV_TURNING_LEFT ||
3817 move_pattern == MV_TURNING_RIGHT ||
3818 move_pattern == MV_TURNING_LEFT_RIGHT ||
3819 move_pattern == MV_TURNING_RIGHT_LEFT ||
3820 move_pattern == MV_TURNING_RANDOM)
3821 MovDir[x][y] = 1 << RND(4);
3822 else if (move_pattern == MV_HORIZONTAL)
3823 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3824 else if (move_pattern == MV_VERTICAL)
3825 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3826 else if (move_pattern & MV_ANY_DIRECTION)
3827 MovDir[x][y] = element_info[element].move_pattern;
3828 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3829 move_pattern == MV_ALONG_RIGHT_SIDE)
3831 /* use random direction as default start direction */
3832 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3833 MovDir[x][y] = 1 << RND(4);
3835 for (i = 0; i < NUM_DIRECTIONS; i++)
3837 int x1 = x + xy[i][0];
3838 int y1 = y + xy[i][1];
3840 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3842 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3843 MovDir[x][y] = direction[0][i];
3845 MovDir[x][y] = direction[1][i];
3854 MovDir[x][y] = 1 << RND(4);
3856 if (element != EL_BUG &&
3857 element != EL_SPACESHIP &&
3858 element != EL_BD_BUTTERFLY &&
3859 element != EL_BD_FIREFLY)
3862 for (i = 0; i < NUM_DIRECTIONS; i++)
3864 int x1 = x + xy[i][0];
3865 int y1 = y + xy[i][1];
3867 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3869 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3871 MovDir[x][y] = direction[0][i];
3874 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3875 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3877 MovDir[x][y] = direction[1][i];
3886 GfxDir[x][y] = MovDir[x][y];
3889 void InitAmoebaNr(int x, int y)
3892 int group_nr = AmoebeNachbarNr(x, y);
3896 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3898 if (AmoebaCnt[i] == 0)
3906 AmoebaNr[x][y] = group_nr;
3907 AmoebaCnt[group_nr]++;
3908 AmoebaCnt2[group_nr]++;
3911 static void PlayerWins(struct PlayerInfo *player)
3913 player->LevelSolved = TRUE;
3914 player->GameOver = TRUE;
3916 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3917 level.native_em_level->lev->score : player->score);
3922 static int time, time_final;
3923 static int score, score_final;
3924 static int game_over_delay_1 = 0;
3925 static int game_over_delay_2 = 0;
3926 int game_over_delay_value_1 = 50;
3927 int game_over_delay_value_2 = 50;
3929 if (!local_player->LevelSolved_GameWon)
3933 /* do not start end game actions before the player stops moving (to exit) */
3934 if (local_player->MovPos)
3937 local_player->LevelSolved_GameWon = TRUE;
3938 local_player->LevelSolved_SaveTape = tape.recording;
3939 local_player->LevelSolved_SaveScore = !tape.playing;
3941 if (tape.auto_play) /* tape might already be stopped here */
3942 tape.auto_play_level_solved = TRUE;
3948 game_over_delay_1 = game_over_delay_value_1;
3949 game_over_delay_2 = game_over_delay_value_2;
3951 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3952 score = score_final = local_player->score_final;
3957 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3959 else if (level.time == 0 && TimePlayed < 999)
3962 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3965 local_player->score_final = score_final;
3967 if (level_editor_test_game)
3970 score = score_final;
3973 game_control_value[GAME_CONTROL_TIME] = time;
3974 game_control_value[GAME_CONTROL_SCORE] = score;
3976 DisplayGameControlValues();
3978 DrawGameValue_Time(time);
3979 DrawGameValue_Score(score);
3983 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3985 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3987 /* close exit door after last player */
3988 if ((AllPlayersGone &&
3989 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3990 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3991 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3992 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3993 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3995 int element = Feld[ExitX][ExitY];
3998 if (element == EL_EM_EXIT_OPEN ||
3999 element == EL_EM_STEEL_EXIT_OPEN)
4006 Feld[ExitX][ExitY] =
4007 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4008 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4009 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4010 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4011 EL_EM_STEEL_EXIT_CLOSING);
4013 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4017 /* player disappears */
4018 DrawLevelField(ExitX, ExitY);
4021 for (i = 0; i < MAX_PLAYERS; i++)
4023 struct PlayerInfo *player = &stored_player[i];
4025 if (player->present)
4027 RemovePlayer(player);
4029 /* player disappears */
4030 DrawLevelField(player->jx, player->jy);
4035 PlaySound(SND_GAME_WINNING);
4038 if (game_over_delay_1 > 0)
4040 game_over_delay_1--;
4045 if (time != time_final)
4047 int time_to_go = ABS(time_final - time);
4048 int time_count_dir = (time < time_final ? +1 : -1);
4049 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4051 time += time_count_steps * time_count_dir;
4052 score += time_count_steps * level.score[SC_TIME_BONUS];
4055 game_control_value[GAME_CONTROL_TIME] = time;
4056 game_control_value[GAME_CONTROL_SCORE] = score;
4058 DisplayGameControlValues();
4060 DrawGameValue_Time(time);
4061 DrawGameValue_Score(score);
4064 if (time == time_final)
4065 StopSound(SND_GAME_LEVELTIME_BONUS);
4066 else if (setup.sound_loops)
4067 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4069 PlaySound(SND_GAME_LEVELTIME_BONUS);
4074 local_player->LevelSolved_PanelOff = TRUE;
4076 if (game_over_delay_2 > 0)
4078 game_over_delay_2--;
4091 boolean raise_level = FALSE;
4093 local_player->LevelSolved_GameEnd = TRUE;
4095 CloseDoor(DOOR_CLOSE_1);
4097 if (local_player->LevelSolved_SaveTape)
4104 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4106 SaveTape(tape.level_nr); /* ask to save tape */
4110 if (level_editor_test_game)
4112 game_status = GAME_MODE_MAIN;
4115 DrawAndFadeInMainMenu(REDRAW_FIELD);
4123 if (!local_player->LevelSolved_SaveScore)
4126 FadeOut(REDRAW_FIELD);
4129 game_status = GAME_MODE_MAIN;
4131 DrawAndFadeInMainMenu(REDRAW_FIELD);
4136 if (level_nr == leveldir_current->handicap_level)
4138 leveldir_current->handicap_level++;
4139 SaveLevelSetup_SeriesInfo();
4142 if (level_nr < leveldir_current->last_level)
4143 raise_level = TRUE; /* advance to next level */
4145 if ((hi_pos = NewHiScore()) >= 0)
4147 game_status = GAME_MODE_SCORES;
4149 DrawHallOfFame(hi_pos);
4160 FadeOut(REDRAW_FIELD);
4163 game_status = GAME_MODE_MAIN;
4171 DrawAndFadeInMainMenu(REDRAW_FIELD);
4180 LoadScore(level_nr);
4182 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4183 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4186 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4188 if (local_player->score_final > highscore[k].Score)
4190 /* player has made it to the hall of fame */
4192 if (k < MAX_SCORE_ENTRIES - 1)
4194 int m = MAX_SCORE_ENTRIES - 1;
4197 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4198 if (strEqual(setup.player_name, highscore[l].Name))
4200 if (m == k) /* player's new highscore overwrites his old one */
4204 for (l = m; l > k; l--)
4206 strcpy(highscore[l].Name, highscore[l - 1].Name);
4207 highscore[l].Score = highscore[l - 1].Score;
4214 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4215 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4216 highscore[k].Score = local_player->score_final;
4222 else if (!strncmp(setup.player_name, highscore[k].Name,
4223 MAX_PLAYER_NAME_LEN))
4224 break; /* player already there with a higher score */
4230 SaveScore(level_nr);
4235 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4237 int element = Feld[x][y];
4238 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4239 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4240 int horiz_move = (dx != 0);
4241 int sign = (horiz_move ? dx : dy);
4242 int step = sign * element_info[element].move_stepsize;
4244 /* special values for move stepsize for spring and things on conveyor belt */
4247 if (CAN_FALL(element) &&
4248 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4249 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4250 else if (element == EL_SPRING)
4251 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4257 inline static int getElementMoveStepsize(int x, int y)
4259 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4262 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4264 if (player->GfxAction != action || player->GfxDir != dir)
4267 printf("Player frame reset! (%d => %d, %d => %d)\n",
4268 player->GfxAction, action, player->GfxDir, dir);
4271 player->GfxAction = action;
4272 player->GfxDir = dir;
4274 player->StepFrame = 0;
4278 #if USE_GFX_RESET_GFX_ANIMATION
4279 static void ResetGfxFrame(int x, int y, boolean redraw)
4281 int element = Feld[x][y];
4282 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4283 int last_gfx_frame = GfxFrame[x][y];
4285 if (graphic_info[graphic].anim_global_sync)
4286 GfxFrame[x][y] = FrameCounter;
4287 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4288 GfxFrame[x][y] = CustomValue[x][y];
4289 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4290 GfxFrame[x][y] = element_info[element].collect_score;
4291 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4292 GfxFrame[x][y] = ChangeDelay[x][y];
4294 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4295 DrawLevelGraphicAnimation(x, y, graphic);
4299 static void ResetGfxAnimation(int x, int y)
4301 GfxAction[x][y] = ACTION_DEFAULT;
4302 GfxDir[x][y] = MovDir[x][y];
4305 #if USE_GFX_RESET_GFX_ANIMATION
4306 ResetGfxFrame(x, y, FALSE);
4310 static void ResetRandomAnimationValue(int x, int y)
4312 GfxRandom[x][y] = INIT_GFX_RANDOM();
4315 void InitMovingField(int x, int y, int direction)
4317 int element = Feld[x][y];
4318 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4319 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4322 boolean is_moving_before, is_moving_after;
4324 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4327 /* check if element was/is moving or being moved before/after mode change */
4330 is_moving_before = (WasJustMoving[x][y] != 0);
4332 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4333 is_moving_before = WasJustMoving[x][y];
4336 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4338 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4340 /* reset animation only for moving elements which change direction of moving
4341 or which just started or stopped moving
4342 (else CEs with property "can move" / "not moving" are reset each frame) */
4343 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4345 if (is_moving_before != is_moving_after ||
4346 direction != MovDir[x][y])
4347 ResetGfxAnimation(x, y);
4349 if ((is_moving_before || is_moving_after) && !continues_moving)
4350 ResetGfxAnimation(x, y);
4353 if (!continues_moving)
4354 ResetGfxAnimation(x, y);
4357 MovDir[x][y] = direction;
4358 GfxDir[x][y] = direction;
4360 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4361 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4362 direction == MV_DOWN && CAN_FALL(element) ?
4363 ACTION_FALLING : ACTION_MOVING);
4365 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4366 ACTION_FALLING : ACTION_MOVING);
4369 /* this is needed for CEs with property "can move" / "not moving" */
4371 if (is_moving_after)
4373 if (Feld[newx][newy] == EL_EMPTY)
4374 Feld[newx][newy] = EL_BLOCKED;
4376 MovDir[newx][newy] = MovDir[x][y];
4378 #if USE_NEW_CUSTOM_VALUE
4379 CustomValue[newx][newy] = CustomValue[x][y];
4382 GfxFrame[newx][newy] = GfxFrame[x][y];
4383 GfxRandom[newx][newy] = GfxRandom[x][y];
4384 GfxAction[newx][newy] = GfxAction[x][y];
4385 GfxDir[newx][newy] = GfxDir[x][y];
4389 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4391 int direction = MovDir[x][y];
4392 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4393 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4399 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4401 int oldx = x, oldy = y;
4402 int direction = MovDir[x][y];
4404 if (direction == MV_LEFT)
4406 else if (direction == MV_RIGHT)
4408 else if (direction == MV_UP)
4410 else if (direction == MV_DOWN)
4413 *comes_from_x = oldx;
4414 *comes_from_y = oldy;
4417 int MovingOrBlocked2Element(int x, int y)
4419 int element = Feld[x][y];
4421 if (element == EL_BLOCKED)
4425 Blocked2Moving(x, y, &oldx, &oldy);
4426 return Feld[oldx][oldy];
4432 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4434 /* like MovingOrBlocked2Element(), but if element is moving
4435 and (x,y) is the field the moving element is just leaving,
4436 return EL_BLOCKED instead of the element value */
4437 int element = Feld[x][y];
4439 if (IS_MOVING(x, y))
4441 if (element == EL_BLOCKED)
4445 Blocked2Moving(x, y, &oldx, &oldy);
4446 return Feld[oldx][oldy];
4455 static void RemoveField(int x, int y)
4457 Feld[x][y] = EL_EMPTY;
4463 #if USE_NEW_CUSTOM_VALUE
4464 CustomValue[x][y] = 0;
4468 ChangeDelay[x][y] = 0;
4469 ChangePage[x][y] = -1;
4470 Pushed[x][y] = FALSE;
4473 ExplodeField[x][y] = EX_TYPE_NONE;
4476 GfxElement[x][y] = EL_UNDEFINED;
4477 GfxAction[x][y] = ACTION_DEFAULT;
4478 GfxDir[x][y] = MV_NONE;
4481 void RemoveMovingField(int x, int y)
4483 int oldx = x, oldy = y, newx = x, newy = y;
4484 int element = Feld[x][y];
4485 int next_element = EL_UNDEFINED;
4487 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4490 if (IS_MOVING(x, y))
4492 Moving2Blocked(x, y, &newx, &newy);
4494 if (Feld[newx][newy] != EL_BLOCKED)
4496 /* element is moving, but target field is not free (blocked), but
4497 already occupied by something different (example: acid pool);
4498 in this case, only remove the moving field, but not the target */
4500 RemoveField(oldx, oldy);
4502 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4504 DrawLevelField(oldx, oldy);
4509 else if (element == EL_BLOCKED)
4511 Blocked2Moving(x, y, &oldx, &oldy);
4512 if (!IS_MOVING(oldx, oldy))
4516 if (element == EL_BLOCKED &&
4517 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4518 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4519 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4520 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4521 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4522 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4523 next_element = get_next_element(Feld[oldx][oldy]);
4525 RemoveField(oldx, oldy);
4526 RemoveField(newx, newy);
4528 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4530 if (next_element != EL_UNDEFINED)
4531 Feld[oldx][oldy] = next_element;
4533 DrawLevelField(oldx, oldy);
4534 DrawLevelField(newx, newy);
4537 void DrawDynamite(int x, int y)
4539 int sx = SCREENX(x), sy = SCREENY(y);
4540 int graphic = el2img(Feld[x][y]);
4543 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4546 if (IS_WALKABLE_INSIDE(Back[x][y]))
4550 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4551 else if (Store[x][y])
4552 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4554 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4556 if (Back[x][y] || Store[x][y])
4557 DrawGraphicThruMask(sx, sy, graphic, frame);
4559 DrawGraphic(sx, sy, graphic, frame);
4562 void CheckDynamite(int x, int y)
4564 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4568 if (MovDelay[x][y] != 0)
4571 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4577 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4582 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4584 boolean num_checked_players = 0;
4587 for (i = 0; i < MAX_PLAYERS; i++)
4589 if (stored_player[i].active)
4591 int sx = stored_player[i].jx;
4592 int sy = stored_player[i].jy;
4594 if (num_checked_players == 0)
4601 *sx1 = MIN(*sx1, sx);
4602 *sy1 = MIN(*sy1, sy);
4603 *sx2 = MAX(*sx2, sx);
4604 *sy2 = MAX(*sy2, sy);
4607 num_checked_players++;
4612 static boolean checkIfAllPlayersFitToScreen_RND()
4614 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4616 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4618 return (sx2 - sx1 < SCR_FIELDX &&
4619 sy2 - sy1 < SCR_FIELDY);
4622 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4624 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4626 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4628 *sx = (sx1 + sx2) / 2;
4629 *sy = (sy1 + sy2) / 2;
4632 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4633 boolean center_screen, boolean quick_relocation)
4635 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4636 boolean no_delay = (tape.warp_forward);
4637 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4638 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4640 if (quick_relocation)
4642 int offset = game.scroll_delay_value;
4644 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4646 if (!level.shifted_relocation || center_screen)
4648 /* quick relocation (without scrolling), with centering of screen */
4650 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4651 x > SBX_Right + MIDPOSX ? SBX_Right :
4654 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4655 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4660 /* quick relocation (without scrolling), but do not center screen */
4662 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4663 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4666 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4667 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4670 int offset_x = x + (scroll_x - center_scroll_x);
4671 int offset_y = y + (scroll_y - center_scroll_y);
4673 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4674 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4675 offset_x - MIDPOSX);
4677 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4678 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4679 offset_y - MIDPOSY);
4684 /* quick relocation (without scrolling), inside visible screen area */
4686 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4687 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4688 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4690 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4691 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4692 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4694 /* don't scroll over playfield boundaries */
4695 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4696 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4698 /* don't scroll over playfield boundaries */
4699 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4700 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4703 RedrawPlayfield(TRUE, 0,0,0,0);
4708 int scroll_xx, scroll_yy;
4710 if (!level.shifted_relocation || center_screen)
4712 /* visible relocation (with scrolling), with centering of screen */
4714 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4715 x > SBX_Right + MIDPOSX ? SBX_Right :
4718 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4719 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4724 /* visible relocation (with scrolling), but do not center screen */
4726 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4727 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4730 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4731 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4734 int offset_x = x + (scroll_x - center_scroll_x);
4735 int offset_y = y + (scroll_y - center_scroll_y);
4737 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4738 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4739 offset_x - MIDPOSX);
4741 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4742 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4743 offset_y - MIDPOSY);
4748 /* visible relocation (with scrolling), with centering of screen */
4750 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4751 x > SBX_Right + MIDPOSX ? SBX_Right :
4754 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4755 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4759 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4761 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4764 int fx = FX, fy = FY;
4766 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4767 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4769 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4775 fx += dx * TILEX / 2;
4776 fy += dy * TILEY / 2;
4778 ScrollLevel(dx, dy);
4781 /* scroll in two steps of half tile size to make things smoother */
4782 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4784 Delay(wait_delay_value);
4786 /* scroll second step to align at full tile size */
4788 Delay(wait_delay_value);
4793 Delay(wait_delay_value);
4797 void RelocatePlayer(int jx, int jy, int el_player_raw)
4799 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4800 int player_nr = GET_PLAYER_NR(el_player);
4801 struct PlayerInfo *player = &stored_player[player_nr];
4802 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4803 boolean no_delay = (tape.warp_forward);
4804 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4805 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4806 int old_jx = player->jx;
4807 int old_jy = player->jy;
4808 int old_element = Feld[old_jx][old_jy];
4809 int element = Feld[jx][jy];
4810 boolean player_relocated = (old_jx != jx || old_jy != jy);
4812 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4813 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4814 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4815 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4816 int leave_side_horiz = move_dir_horiz;
4817 int leave_side_vert = move_dir_vert;
4818 int enter_side = enter_side_horiz | enter_side_vert;
4819 int leave_side = leave_side_horiz | leave_side_vert;
4821 if (player->GameOver) /* do not reanimate dead player */
4824 if (!player_relocated) /* no need to relocate the player */
4827 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4829 RemoveField(jx, jy); /* temporarily remove newly placed player */
4830 DrawLevelField(jx, jy);
4833 if (player->present)
4835 while (player->MovPos)
4837 ScrollPlayer(player, SCROLL_GO_ON);
4838 ScrollScreen(NULL, SCROLL_GO_ON);
4840 AdvanceFrameAndPlayerCounters(player->index_nr);
4845 Delay(wait_delay_value);
4848 DrawPlayer(player); /* needed here only to cleanup last field */
4849 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4851 player->is_moving = FALSE;
4854 if (IS_CUSTOM_ELEMENT(old_element))
4855 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4857 player->index_bit, leave_side);
4859 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4861 player->index_bit, leave_side);
4863 Feld[jx][jy] = el_player;
4864 InitPlayerField(jx, jy, el_player, TRUE);
4866 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4868 Feld[jx][jy] = element;
4869 InitField(jx, jy, FALSE);
4872 /* only visually relocate centered player */
4873 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4874 FALSE, level.instant_relocation);
4876 TestIfPlayerTouchesBadThing(jx, jy);
4877 TestIfPlayerTouchesCustomElement(jx, jy);
4879 if (IS_CUSTOM_ELEMENT(element))
4880 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4881 player->index_bit, enter_side);
4883 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4884 player->index_bit, enter_side);
4887 void Explode(int ex, int ey, int phase, int mode)
4893 /* !!! eliminate this variable !!! */
4894 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4896 if (game.explosions_delayed)
4898 ExplodeField[ex][ey] = mode;
4902 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4904 int center_element = Feld[ex][ey];
4905 int artwork_element, explosion_element; /* set these values later */
4908 /* --- This is only really needed (and now handled) in "Impact()". --- */
4909 /* do not explode moving elements that left the explode field in time */
4910 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4911 center_element == EL_EMPTY &&
4912 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4917 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4918 if (mode == EX_TYPE_NORMAL ||
4919 mode == EX_TYPE_CENTER ||
4920 mode == EX_TYPE_CROSS)
4921 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4924 /* remove things displayed in background while burning dynamite */
4925 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4928 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4930 /* put moving element to center field (and let it explode there) */
4931 center_element = MovingOrBlocked2Element(ex, ey);
4932 RemoveMovingField(ex, ey);
4933 Feld[ex][ey] = center_element;
4936 /* now "center_element" is finally determined -- set related values now */
4937 artwork_element = center_element; /* for custom player artwork */
4938 explosion_element = center_element; /* for custom player artwork */
4940 if (IS_PLAYER(ex, ey))
4942 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4944 artwork_element = stored_player[player_nr].artwork_element;
4946 if (level.use_explosion_element[player_nr])
4948 explosion_element = level.explosion_element[player_nr];
4949 artwork_element = explosion_element;
4954 if (mode == EX_TYPE_NORMAL ||
4955 mode == EX_TYPE_CENTER ||
4956 mode == EX_TYPE_CROSS)
4957 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4960 last_phase = element_info[explosion_element].explosion_delay + 1;
4962 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4964 int xx = x - ex + 1;
4965 int yy = y - ey + 1;
4968 if (!IN_LEV_FIELD(x, y) ||
4969 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4970 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4973 element = Feld[x][y];
4975 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4977 element = MovingOrBlocked2Element(x, y);
4979 if (!IS_EXPLOSION_PROOF(element))
4980 RemoveMovingField(x, y);
4983 /* indestructible elements can only explode in center (but not flames) */
4984 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4985 mode == EX_TYPE_BORDER)) ||
4986 element == EL_FLAMES)
4989 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4990 behaviour, for example when touching a yamyam that explodes to rocks
4991 with active deadly shield, a rock is created under the player !!! */
4992 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4994 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4995 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4996 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4998 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5001 if (IS_ACTIVE_BOMB(element))
5003 /* re-activate things under the bomb like gate or penguin */
5004 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5011 /* save walkable background elements while explosion on same tile */
5012 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5013 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5014 Back[x][y] = element;
5016 /* ignite explodable elements reached by other explosion */
5017 if (element == EL_EXPLOSION)
5018 element = Store2[x][y];
5020 if (AmoebaNr[x][y] &&
5021 (element == EL_AMOEBA_FULL ||
5022 element == EL_BD_AMOEBA ||
5023 element == EL_AMOEBA_GROWING))
5025 AmoebaCnt[AmoebaNr[x][y]]--;
5026 AmoebaCnt2[AmoebaNr[x][y]]--;
5031 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5033 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5035 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5037 if (PLAYERINFO(ex, ey)->use_murphy)
5038 Store[x][y] = EL_EMPTY;
5041 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5042 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5043 else if (ELEM_IS_PLAYER(center_element))
5044 Store[x][y] = EL_EMPTY;
5045 else if (center_element == EL_YAMYAM)
5046 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5047 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5048 Store[x][y] = element_info[center_element].content.e[xx][yy];
5050 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5051 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5052 otherwise) -- FIX THIS !!! */
5053 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5054 Store[x][y] = element_info[element].content.e[1][1];
5056 else if (!CAN_EXPLODE(element))
5057 Store[x][y] = element_info[element].content.e[1][1];
5060 Store[x][y] = EL_EMPTY;
5062 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5063 center_element == EL_AMOEBA_TO_DIAMOND)
5064 Store2[x][y] = element;
5066 Feld[x][y] = EL_EXPLOSION;
5067 GfxElement[x][y] = artwork_element;
5069 ExplodePhase[x][y] = 1;
5070 ExplodeDelay[x][y] = last_phase;
5075 if (center_element == EL_YAMYAM)
5076 game.yamyam_content_nr =
5077 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5089 GfxFrame[x][y] = 0; /* restart explosion animation */
5091 last_phase = ExplodeDelay[x][y];
5093 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5097 /* activate this even in non-DEBUG version until cause for crash in
5098 getGraphicAnimationFrame() (see below) is found and eliminated */
5104 /* this can happen if the player leaves an explosion just in time */
5105 if (GfxElement[x][y] == EL_UNDEFINED)
5106 GfxElement[x][y] = EL_EMPTY;
5108 if (GfxElement[x][y] == EL_UNDEFINED)
5111 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5112 printf("Explode(): This should never happen!\n");
5115 GfxElement[x][y] = EL_EMPTY;
5121 border_element = Store2[x][y];
5122 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5123 border_element = StorePlayer[x][y];
5125 if (phase == element_info[border_element].ignition_delay ||
5126 phase == last_phase)
5128 boolean border_explosion = FALSE;
5130 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5131 !PLAYER_EXPLOSION_PROTECTED(x, y))
5133 KillPlayerUnlessExplosionProtected(x, y);
5134 border_explosion = TRUE;
5136 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5138 Feld[x][y] = Store2[x][y];
5141 border_explosion = TRUE;
5143 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5145 AmoebeUmwandeln(x, y);
5147 border_explosion = TRUE;
5150 /* if an element just explodes due to another explosion (chain-reaction),
5151 do not immediately end the new explosion when it was the last frame of
5152 the explosion (as it would be done in the following "if"-statement!) */
5153 if (border_explosion && phase == last_phase)
5157 if (phase == last_phase)
5161 element = Feld[x][y] = Store[x][y];
5162 Store[x][y] = Store2[x][y] = 0;
5163 GfxElement[x][y] = EL_UNDEFINED;
5165 /* player can escape from explosions and might therefore be still alive */
5166 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5167 element <= EL_PLAYER_IS_EXPLODING_4)
5169 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5170 int explosion_element = EL_PLAYER_1 + player_nr;
5171 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5172 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5174 if (level.use_explosion_element[player_nr])
5175 explosion_element = level.explosion_element[player_nr];
5177 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5178 element_info[explosion_element].content.e[xx][yy]);
5181 /* restore probably existing indestructible background element */
5182 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5183 element = Feld[x][y] = Back[x][y];
5186 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5187 GfxDir[x][y] = MV_NONE;
5188 ChangeDelay[x][y] = 0;
5189 ChangePage[x][y] = -1;
5191 #if USE_NEW_CUSTOM_VALUE
5192 CustomValue[x][y] = 0;
5195 InitField_WithBug2(x, y, FALSE);
5197 DrawLevelField(x, y);
5199 TestIfElementTouchesCustomElement(x, y);
5201 if (GFX_CRUMBLED(element))
5202 DrawLevelFieldCrumbledSandNeighbours(x, y);
5204 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5205 StorePlayer[x][y] = 0;
5207 if (ELEM_IS_PLAYER(element))
5208 RelocatePlayer(x, y, element);
5210 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5212 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5213 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5216 DrawLevelFieldCrumbledSand(x, y);
5218 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5220 DrawLevelElement(x, y, Back[x][y]);
5221 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5223 else if (IS_WALKABLE_UNDER(Back[x][y]))
5225 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5226 DrawLevelElementThruMask(x, y, Back[x][y]);
5228 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5229 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5233 void DynaExplode(int ex, int ey)
5236 int dynabomb_element = Feld[ex][ey];
5237 int dynabomb_size = 1;
5238 boolean dynabomb_xl = FALSE;
5239 struct PlayerInfo *player;
5240 static int xy[4][2] =
5248 if (IS_ACTIVE_BOMB(dynabomb_element))
5250 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5251 dynabomb_size = player->dynabomb_size;
5252 dynabomb_xl = player->dynabomb_xl;
5253 player->dynabombs_left++;
5256 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5258 for (i = 0; i < NUM_DIRECTIONS; i++)
5260 for (j = 1; j <= dynabomb_size; j++)
5262 int x = ex + j * xy[i][0];
5263 int y = ey + j * xy[i][1];
5266 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5269 element = Feld[x][y];
5271 /* do not restart explosions of fields with active bombs */
5272 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5275 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5277 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5278 !IS_DIGGABLE(element) && !dynabomb_xl)
5284 void Bang(int x, int y)
5286 int element = MovingOrBlocked2Element(x, y);
5287 int explosion_type = EX_TYPE_NORMAL;
5289 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5291 struct PlayerInfo *player = PLAYERINFO(x, y);
5293 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5294 player->element_nr);
5296 if (level.use_explosion_element[player->index_nr])
5298 int explosion_element = level.explosion_element[player->index_nr];
5300 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5301 explosion_type = EX_TYPE_CROSS;
5302 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5303 explosion_type = EX_TYPE_CENTER;
5311 case EL_BD_BUTTERFLY:
5314 case EL_DARK_YAMYAM:
5318 RaiseScoreElement(element);
5321 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5322 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5323 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5324 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5325 case EL_DYNABOMB_INCREASE_NUMBER:
5326 case EL_DYNABOMB_INCREASE_SIZE:
5327 case EL_DYNABOMB_INCREASE_POWER:
5328 explosion_type = EX_TYPE_DYNA;
5331 case EL_DC_LANDMINE:
5333 case EL_EM_EXIT_OPEN:
5334 case EL_EM_STEEL_EXIT_OPEN:
5336 explosion_type = EX_TYPE_CENTER;
5341 case EL_LAMP_ACTIVE:
5342 case EL_AMOEBA_TO_DIAMOND:
5343 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5344 explosion_type = EX_TYPE_CENTER;
5348 if (element_info[element].explosion_type == EXPLODES_CROSS)
5349 explosion_type = EX_TYPE_CROSS;
5350 else if (element_info[element].explosion_type == EXPLODES_1X1)
5351 explosion_type = EX_TYPE_CENTER;
5355 if (explosion_type == EX_TYPE_DYNA)
5358 Explode(x, y, EX_PHASE_START, explosion_type);
5360 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5363 void SplashAcid(int x, int y)
5365 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5366 (!IN_LEV_FIELD(x - 1, y - 2) ||
5367 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5368 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5370 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5371 (!IN_LEV_FIELD(x + 1, y - 2) ||
5372 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5373 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5375 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5378 static void InitBeltMovement()
5380 static int belt_base_element[4] =
5382 EL_CONVEYOR_BELT_1_LEFT,
5383 EL_CONVEYOR_BELT_2_LEFT,
5384 EL_CONVEYOR_BELT_3_LEFT,
5385 EL_CONVEYOR_BELT_4_LEFT
5387 static int belt_base_active_element[4] =
5389 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5390 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5391 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5392 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5397 /* set frame order for belt animation graphic according to belt direction */
5398 for (i = 0; i < NUM_BELTS; i++)
5402 for (j = 0; j < NUM_BELT_PARTS; j++)
5404 int element = belt_base_active_element[belt_nr] + j;
5405 int graphic = el2img(element);
5407 if (game.belt_dir[i] == MV_LEFT)
5408 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5410 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5414 SCAN_PLAYFIELD(x, y)
5416 int element = Feld[x][y];
5418 for (i = 0; i < NUM_BELTS; i++)
5420 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5422 int e_belt_nr = getBeltNrFromBeltElement(element);
5425 if (e_belt_nr == belt_nr)
5427 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5429 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5436 static void ToggleBeltSwitch(int x, int y)
5438 static int belt_base_element[4] =
5440 EL_CONVEYOR_BELT_1_LEFT,
5441 EL_CONVEYOR_BELT_2_LEFT,
5442 EL_CONVEYOR_BELT_3_LEFT,
5443 EL_CONVEYOR_BELT_4_LEFT
5445 static int belt_base_active_element[4] =
5447 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5448 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5449 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5450 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5452 static int belt_base_switch_element[4] =
5454 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5455 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5456 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5457 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5459 static int belt_move_dir[4] =
5467 int element = Feld[x][y];
5468 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5469 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5470 int belt_dir = belt_move_dir[belt_dir_nr];
5473 if (!IS_BELT_SWITCH(element))
5476 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5477 game.belt_dir[belt_nr] = belt_dir;
5479 if (belt_dir_nr == 3)
5482 /* set frame order for belt animation graphic according to belt direction */
5483 for (i = 0; i < NUM_BELT_PARTS; i++)
5485 int element = belt_base_active_element[belt_nr] + i;
5486 int graphic = el2img(element);
5488 if (belt_dir == MV_LEFT)
5489 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5491 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5494 SCAN_PLAYFIELD(xx, yy)
5496 int element = Feld[xx][yy];
5498 if (IS_BELT_SWITCH(element))
5500 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5502 if (e_belt_nr == belt_nr)
5504 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5505 DrawLevelField(xx, yy);
5508 else if (IS_BELT(element) && belt_dir != MV_NONE)
5510 int e_belt_nr = getBeltNrFromBeltElement(element);
5512 if (e_belt_nr == belt_nr)
5514 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5516 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5517 DrawLevelField(xx, yy);
5520 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5522 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5524 if (e_belt_nr == belt_nr)
5526 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5528 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5529 DrawLevelField(xx, yy);
5535 static void ToggleSwitchgateSwitch(int x, int y)
5539 game.switchgate_pos = !game.switchgate_pos;
5541 SCAN_PLAYFIELD(xx, yy)
5543 int element = Feld[xx][yy];
5545 #if !USE_BOTH_SWITCHGATE_SWITCHES
5546 if (element == EL_SWITCHGATE_SWITCH_UP ||
5547 element == EL_SWITCHGATE_SWITCH_DOWN)
5549 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5550 DrawLevelField(xx, yy);
5552 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5553 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5555 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5556 DrawLevelField(xx, yy);
5559 if (element == EL_SWITCHGATE_SWITCH_UP)
5561 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5562 DrawLevelField(xx, yy);
5564 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5566 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5567 DrawLevelField(xx, yy);
5569 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5571 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5572 DrawLevelField(xx, yy);
5574 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5576 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5577 DrawLevelField(xx, yy);
5580 else if (element == EL_SWITCHGATE_OPEN ||
5581 element == EL_SWITCHGATE_OPENING)
5583 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5585 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5587 else if (element == EL_SWITCHGATE_CLOSED ||
5588 element == EL_SWITCHGATE_CLOSING)
5590 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5592 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5597 static int getInvisibleActiveFromInvisibleElement(int element)
5599 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5600 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5601 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5605 static int getInvisibleFromInvisibleActiveElement(int element)
5607 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5608 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5609 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5613 static void RedrawAllLightSwitchesAndInvisibleElements()
5617 SCAN_PLAYFIELD(x, y)
5619 int element = Feld[x][y];
5621 if (element == EL_LIGHT_SWITCH &&
5622 game.light_time_left > 0)
5624 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5625 DrawLevelField(x, y);
5627 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5628 game.light_time_left == 0)
5630 Feld[x][y] = EL_LIGHT_SWITCH;
5631 DrawLevelField(x, y);
5633 else if (element == EL_EMC_DRIPPER &&
5634 game.light_time_left > 0)
5636 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5637 DrawLevelField(x, y);
5639 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5640 game.light_time_left == 0)
5642 Feld[x][y] = EL_EMC_DRIPPER;
5643 DrawLevelField(x, y);
5645 else if (element == EL_INVISIBLE_STEELWALL ||
5646 element == EL_INVISIBLE_WALL ||
5647 element == EL_INVISIBLE_SAND)
5649 if (game.light_time_left > 0)
5650 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5652 DrawLevelField(x, y);
5654 /* uncrumble neighbour fields, if needed */
5655 if (element == EL_INVISIBLE_SAND)
5656 DrawLevelFieldCrumbledSandNeighbours(x, y);
5658 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5659 element == EL_INVISIBLE_WALL_ACTIVE ||
5660 element == EL_INVISIBLE_SAND_ACTIVE)
5662 if (game.light_time_left == 0)
5663 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5665 DrawLevelField(x, y);
5667 /* re-crumble neighbour fields, if needed */
5668 if (element == EL_INVISIBLE_SAND)
5669 DrawLevelFieldCrumbledSandNeighbours(x, y);
5674 static void RedrawAllInvisibleElementsForLenses()
5678 SCAN_PLAYFIELD(x, y)
5680 int element = Feld[x][y];
5682 if (element == EL_EMC_DRIPPER &&
5683 game.lenses_time_left > 0)
5685 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5686 DrawLevelField(x, y);
5688 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5689 game.lenses_time_left == 0)
5691 Feld[x][y] = EL_EMC_DRIPPER;
5692 DrawLevelField(x, y);
5694 else if (element == EL_INVISIBLE_STEELWALL ||
5695 element == EL_INVISIBLE_WALL ||
5696 element == EL_INVISIBLE_SAND)
5698 if (game.lenses_time_left > 0)
5699 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5701 DrawLevelField(x, y);
5703 /* uncrumble neighbour fields, if needed */
5704 if (element == EL_INVISIBLE_SAND)
5705 DrawLevelFieldCrumbledSandNeighbours(x, y);
5707 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5708 element == EL_INVISIBLE_WALL_ACTIVE ||
5709 element == EL_INVISIBLE_SAND_ACTIVE)
5711 if (game.lenses_time_left == 0)
5712 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5714 DrawLevelField(x, y);
5716 /* re-crumble neighbour fields, if needed */
5717 if (element == EL_INVISIBLE_SAND)
5718 DrawLevelFieldCrumbledSandNeighbours(x, y);
5723 static void RedrawAllInvisibleElementsForMagnifier()
5727 SCAN_PLAYFIELD(x, y)
5729 int element = Feld[x][y];
5731 if (element == EL_EMC_FAKE_GRASS &&
5732 game.magnify_time_left > 0)
5734 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5735 DrawLevelField(x, y);
5737 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5738 game.magnify_time_left == 0)
5740 Feld[x][y] = EL_EMC_FAKE_GRASS;
5741 DrawLevelField(x, y);
5743 else if (IS_GATE_GRAY(element) &&
5744 game.magnify_time_left > 0)
5746 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5747 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5748 IS_EM_GATE_GRAY(element) ?
5749 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5750 IS_EMC_GATE_GRAY(element) ?
5751 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5753 DrawLevelField(x, y);
5755 else if (IS_GATE_GRAY_ACTIVE(element) &&
5756 game.magnify_time_left == 0)
5758 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5759 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5760 IS_EM_GATE_GRAY_ACTIVE(element) ?
5761 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5762 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5763 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5765 DrawLevelField(x, y);
5770 static void ToggleLightSwitch(int x, int y)
5772 int element = Feld[x][y];
5774 game.light_time_left =
5775 (element == EL_LIGHT_SWITCH ?
5776 level.time_light * FRAMES_PER_SECOND : 0);
5778 RedrawAllLightSwitchesAndInvisibleElements();
5781 static void ActivateTimegateSwitch(int x, int y)
5785 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5787 SCAN_PLAYFIELD(xx, yy)
5789 int element = Feld[xx][yy];
5791 if (element == EL_TIMEGATE_CLOSED ||
5792 element == EL_TIMEGATE_CLOSING)
5794 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5795 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5799 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5801 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5802 DrawLevelField(xx, yy);
5809 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5810 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5812 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5816 void Impact(int x, int y)
5818 boolean last_line = (y == lev_fieldy - 1);
5819 boolean object_hit = FALSE;
5820 boolean impact = (last_line || object_hit);
5821 int element = Feld[x][y];
5822 int smashed = EL_STEELWALL;
5824 if (!last_line) /* check if element below was hit */
5826 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5829 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5830 MovDir[x][y + 1] != MV_DOWN ||
5831 MovPos[x][y + 1] <= TILEY / 2));
5833 /* do not smash moving elements that left the smashed field in time */
5834 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5835 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5838 #if USE_QUICKSAND_IMPACT_BUGFIX
5839 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5841 RemoveMovingField(x, y + 1);
5842 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5843 Feld[x][y + 2] = EL_ROCK;
5844 DrawLevelField(x, y + 2);
5849 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5851 RemoveMovingField(x, y + 1);
5852 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5853 Feld[x][y + 2] = EL_ROCK;
5854 DrawLevelField(x, y + 2);
5861 smashed = MovingOrBlocked2Element(x, y + 1);
5863 impact = (last_line || object_hit);
5866 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5868 SplashAcid(x, y + 1);
5872 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5873 /* only reset graphic animation if graphic really changes after impact */
5875 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5877 ResetGfxAnimation(x, y);
5878 DrawLevelField(x, y);
5881 if (impact && CAN_EXPLODE_IMPACT(element))
5886 else if (impact && element == EL_PEARL &&
5887 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5889 ResetGfxAnimation(x, y);
5891 Feld[x][y] = EL_PEARL_BREAKING;
5892 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5895 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5897 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5902 if (impact && element == EL_AMOEBA_DROP)
5904 if (object_hit && IS_PLAYER(x, y + 1))
5905 KillPlayerUnlessEnemyProtected(x, y + 1);
5906 else if (object_hit && smashed == EL_PENGUIN)
5910 Feld[x][y] = EL_AMOEBA_GROWING;
5911 Store[x][y] = EL_AMOEBA_WET;
5913 ResetRandomAnimationValue(x, y);
5918 if (object_hit) /* check which object was hit */
5920 if ((CAN_PASS_MAGIC_WALL(element) &&
5921 (smashed == EL_MAGIC_WALL ||
5922 smashed == EL_BD_MAGIC_WALL)) ||
5923 (CAN_PASS_DC_MAGIC_WALL(element) &&
5924 smashed == EL_DC_MAGIC_WALL))
5927 int activated_magic_wall =
5928 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5929 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5930 EL_DC_MAGIC_WALL_ACTIVE);
5932 /* activate magic wall / mill */
5933 SCAN_PLAYFIELD(xx, yy)
5935 if (Feld[xx][yy] == smashed)
5936 Feld[xx][yy] = activated_magic_wall;
5939 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5940 game.magic_wall_active = TRUE;
5942 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5943 SND_MAGIC_WALL_ACTIVATING :
5944 smashed == EL_BD_MAGIC_WALL ?
5945 SND_BD_MAGIC_WALL_ACTIVATING :
5946 SND_DC_MAGIC_WALL_ACTIVATING));
5949 if (IS_PLAYER(x, y + 1))
5951 if (CAN_SMASH_PLAYER(element))
5953 KillPlayerUnlessEnemyProtected(x, y + 1);
5957 else if (smashed == EL_PENGUIN)
5959 if (CAN_SMASH_PLAYER(element))
5965 else if (element == EL_BD_DIAMOND)
5967 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5973 else if (((element == EL_SP_INFOTRON ||
5974 element == EL_SP_ZONK) &&
5975 (smashed == EL_SP_SNIKSNAK ||
5976 smashed == EL_SP_ELECTRON ||
5977 smashed == EL_SP_DISK_ORANGE)) ||
5978 (element == EL_SP_INFOTRON &&
5979 smashed == EL_SP_DISK_YELLOW))
5984 else if (CAN_SMASH_EVERYTHING(element))
5986 if (IS_CLASSIC_ENEMY(smashed) ||
5987 CAN_EXPLODE_SMASHED(smashed))
5992 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5994 if (smashed == EL_LAMP ||
5995 smashed == EL_LAMP_ACTIVE)
6000 else if (smashed == EL_NUT)
6002 Feld[x][y + 1] = EL_NUT_BREAKING;
6003 PlayLevelSound(x, y, SND_NUT_BREAKING);
6004 RaiseScoreElement(EL_NUT);
6007 else if (smashed == EL_PEARL)
6009 ResetGfxAnimation(x, y);
6011 Feld[x][y + 1] = EL_PEARL_BREAKING;
6012 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6015 else if (smashed == EL_DIAMOND)
6017 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6018 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6021 else if (IS_BELT_SWITCH(smashed))
6023 ToggleBeltSwitch(x, y + 1);
6025 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6026 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6027 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6028 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6030 ToggleSwitchgateSwitch(x, y + 1);
6032 else if (smashed == EL_LIGHT_SWITCH ||
6033 smashed == EL_LIGHT_SWITCH_ACTIVE)
6035 ToggleLightSwitch(x, y + 1);
6040 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6043 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6045 CheckElementChangeBySide(x, y + 1, smashed, element,
6046 CE_SWITCHED, CH_SIDE_TOP);
6047 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6053 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6058 /* play sound of magic wall / mill */
6060 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6061 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6062 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6064 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6065 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6066 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6067 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6068 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6069 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6074 /* play sound of object that hits the ground */
6075 if (last_line || object_hit)
6076 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6079 inline static void TurnRoundExt(int x, int y)
6091 { 0, 0 }, { 0, 0 }, { 0, 0 },
6096 int left, right, back;
6100 { MV_DOWN, MV_UP, MV_RIGHT },
6101 { MV_UP, MV_DOWN, MV_LEFT },
6103 { MV_LEFT, MV_RIGHT, MV_DOWN },
6107 { MV_RIGHT, MV_LEFT, MV_UP }
6110 int element = Feld[x][y];
6111 int move_pattern = element_info[element].move_pattern;
6113 int old_move_dir = MovDir[x][y];
6114 int left_dir = turn[old_move_dir].left;
6115 int right_dir = turn[old_move_dir].right;
6116 int back_dir = turn[old_move_dir].back;
6118 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6119 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6120 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6121 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6123 int left_x = x + left_dx, left_y = y + left_dy;
6124 int right_x = x + right_dx, right_y = y + right_dy;
6125 int move_x = x + move_dx, move_y = y + move_dy;
6129 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6131 TestIfBadThingTouchesOtherBadThing(x, y);
6133 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6134 MovDir[x][y] = right_dir;
6135 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6136 MovDir[x][y] = left_dir;
6138 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6140 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6143 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6145 TestIfBadThingTouchesOtherBadThing(x, y);
6147 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6148 MovDir[x][y] = left_dir;
6149 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6150 MovDir[x][y] = right_dir;
6152 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6154 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6157 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6159 TestIfBadThingTouchesOtherBadThing(x, y);
6161 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6162 MovDir[x][y] = left_dir;
6163 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6164 MovDir[x][y] = right_dir;
6166 if (MovDir[x][y] != old_move_dir)
6169 else if (element == EL_YAMYAM)
6171 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6172 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6174 if (can_turn_left && can_turn_right)
6175 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6176 else if (can_turn_left)
6177 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6178 else if (can_turn_right)
6179 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6181 MovDir[x][y] = back_dir;
6183 MovDelay[x][y] = 16 + 16 * RND(3);
6185 else if (element == EL_DARK_YAMYAM)
6187 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6189 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6192 if (can_turn_left && can_turn_right)
6193 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6194 else if (can_turn_left)
6195 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6196 else if (can_turn_right)
6197 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6199 MovDir[x][y] = back_dir;
6201 MovDelay[x][y] = 16 + 16 * RND(3);
6203 else if (element == EL_PACMAN)
6205 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6206 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6208 if (can_turn_left && can_turn_right)
6209 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6210 else if (can_turn_left)
6211 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6212 else if (can_turn_right)
6213 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6215 MovDir[x][y] = back_dir;
6217 MovDelay[x][y] = 6 + RND(40);
6219 else if (element == EL_PIG)
6221 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6222 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6223 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6224 boolean should_turn_left, should_turn_right, should_move_on;
6226 int rnd = RND(rnd_value);
6228 should_turn_left = (can_turn_left &&
6230 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6231 y + back_dy + left_dy)));
6232 should_turn_right = (can_turn_right &&
6234 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6235 y + back_dy + right_dy)));
6236 should_move_on = (can_move_on &&
6239 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6240 y + move_dy + left_dy) ||
6241 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6242 y + move_dy + right_dy)));
6244 if (should_turn_left || should_turn_right || should_move_on)
6246 if (should_turn_left && should_turn_right && should_move_on)
6247 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6248 rnd < 2 * rnd_value / 3 ? right_dir :
6250 else if (should_turn_left && should_turn_right)
6251 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6252 else if (should_turn_left && should_move_on)
6253 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6254 else if (should_turn_right && should_move_on)
6255 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6256 else if (should_turn_left)
6257 MovDir[x][y] = left_dir;
6258 else if (should_turn_right)
6259 MovDir[x][y] = right_dir;
6260 else if (should_move_on)
6261 MovDir[x][y] = old_move_dir;
6263 else if (can_move_on && rnd > rnd_value / 8)
6264 MovDir[x][y] = old_move_dir;
6265 else if (can_turn_left && can_turn_right)
6266 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6267 else if (can_turn_left && rnd > rnd_value / 8)
6268 MovDir[x][y] = left_dir;
6269 else if (can_turn_right && rnd > rnd_value/8)
6270 MovDir[x][y] = right_dir;
6272 MovDir[x][y] = back_dir;
6274 xx = x + move_xy[MovDir[x][y]].dx;
6275 yy = y + move_xy[MovDir[x][y]].dy;
6277 if (!IN_LEV_FIELD(xx, yy) ||
6278 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6279 MovDir[x][y] = old_move_dir;
6283 else if (element == EL_DRAGON)
6285 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6286 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6287 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6289 int rnd = RND(rnd_value);
6291 if (can_move_on && rnd > rnd_value / 8)
6292 MovDir[x][y] = old_move_dir;
6293 else if (can_turn_left && can_turn_right)
6294 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6295 else if (can_turn_left && rnd > rnd_value / 8)
6296 MovDir[x][y] = left_dir;
6297 else if (can_turn_right && rnd > rnd_value / 8)
6298 MovDir[x][y] = right_dir;
6300 MovDir[x][y] = back_dir;
6302 xx = x + move_xy[MovDir[x][y]].dx;
6303 yy = y + move_xy[MovDir[x][y]].dy;
6305 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6306 MovDir[x][y] = old_move_dir;
6310 else if (element == EL_MOLE)
6312 boolean can_move_on =
6313 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6314 IS_AMOEBOID(Feld[move_x][move_y]) ||
6315 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6318 boolean can_turn_left =
6319 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6320 IS_AMOEBOID(Feld[left_x][left_y])));
6322 boolean can_turn_right =
6323 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6324 IS_AMOEBOID(Feld[right_x][right_y])));
6326 if (can_turn_left && can_turn_right)
6327 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6328 else if (can_turn_left)
6329 MovDir[x][y] = left_dir;
6331 MovDir[x][y] = right_dir;
6334 if (MovDir[x][y] != old_move_dir)
6337 else if (element == EL_BALLOON)
6339 MovDir[x][y] = game.wind_direction;
6342 else if (element == EL_SPRING)
6344 #if USE_NEW_SPRING_BUMPER
6345 if (MovDir[x][y] & MV_HORIZONTAL)
6347 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6348 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6350 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6351 ResetGfxAnimation(move_x, move_y);
6352 DrawLevelField(move_x, move_y);
6354 MovDir[x][y] = back_dir;
6356 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6357 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6358 MovDir[x][y] = MV_NONE;
6361 if (MovDir[x][y] & MV_HORIZONTAL &&
6362 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6363 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6364 MovDir[x][y] = MV_NONE;
6369 else if (element == EL_ROBOT ||
6370 element == EL_SATELLITE ||
6371 element == EL_PENGUIN ||
6372 element == EL_EMC_ANDROID)
6374 int attr_x = -1, attr_y = -1;
6385 for (i = 0; i < MAX_PLAYERS; i++)
6387 struct PlayerInfo *player = &stored_player[i];
6388 int jx = player->jx, jy = player->jy;
6390 if (!player->active)
6394 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6402 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6403 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6404 game.engine_version < VERSION_IDENT(3,1,0,0)))
6410 if (element == EL_PENGUIN)
6413 static int xy[4][2] =
6421 for (i = 0; i < NUM_DIRECTIONS; i++)
6423 int ex = x + xy[i][0];
6424 int ey = y + xy[i][1];
6426 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6427 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6428 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6429 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6438 MovDir[x][y] = MV_NONE;
6440 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6441 else if (attr_x > x)
6442 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6444 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6445 else if (attr_y > y)
6446 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6448 if (element == EL_ROBOT)
6452 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6453 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6454 Moving2Blocked(x, y, &newx, &newy);
6456 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6457 MovDelay[x][y] = 8 + 8 * !RND(3);
6459 MovDelay[x][y] = 16;
6461 else if (element == EL_PENGUIN)
6467 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6469 boolean first_horiz = RND(2);
6470 int new_move_dir = MovDir[x][y];
6473 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6474 Moving2Blocked(x, y, &newx, &newy);
6476 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6480 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6481 Moving2Blocked(x, y, &newx, &newy);
6483 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6486 MovDir[x][y] = old_move_dir;
6490 else if (element == EL_SATELLITE)
6496 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6498 boolean first_horiz = RND(2);
6499 int new_move_dir = MovDir[x][y];
6502 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6503 Moving2Blocked(x, y, &newx, &newy);
6505 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6509 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6510 Moving2Blocked(x, y, &newx, &newy);
6512 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6515 MovDir[x][y] = old_move_dir;
6519 else if (element == EL_EMC_ANDROID)
6521 static int check_pos[16] =
6523 -1, /* 0 => (invalid) */
6524 7, /* 1 => MV_LEFT */
6525 3, /* 2 => MV_RIGHT */
6526 -1, /* 3 => (invalid) */
6528 0, /* 5 => MV_LEFT | MV_UP */
6529 2, /* 6 => MV_RIGHT | MV_UP */
6530 -1, /* 7 => (invalid) */
6531 5, /* 8 => MV_DOWN */
6532 6, /* 9 => MV_LEFT | MV_DOWN */
6533 4, /* 10 => MV_RIGHT | MV_DOWN */
6534 -1, /* 11 => (invalid) */
6535 -1, /* 12 => (invalid) */
6536 -1, /* 13 => (invalid) */
6537 -1, /* 14 => (invalid) */
6538 -1, /* 15 => (invalid) */
6546 { -1, -1, MV_LEFT | MV_UP },
6548 { +1, -1, MV_RIGHT | MV_UP },
6549 { +1, 0, MV_RIGHT },
6550 { +1, +1, MV_RIGHT | MV_DOWN },
6552 { -1, +1, MV_LEFT | MV_DOWN },
6555 int start_pos, check_order;
6556 boolean can_clone = FALSE;
6559 /* check if there is any free field around current position */
6560 for (i = 0; i < 8; i++)
6562 int newx = x + check_xy[i].dx;
6563 int newy = y + check_xy[i].dy;
6565 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6573 if (can_clone) /* randomly find an element to clone */
6577 start_pos = check_pos[RND(8)];
6578 check_order = (RND(2) ? -1 : +1);
6580 for (i = 0; i < 8; i++)
6582 int pos_raw = start_pos + i * check_order;
6583 int pos = (pos_raw + 8) % 8;
6584 int newx = x + check_xy[pos].dx;
6585 int newy = y + check_xy[pos].dy;
6587 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6589 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6590 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6592 Store[x][y] = Feld[newx][newy];
6601 if (can_clone) /* randomly find a direction to move */
6605 start_pos = check_pos[RND(8)];
6606 check_order = (RND(2) ? -1 : +1);
6608 for (i = 0; i < 8; i++)
6610 int pos_raw = start_pos + i * check_order;
6611 int pos = (pos_raw + 8) % 8;
6612 int newx = x + check_xy[pos].dx;
6613 int newy = y + check_xy[pos].dy;
6614 int new_move_dir = check_xy[pos].dir;
6616 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6618 MovDir[x][y] = new_move_dir;
6619 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6628 if (can_clone) /* cloning and moving successful */
6631 /* cannot clone -- try to move towards player */
6633 start_pos = check_pos[MovDir[x][y] & 0x0f];
6634 check_order = (RND(2) ? -1 : +1);
6636 for (i = 0; i < 3; i++)
6638 /* first check start_pos, then previous/next or (next/previous) pos */
6639 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6640 int pos = (pos_raw + 8) % 8;
6641 int newx = x + check_xy[pos].dx;
6642 int newy = y + check_xy[pos].dy;
6643 int new_move_dir = check_xy[pos].dir;
6645 if (IS_PLAYER(newx, newy))
6648 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6650 MovDir[x][y] = new_move_dir;
6651 MovDelay[x][y] = level.android_move_time * 8 + 1;
6658 else if (move_pattern == MV_TURNING_LEFT ||
6659 move_pattern == MV_TURNING_RIGHT ||
6660 move_pattern == MV_TURNING_LEFT_RIGHT ||
6661 move_pattern == MV_TURNING_RIGHT_LEFT ||
6662 move_pattern == MV_TURNING_RANDOM ||
6663 move_pattern == MV_ALL_DIRECTIONS)
6665 boolean can_turn_left =
6666 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6667 boolean can_turn_right =
6668 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6670 if (element_info[element].move_stepsize == 0) /* "not moving" */
6673 if (move_pattern == MV_TURNING_LEFT)
6674 MovDir[x][y] = left_dir;
6675 else if (move_pattern == MV_TURNING_RIGHT)
6676 MovDir[x][y] = right_dir;
6677 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6678 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6679 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6680 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6681 else if (move_pattern == MV_TURNING_RANDOM)
6682 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6683 can_turn_right && !can_turn_left ? right_dir :
6684 RND(2) ? left_dir : right_dir);
6685 else if (can_turn_left && can_turn_right)
6686 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6687 else if (can_turn_left)
6688 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6689 else if (can_turn_right)
6690 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6692 MovDir[x][y] = back_dir;
6694 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6696 else if (move_pattern == MV_HORIZONTAL ||
6697 move_pattern == MV_VERTICAL)
6699 if (move_pattern & old_move_dir)
6700 MovDir[x][y] = back_dir;
6701 else if (move_pattern == MV_HORIZONTAL)
6702 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6703 else if (move_pattern == MV_VERTICAL)
6704 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6706 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6708 else if (move_pattern & MV_ANY_DIRECTION)
6710 MovDir[x][y] = move_pattern;
6711 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6713 else if (move_pattern & MV_WIND_DIRECTION)
6715 MovDir[x][y] = game.wind_direction;
6716 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6718 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6720 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6721 MovDir[x][y] = left_dir;
6722 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6723 MovDir[x][y] = right_dir;
6725 if (MovDir[x][y] != old_move_dir)
6726 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6728 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6730 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6731 MovDir[x][y] = right_dir;
6732 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6733 MovDir[x][y] = left_dir;
6735 if (MovDir[x][y] != old_move_dir)
6736 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6738 else if (move_pattern == MV_TOWARDS_PLAYER ||
6739 move_pattern == MV_AWAY_FROM_PLAYER)
6741 int attr_x = -1, attr_y = -1;
6743 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6754 for (i = 0; i < MAX_PLAYERS; i++)
6756 struct PlayerInfo *player = &stored_player[i];
6757 int jx = player->jx, jy = player->jy;
6759 if (!player->active)
6763 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6771 MovDir[x][y] = MV_NONE;
6773 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6774 else if (attr_x > x)
6775 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6777 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6778 else if (attr_y > y)
6779 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6781 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6783 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6785 boolean first_horiz = RND(2);
6786 int new_move_dir = MovDir[x][y];
6788 if (element_info[element].move_stepsize == 0) /* "not moving" */
6790 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6791 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6797 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6798 Moving2Blocked(x, y, &newx, &newy);
6800 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6804 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6805 Moving2Blocked(x, y, &newx, &newy);
6807 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6810 MovDir[x][y] = old_move_dir;
6813 else if (move_pattern == MV_WHEN_PUSHED ||
6814 move_pattern == MV_WHEN_DROPPED)
6816 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6817 MovDir[x][y] = MV_NONE;
6821 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6823 static int test_xy[7][2] =
6833 static int test_dir[7] =
6843 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6844 int move_preference = -1000000; /* start with very low preference */
6845 int new_move_dir = MV_NONE;
6846 int start_test = RND(4);
6849 for (i = 0; i < NUM_DIRECTIONS; i++)
6851 int move_dir = test_dir[start_test + i];
6852 int move_dir_preference;
6854 xx = x + test_xy[start_test + i][0];
6855 yy = y + test_xy[start_test + i][1];
6857 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6858 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6860 new_move_dir = move_dir;
6865 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6868 move_dir_preference = -1 * RunnerVisit[xx][yy];
6869 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6870 move_dir_preference = PlayerVisit[xx][yy];
6872 if (move_dir_preference > move_preference)
6874 /* prefer field that has not been visited for the longest time */
6875 move_preference = move_dir_preference;
6876 new_move_dir = move_dir;
6878 else if (move_dir_preference == move_preference &&
6879 move_dir == old_move_dir)
6881 /* prefer last direction when all directions are preferred equally */
6882 move_preference = move_dir_preference;
6883 new_move_dir = move_dir;
6887 MovDir[x][y] = new_move_dir;
6888 if (old_move_dir != new_move_dir)
6889 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6893 static void TurnRound(int x, int y)
6895 int direction = MovDir[x][y];
6899 GfxDir[x][y] = MovDir[x][y];
6901 if (direction != MovDir[x][y])
6905 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6907 ResetGfxFrame(x, y, FALSE);
6910 static boolean JustBeingPushed(int x, int y)
6914 for (i = 0; i < MAX_PLAYERS; i++)
6916 struct PlayerInfo *player = &stored_player[i];
6918 if (player->active && player->is_pushing && player->MovPos)
6920 int next_jx = player->jx + (player->jx - player->last_jx);
6921 int next_jy = player->jy + (player->jy - player->last_jy);
6923 if (x == next_jx && y == next_jy)
6931 void StartMoving(int x, int y)
6933 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6934 int element = Feld[x][y];
6939 if (MovDelay[x][y] == 0)
6940 GfxAction[x][y] = ACTION_DEFAULT;
6942 if (CAN_FALL(element) && y < lev_fieldy - 1)
6944 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6945 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6946 if (JustBeingPushed(x, y))
6949 if (element == EL_QUICKSAND_FULL)
6951 if (IS_FREE(x, y + 1))
6953 InitMovingField(x, y, MV_DOWN);
6954 started_moving = TRUE;
6956 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6957 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6958 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6959 Store[x][y] = EL_ROCK;
6961 Store[x][y] = EL_ROCK;
6964 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6966 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6968 if (!MovDelay[x][y])
6969 MovDelay[x][y] = TILEY + 1;
6978 Feld[x][y] = EL_QUICKSAND_EMPTY;
6979 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6980 Store[x][y + 1] = Store[x][y];
6983 PlayLevelSoundAction(x, y, ACTION_FILLING);
6986 else if (element == EL_QUICKSAND_FAST_FULL)
6988 if (IS_FREE(x, y + 1))
6990 InitMovingField(x, y, MV_DOWN);
6991 started_moving = TRUE;
6993 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6994 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6995 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6996 Store[x][y] = EL_ROCK;
6998 Store[x][y] = EL_ROCK;
7001 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7003 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7005 if (!MovDelay[x][y])
7006 MovDelay[x][y] = TILEY + 1;
7015 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7016 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7017 Store[x][y + 1] = Store[x][y];
7020 PlayLevelSoundAction(x, y, ACTION_FILLING);
7023 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7024 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7026 InitMovingField(x, y, MV_DOWN);
7027 started_moving = TRUE;
7029 Feld[x][y] = EL_QUICKSAND_FILLING;
7030 Store[x][y] = element;
7032 PlayLevelSoundAction(x, y, ACTION_FILLING);
7034 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7035 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7037 InitMovingField(x, y, MV_DOWN);
7038 started_moving = TRUE;
7040 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7041 Store[x][y] = element;
7043 PlayLevelSoundAction(x, y, ACTION_FILLING);
7045 else if (element == EL_MAGIC_WALL_FULL)
7047 if (IS_FREE(x, y + 1))
7049 InitMovingField(x, y, MV_DOWN);
7050 started_moving = TRUE;
7052 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7053 Store[x][y] = EL_CHANGED(Store[x][y]);
7055 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7057 if (!MovDelay[x][y])
7058 MovDelay[x][y] = TILEY/4 + 1;
7067 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7068 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7069 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7073 else if (element == EL_BD_MAGIC_WALL_FULL)
7075 if (IS_FREE(x, y + 1))
7077 InitMovingField(x, y, MV_DOWN);
7078 started_moving = TRUE;
7080 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7081 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7083 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7085 if (!MovDelay[x][y])
7086 MovDelay[x][y] = TILEY/4 + 1;
7095 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7096 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7097 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7101 else if (element == EL_DC_MAGIC_WALL_FULL)
7103 if (IS_FREE(x, y + 1))
7105 InitMovingField(x, y, MV_DOWN);
7106 started_moving = TRUE;
7108 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7109 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7111 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7113 if (!MovDelay[x][y])
7114 MovDelay[x][y] = TILEY/4 + 1;
7123 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7124 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7125 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7129 else if ((CAN_PASS_MAGIC_WALL(element) &&
7130 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7131 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7132 (CAN_PASS_DC_MAGIC_WALL(element) &&
7133 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7136 InitMovingField(x, y, MV_DOWN);
7137 started_moving = TRUE;
7140 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7141 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7142 EL_DC_MAGIC_WALL_FILLING);
7143 Store[x][y] = element;
7145 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7147 SplashAcid(x, y + 1);
7149 InitMovingField(x, y, MV_DOWN);
7150 started_moving = TRUE;
7152 Store[x][y] = EL_ACID;
7155 #if USE_FIX_IMPACT_COLLISION
7156 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7157 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7159 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7160 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7162 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7163 CAN_FALL(element) && WasJustFalling[x][y] &&
7164 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7166 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7167 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7168 (Feld[x][y + 1] == EL_BLOCKED)))
7170 /* this is needed for a special case not covered by calling "Impact()"
7171 from "ContinueMoving()": if an element moves to a tile directly below
7172 another element which was just falling on that tile (which was empty
7173 in the previous frame), the falling element above would just stop
7174 instead of smashing the element below (in previous version, the above
7175 element was just checked for "moving" instead of "falling", resulting
7176 in incorrect smashes caused by horizontal movement of the above
7177 element; also, the case of the player being the element to smash was
7178 simply not covered here... :-/ ) */
7180 CheckCollision[x][y] = 0;
7181 CheckImpact[x][y] = 0;
7185 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7187 if (MovDir[x][y] == MV_NONE)
7189 InitMovingField(x, y, MV_DOWN);
7190 started_moving = TRUE;
7193 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7195 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7196 MovDir[x][y] = MV_DOWN;
7198 InitMovingField(x, y, MV_DOWN);
7199 started_moving = TRUE;
7201 else if (element == EL_AMOEBA_DROP)
7203 Feld[x][y] = EL_AMOEBA_GROWING;
7204 Store[x][y] = EL_AMOEBA_WET;
7206 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7207 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7208 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7209 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7211 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7212 (IS_FREE(x - 1, y + 1) ||
7213 Feld[x - 1][y + 1] == EL_ACID));
7214 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7215 (IS_FREE(x + 1, y + 1) ||
7216 Feld[x + 1][y + 1] == EL_ACID));
7217 boolean can_fall_any = (can_fall_left || can_fall_right);
7218 boolean can_fall_both = (can_fall_left && can_fall_right);
7219 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7221 #if USE_NEW_ALL_SLIPPERY
7222 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7224 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7225 can_fall_right = FALSE;
7226 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7227 can_fall_left = FALSE;
7228 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7229 can_fall_right = FALSE;
7230 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7231 can_fall_left = FALSE;
7233 can_fall_any = (can_fall_left || can_fall_right);
7234 can_fall_both = FALSE;
7237 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7239 if (slippery_type == SLIPPERY_ONLY_LEFT)
7240 can_fall_right = FALSE;
7241 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7242 can_fall_left = FALSE;
7243 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7244 can_fall_right = FALSE;
7245 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7246 can_fall_left = FALSE;
7248 can_fall_any = (can_fall_left || can_fall_right);
7249 can_fall_both = (can_fall_left && can_fall_right);
7253 #if USE_NEW_ALL_SLIPPERY
7255 #if USE_NEW_SP_SLIPPERY
7256 /* !!! better use the same properties as for custom elements here !!! */
7257 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7258 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7260 can_fall_right = FALSE; /* slip down on left side */
7261 can_fall_both = FALSE;
7266 #if USE_NEW_ALL_SLIPPERY
7269 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7270 can_fall_right = FALSE; /* slip down on left side */
7272 can_fall_left = !(can_fall_right = RND(2));
7274 can_fall_both = FALSE;
7279 if (game.emulation == EMU_BOULDERDASH ||
7280 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7281 can_fall_right = FALSE; /* slip down on left side */
7283 can_fall_left = !(can_fall_right = RND(2));
7285 can_fall_both = FALSE;
7291 /* if not determined otherwise, prefer left side for slipping down */
7292 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7293 started_moving = TRUE;
7297 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7299 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7302 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7303 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7304 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7305 int belt_dir = game.belt_dir[belt_nr];
7307 if ((belt_dir == MV_LEFT && left_is_free) ||
7308 (belt_dir == MV_RIGHT && right_is_free))
7310 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7312 InitMovingField(x, y, belt_dir);
7313 started_moving = TRUE;
7315 Pushed[x][y] = TRUE;
7316 Pushed[nextx][y] = TRUE;
7318 GfxAction[x][y] = ACTION_DEFAULT;
7322 MovDir[x][y] = 0; /* if element was moving, stop it */
7327 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7329 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7331 if (CAN_MOVE(element) && !started_moving)
7334 int move_pattern = element_info[element].move_pattern;
7339 if (MovDir[x][y] == MV_NONE)
7341 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7342 x, y, element, element_info[element].token_name);
7343 printf("StartMoving(): This should never happen!\n");
7348 Moving2Blocked(x, y, &newx, &newy);
7350 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7353 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7354 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7356 WasJustMoving[x][y] = 0;
7357 CheckCollision[x][y] = 0;
7359 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7361 if (Feld[x][y] != element) /* element has changed */
7365 if (!MovDelay[x][y]) /* start new movement phase */
7367 /* all objects that can change their move direction after each step
7368 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7370 if (element != EL_YAMYAM &&
7371 element != EL_DARK_YAMYAM &&
7372 element != EL_PACMAN &&
7373 !(move_pattern & MV_ANY_DIRECTION) &&
7374 move_pattern != MV_TURNING_LEFT &&
7375 move_pattern != MV_TURNING_RIGHT &&
7376 move_pattern != MV_TURNING_LEFT_RIGHT &&
7377 move_pattern != MV_TURNING_RIGHT_LEFT &&
7378 move_pattern != MV_TURNING_RANDOM)
7382 if (MovDelay[x][y] && (element == EL_BUG ||
7383 element == EL_SPACESHIP ||
7384 element == EL_SP_SNIKSNAK ||
7385 element == EL_SP_ELECTRON ||
7386 element == EL_MOLE))
7387 DrawLevelField(x, y);
7391 if (MovDelay[x][y]) /* wait some time before next movement */
7395 if (element == EL_ROBOT ||
7396 element == EL_YAMYAM ||
7397 element == EL_DARK_YAMYAM)
7399 DrawLevelElementAnimationIfNeeded(x, y, element);
7400 PlayLevelSoundAction(x, y, ACTION_WAITING);
7402 else if (element == EL_SP_ELECTRON)
7403 DrawLevelElementAnimationIfNeeded(x, y, element);
7404 else if (element == EL_DRAGON)
7407 int dir = MovDir[x][y];
7408 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7409 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7410 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7411 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7412 dir == MV_UP ? IMG_FLAMES_1_UP :
7413 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7414 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7416 GfxAction[x][y] = ACTION_ATTACKING;
7418 if (IS_PLAYER(x, y))
7419 DrawPlayerField(x, y);
7421 DrawLevelField(x, y);
7423 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7425 for (i = 1; i <= 3; i++)
7427 int xx = x + i * dx;
7428 int yy = y + i * dy;
7429 int sx = SCREENX(xx);
7430 int sy = SCREENY(yy);
7431 int flame_graphic = graphic + (i - 1);
7433 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7438 int flamed = MovingOrBlocked2Element(xx, yy);
7442 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7444 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7445 RemoveMovingField(xx, yy);
7447 RemoveField(xx, yy);
7449 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7452 RemoveMovingField(xx, yy);
7455 ChangeDelay[xx][yy] = 0;
7457 Feld[xx][yy] = EL_FLAMES;
7459 if (IN_SCR_FIELD(sx, sy))
7461 DrawLevelFieldCrumbledSand(xx, yy);
7462 DrawGraphic(sx, sy, flame_graphic, frame);
7467 if (Feld[xx][yy] == EL_FLAMES)
7468 Feld[xx][yy] = EL_EMPTY;
7469 DrawLevelField(xx, yy);
7474 if (MovDelay[x][y]) /* element still has to wait some time */
7476 PlayLevelSoundAction(x, y, ACTION_WAITING);
7482 /* now make next step */
7484 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7486 if (DONT_COLLIDE_WITH(element) &&
7487 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7488 !PLAYER_ENEMY_PROTECTED(newx, newy))
7490 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7495 else if (CAN_MOVE_INTO_ACID(element) &&
7496 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7497 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7498 (MovDir[x][y] == MV_DOWN ||
7499 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7501 SplashAcid(newx, newy);
7502 Store[x][y] = EL_ACID;
7504 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7506 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7507 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7508 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7509 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7512 DrawLevelField(x, y);
7514 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7515 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7516 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7518 local_player->friends_still_needed--;
7519 if (!local_player->friends_still_needed &&
7520 !local_player->GameOver && AllPlayersGone)
7521 PlayerWins(local_player);
7525 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7527 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7528 DrawLevelField(newx, newy);
7530 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7532 else if (!IS_FREE(newx, newy))
7534 GfxAction[x][y] = ACTION_WAITING;
7536 if (IS_PLAYER(x, y))
7537 DrawPlayerField(x, y);
7539 DrawLevelField(x, y);
7544 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7546 if (IS_FOOD_PIG(Feld[newx][newy]))
7548 if (IS_MOVING(newx, newy))
7549 RemoveMovingField(newx, newy);
7552 Feld[newx][newy] = EL_EMPTY;
7553 DrawLevelField(newx, newy);
7556 PlayLevelSound(x, y, SND_PIG_DIGGING);
7558 else if (!IS_FREE(newx, newy))
7560 if (IS_PLAYER(x, y))
7561 DrawPlayerField(x, y);
7563 DrawLevelField(x, y);
7568 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7570 if (Store[x][y] != EL_EMPTY)
7572 boolean can_clone = FALSE;
7575 /* check if element to clone is still there */
7576 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7578 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7586 /* cannot clone or target field not free anymore -- do not clone */
7587 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7588 Store[x][y] = EL_EMPTY;
7591 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7593 if (IS_MV_DIAGONAL(MovDir[x][y]))
7595 int diagonal_move_dir = MovDir[x][y];
7596 int stored = Store[x][y];
7597 int change_delay = 8;
7600 /* android is moving diagonally */
7602 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7604 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7605 GfxElement[x][y] = EL_EMC_ANDROID;
7606 GfxAction[x][y] = ACTION_SHRINKING;
7607 GfxDir[x][y] = diagonal_move_dir;
7608 ChangeDelay[x][y] = change_delay;
7610 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7613 DrawLevelGraphicAnimation(x, y, graphic);
7614 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7616 if (Feld[newx][newy] == EL_ACID)
7618 SplashAcid(newx, newy);
7623 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7625 Store[newx][newy] = EL_EMC_ANDROID;
7626 GfxElement[newx][newy] = EL_EMC_ANDROID;
7627 GfxAction[newx][newy] = ACTION_GROWING;
7628 GfxDir[newx][newy] = diagonal_move_dir;
7629 ChangeDelay[newx][newy] = change_delay;
7631 graphic = el_act_dir2img(GfxElement[newx][newy],
7632 GfxAction[newx][newy], GfxDir[newx][newy]);
7634 DrawLevelGraphicAnimation(newx, newy, graphic);
7635 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7641 Feld[newx][newy] = EL_EMPTY;
7642 DrawLevelField(newx, newy);
7644 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7647 else if (!IS_FREE(newx, newy))
7650 if (IS_PLAYER(x, y))
7651 DrawPlayerField(x, y);
7653 DrawLevelField(x, y);
7659 else if (IS_CUSTOM_ELEMENT(element) &&
7660 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7662 int new_element = Feld[newx][newy];
7664 if (!IS_FREE(newx, newy))
7666 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7667 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7670 /* no element can dig solid indestructible elements */
7671 if (IS_INDESTRUCTIBLE(new_element) &&
7672 !IS_DIGGABLE(new_element) &&
7673 !IS_COLLECTIBLE(new_element))
7676 if (AmoebaNr[newx][newy] &&
7677 (new_element == EL_AMOEBA_FULL ||
7678 new_element == EL_BD_AMOEBA ||
7679 new_element == EL_AMOEBA_GROWING))
7681 AmoebaCnt[AmoebaNr[newx][newy]]--;
7682 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7685 if (IS_MOVING(newx, newy))
7686 RemoveMovingField(newx, newy);
7689 RemoveField(newx, newy);
7690 DrawLevelField(newx, newy);
7693 /* if digged element was about to explode, prevent the explosion */
7694 ExplodeField[newx][newy] = EX_TYPE_NONE;
7696 PlayLevelSoundAction(x, y, action);
7699 Store[newx][newy] = EL_EMPTY;
7701 /* this makes it possible to leave the removed element again */
7702 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7703 Store[newx][newy] = new_element;
7705 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7707 int move_leave_element = element_info[element].move_leave_element;
7709 /* this makes it possible to leave the removed element again */
7710 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7711 new_element : move_leave_element);
7715 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7717 RunnerVisit[x][y] = FrameCounter;
7718 PlayerVisit[x][y] /= 8; /* expire player visit path */
7721 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7723 if (!IS_FREE(newx, newy))
7725 if (IS_PLAYER(x, y))
7726 DrawPlayerField(x, y);
7728 DrawLevelField(x, y);
7734 boolean wanna_flame = !RND(10);
7735 int dx = newx - x, dy = newy - y;
7736 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7737 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7738 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7739 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7740 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7741 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7744 IS_CLASSIC_ENEMY(element1) ||
7745 IS_CLASSIC_ENEMY(element2)) &&
7746 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7747 element1 != EL_FLAMES && element2 != EL_FLAMES)
7749 ResetGfxAnimation(x, y);
7750 GfxAction[x][y] = ACTION_ATTACKING;
7752 if (IS_PLAYER(x, y))
7753 DrawPlayerField(x, y);
7755 DrawLevelField(x, y);
7757 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7759 MovDelay[x][y] = 50;
7763 RemoveField(newx, newy);
7765 Feld[newx][newy] = EL_FLAMES;
7766 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7769 RemoveField(newx1, newy1);
7771 Feld[newx1][newy1] = EL_FLAMES;
7773 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7776 RemoveField(newx2, newy2);
7778 Feld[newx2][newy2] = EL_FLAMES;
7785 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7786 Feld[newx][newy] == EL_DIAMOND)
7788 if (IS_MOVING(newx, newy))
7789 RemoveMovingField(newx, newy);
7792 Feld[newx][newy] = EL_EMPTY;
7793 DrawLevelField(newx, newy);
7796 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7798 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7799 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7801 if (AmoebaNr[newx][newy])
7803 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7804 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7805 Feld[newx][newy] == EL_BD_AMOEBA)
7806 AmoebaCnt[AmoebaNr[newx][newy]]--;
7811 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7813 RemoveMovingField(newx, newy);
7816 if (IS_MOVING(newx, newy))
7818 RemoveMovingField(newx, newy);
7823 Feld[newx][newy] = EL_EMPTY;
7824 DrawLevelField(newx, newy);
7827 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7829 else if ((element == EL_PACMAN || element == EL_MOLE)
7830 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7832 if (AmoebaNr[newx][newy])
7834 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7835 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7836 Feld[newx][newy] == EL_BD_AMOEBA)
7837 AmoebaCnt[AmoebaNr[newx][newy]]--;
7840 if (element == EL_MOLE)
7842 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7843 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7845 ResetGfxAnimation(x, y);
7846 GfxAction[x][y] = ACTION_DIGGING;
7847 DrawLevelField(x, y);
7849 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7851 return; /* wait for shrinking amoeba */
7853 else /* element == EL_PACMAN */
7855 Feld[newx][newy] = EL_EMPTY;
7856 DrawLevelField(newx, newy);
7857 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7860 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7861 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7862 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7864 /* wait for shrinking amoeba to completely disappear */
7867 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7869 /* object was running against a wall */
7874 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7875 if (move_pattern & MV_ANY_DIRECTION &&
7876 move_pattern == MovDir[x][y])
7878 int blocking_element =
7879 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7881 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7884 element = Feld[x][y]; /* element might have changed */
7888 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7889 DrawLevelElementAnimation(x, y, element);
7891 if (DONT_TOUCH(element))
7892 TestIfBadThingTouchesPlayer(x, y);
7897 InitMovingField(x, y, MovDir[x][y]);
7899 PlayLevelSoundAction(x, y, ACTION_MOVING);
7903 ContinueMoving(x, y);
7906 void ContinueMoving(int x, int y)
7908 int element = Feld[x][y];
7909 struct ElementInfo *ei = &element_info[element];
7910 int direction = MovDir[x][y];
7911 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7912 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7913 int newx = x + dx, newy = y + dy;
7914 int stored = Store[x][y];
7915 int stored_new = Store[newx][newy];
7916 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7917 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7918 boolean last_line = (newy == lev_fieldy - 1);
7920 MovPos[x][y] += getElementMoveStepsize(x, y);
7922 if (pushed_by_player) /* special case: moving object pushed by player */
7923 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7925 if (ABS(MovPos[x][y]) < TILEX)
7928 int ee = Feld[x][y];
7929 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7930 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7932 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7933 x, y, ABS(MovPos[x][y]),
7935 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7938 DrawLevelField(x, y);
7940 return; /* element is still moving */
7943 /* element reached destination field */
7945 Feld[x][y] = EL_EMPTY;
7946 Feld[newx][newy] = element;
7947 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7949 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7951 element = Feld[newx][newy] = EL_ACID;
7953 else if (element == EL_MOLE)
7955 Feld[x][y] = EL_SAND;
7957 DrawLevelFieldCrumbledSandNeighbours(x, y);
7959 else if (element == EL_QUICKSAND_FILLING)
7961 element = Feld[newx][newy] = get_next_element(element);
7962 Store[newx][newy] = Store[x][y];
7964 else if (element == EL_QUICKSAND_EMPTYING)
7966 Feld[x][y] = get_next_element(element);
7967 element = Feld[newx][newy] = Store[x][y];
7969 else if (element == EL_QUICKSAND_FAST_FILLING)
7971 element = Feld[newx][newy] = get_next_element(element);
7972 Store[newx][newy] = Store[x][y];
7974 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7976 Feld[x][y] = get_next_element(element);
7977 element = Feld[newx][newy] = Store[x][y];
7979 else if (element == EL_MAGIC_WALL_FILLING)
7981 element = Feld[newx][newy] = get_next_element(element);
7982 if (!game.magic_wall_active)
7983 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7984 Store[newx][newy] = Store[x][y];
7986 else if (element == EL_MAGIC_WALL_EMPTYING)
7988 Feld[x][y] = get_next_element(element);
7989 if (!game.magic_wall_active)
7990 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7991 element = Feld[newx][newy] = Store[x][y];
7993 #if USE_NEW_CUSTOM_VALUE
7994 InitField(newx, newy, FALSE);
7997 else if (element == EL_BD_MAGIC_WALL_FILLING)
7999 element = Feld[newx][newy] = get_next_element(element);
8000 if (!game.magic_wall_active)
8001 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8002 Store[newx][newy] = Store[x][y];
8004 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8006 Feld[x][y] = get_next_element(element);
8007 if (!game.magic_wall_active)
8008 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8009 element = Feld[newx][newy] = Store[x][y];
8011 #if USE_NEW_CUSTOM_VALUE
8012 InitField(newx, newy, FALSE);
8015 else if (element == EL_DC_MAGIC_WALL_FILLING)
8017 element = Feld[newx][newy] = get_next_element(element);
8018 if (!game.magic_wall_active)
8019 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8020 Store[newx][newy] = Store[x][y];
8022 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8024 Feld[x][y] = get_next_element(element);
8025 if (!game.magic_wall_active)
8026 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8027 element = Feld[newx][newy] = Store[x][y];
8029 #if USE_NEW_CUSTOM_VALUE
8030 InitField(newx, newy, FALSE);
8033 else if (element == EL_AMOEBA_DROPPING)
8035 Feld[x][y] = get_next_element(element);
8036 element = Feld[newx][newy] = Store[x][y];
8038 else if (element == EL_SOKOBAN_OBJECT)
8041 Feld[x][y] = Back[x][y];
8043 if (Back[newx][newy])
8044 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8046 Back[x][y] = Back[newx][newy] = 0;
8049 Store[x][y] = EL_EMPTY;
8054 MovDelay[newx][newy] = 0;
8056 if (CAN_CHANGE_OR_HAS_ACTION(element))
8058 /* copy element change control values to new field */
8059 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8060 ChangePage[newx][newy] = ChangePage[x][y];
8061 ChangeCount[newx][newy] = ChangeCount[x][y];
8062 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8065 #if USE_NEW_CUSTOM_VALUE
8066 CustomValue[newx][newy] = CustomValue[x][y];
8069 ChangeDelay[x][y] = 0;
8070 ChangePage[x][y] = -1;
8071 ChangeCount[x][y] = 0;
8072 ChangeEvent[x][y] = -1;
8074 #if USE_NEW_CUSTOM_VALUE
8075 CustomValue[x][y] = 0;
8078 /* copy animation control values to new field */
8079 GfxFrame[newx][newy] = GfxFrame[x][y];
8080 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8081 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8082 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8084 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8086 /* some elements can leave other elements behind after moving */
8088 if (ei->move_leave_element != EL_EMPTY &&
8089 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8090 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8092 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8093 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8094 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8097 int move_leave_element = ei->move_leave_element;
8101 /* this makes it possible to leave the removed element again */
8102 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8103 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8105 /* this makes it possible to leave the removed element again */
8106 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8107 move_leave_element = stored;
8110 /* this makes it possible to leave the removed element again */
8111 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8112 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8113 move_leave_element = stored;
8116 Feld[x][y] = move_leave_element;
8118 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8119 MovDir[x][y] = direction;
8121 InitField(x, y, FALSE);
8123 if (GFX_CRUMBLED(Feld[x][y]))
8124 DrawLevelFieldCrumbledSandNeighbours(x, y);
8126 if (ELEM_IS_PLAYER(move_leave_element))
8127 RelocatePlayer(x, y, move_leave_element);
8130 /* do this after checking for left-behind element */
8131 ResetGfxAnimation(x, y); /* reset animation values for old field */
8133 if (!CAN_MOVE(element) ||
8134 (CAN_FALL(element) && direction == MV_DOWN &&
8135 (element == EL_SPRING ||
8136 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8137 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8138 GfxDir[x][y] = MovDir[newx][newy] = 0;
8140 DrawLevelField(x, y);
8141 DrawLevelField(newx, newy);
8143 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8145 /* prevent pushed element from moving on in pushed direction */
8146 if (pushed_by_player && CAN_MOVE(element) &&
8147 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8148 !(element_info[element].move_pattern & direction))
8149 TurnRound(newx, newy);
8151 /* prevent elements on conveyor belt from moving on in last direction */
8152 if (pushed_by_conveyor && CAN_FALL(element) &&
8153 direction & MV_HORIZONTAL)
8154 MovDir[newx][newy] = 0;
8156 if (!pushed_by_player)
8158 int nextx = newx + dx, nexty = newy + dy;
8159 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8161 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8163 if (CAN_FALL(element) && direction == MV_DOWN)
8164 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8166 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8167 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8169 #if USE_FIX_IMPACT_COLLISION
8170 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8171 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8175 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8177 TestIfBadThingTouchesPlayer(newx, newy);
8178 TestIfBadThingTouchesFriend(newx, newy);
8180 if (!IS_CUSTOM_ELEMENT(element))
8181 TestIfBadThingTouchesOtherBadThing(newx, newy);
8183 else if (element == EL_PENGUIN)
8184 TestIfFriendTouchesBadThing(newx, newy);
8186 /* give the player one last chance (one more frame) to move away */
8187 if (CAN_FALL(element) && direction == MV_DOWN &&
8188 (last_line || (!IS_FREE(x, newy + 1) &&
8189 (!IS_PLAYER(x, newy + 1) ||
8190 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8193 if (pushed_by_player && !game.use_change_when_pushing_bug)
8195 int push_side = MV_DIR_OPPOSITE(direction);
8196 struct PlayerInfo *player = PLAYERINFO(x, y);
8198 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8199 player->index_bit, push_side);
8200 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8201 player->index_bit, push_side);
8204 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8205 MovDelay[newx][newy] = 1;
8207 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8209 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8212 if (ChangePage[newx][newy] != -1) /* delayed change */
8214 int page = ChangePage[newx][newy];
8215 struct ElementChangeInfo *change = &ei->change_page[page];
8217 ChangePage[newx][newy] = -1;
8219 if (change->can_change)
8221 if (ChangeElement(newx, newy, element, page))
8223 if (change->post_change_function)
8224 change->post_change_function(newx, newy);
8228 if (change->has_action)
8229 ExecuteCustomElementAction(newx, newy, element, page);
8233 TestIfElementHitsCustomElement(newx, newy, direction);
8234 TestIfPlayerTouchesCustomElement(newx, newy);
8235 TestIfElementTouchesCustomElement(newx, newy);
8237 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8238 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8239 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8240 MV_DIR_OPPOSITE(direction));
8243 int AmoebeNachbarNr(int ax, int ay)
8246 int element = Feld[ax][ay];
8248 static int xy[4][2] =
8256 for (i = 0; i < NUM_DIRECTIONS; i++)
8258 int x = ax + xy[i][0];
8259 int y = ay + xy[i][1];
8261 if (!IN_LEV_FIELD(x, y))
8264 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8265 group_nr = AmoebaNr[x][y];
8271 void AmoebenVereinigen(int ax, int ay)
8273 int i, x, y, xx, yy;
8274 int new_group_nr = AmoebaNr[ax][ay];
8275 static int xy[4][2] =
8283 if (new_group_nr == 0)
8286 for (i = 0; i < NUM_DIRECTIONS; i++)
8291 if (!IN_LEV_FIELD(x, y))
8294 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8295 Feld[x][y] == EL_BD_AMOEBA ||
8296 Feld[x][y] == EL_AMOEBA_DEAD) &&
8297 AmoebaNr[x][y] != new_group_nr)
8299 int old_group_nr = AmoebaNr[x][y];
8301 if (old_group_nr == 0)
8304 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8305 AmoebaCnt[old_group_nr] = 0;
8306 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8307 AmoebaCnt2[old_group_nr] = 0;
8309 SCAN_PLAYFIELD(xx, yy)
8311 if (AmoebaNr[xx][yy] == old_group_nr)
8312 AmoebaNr[xx][yy] = new_group_nr;
8318 void AmoebeUmwandeln(int ax, int ay)
8322 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8324 int group_nr = AmoebaNr[ax][ay];
8329 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8330 printf("AmoebeUmwandeln(): This should never happen!\n");
8335 SCAN_PLAYFIELD(x, y)
8337 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8340 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8344 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8345 SND_AMOEBA_TURNING_TO_GEM :
8346 SND_AMOEBA_TURNING_TO_ROCK));
8351 static int xy[4][2] =
8359 for (i = 0; i < NUM_DIRECTIONS; i++)
8364 if (!IN_LEV_FIELD(x, y))
8367 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8369 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8370 SND_AMOEBA_TURNING_TO_GEM :
8371 SND_AMOEBA_TURNING_TO_ROCK));
8378 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8381 int group_nr = AmoebaNr[ax][ay];
8382 boolean done = FALSE;
8387 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8388 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8393 SCAN_PLAYFIELD(x, y)
8395 if (AmoebaNr[x][y] == group_nr &&
8396 (Feld[x][y] == EL_AMOEBA_DEAD ||
8397 Feld[x][y] == EL_BD_AMOEBA ||
8398 Feld[x][y] == EL_AMOEBA_GROWING))
8401 Feld[x][y] = new_element;
8402 InitField(x, y, FALSE);
8403 DrawLevelField(x, y);
8409 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8410 SND_BD_AMOEBA_TURNING_TO_ROCK :
8411 SND_BD_AMOEBA_TURNING_TO_GEM));
8414 void AmoebeWaechst(int x, int y)
8416 static unsigned long sound_delay = 0;
8417 static unsigned long sound_delay_value = 0;
8419 if (!MovDelay[x][y]) /* start new growing cycle */
8423 if (DelayReached(&sound_delay, sound_delay_value))
8425 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8426 sound_delay_value = 30;
8430 if (MovDelay[x][y]) /* wait some time before growing bigger */
8433 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8435 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8436 6 - MovDelay[x][y]);
8438 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8441 if (!MovDelay[x][y])
8443 Feld[x][y] = Store[x][y];
8445 DrawLevelField(x, y);
8450 void AmoebaDisappearing(int x, int y)
8452 static unsigned long sound_delay = 0;
8453 static unsigned long sound_delay_value = 0;
8455 if (!MovDelay[x][y]) /* start new shrinking cycle */
8459 if (DelayReached(&sound_delay, sound_delay_value))
8460 sound_delay_value = 30;
8463 if (MovDelay[x][y]) /* wait some time before shrinking */
8466 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8468 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8469 6 - MovDelay[x][y]);
8471 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8474 if (!MovDelay[x][y])
8476 Feld[x][y] = EL_EMPTY;
8477 DrawLevelField(x, y);
8479 /* don't let mole enter this field in this cycle;
8480 (give priority to objects falling to this field from above) */
8486 void AmoebeAbleger(int ax, int ay)
8489 int element = Feld[ax][ay];
8490 int graphic = el2img(element);
8491 int newax = ax, neway = ay;
8492 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8493 static int xy[4][2] =
8501 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8503 Feld[ax][ay] = EL_AMOEBA_DEAD;
8504 DrawLevelField(ax, ay);
8508 if (IS_ANIMATED(graphic))
8509 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8511 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8512 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8514 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8517 if (MovDelay[ax][ay])
8521 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8524 int x = ax + xy[start][0];
8525 int y = ay + xy[start][1];
8527 if (!IN_LEV_FIELD(x, y))
8530 if (IS_FREE(x, y) ||
8531 CAN_GROW_INTO(Feld[x][y]) ||
8532 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8533 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8539 if (newax == ax && neway == ay)
8542 else /* normal or "filled" (BD style) amoeba */
8545 boolean waiting_for_player = FALSE;
8547 for (i = 0; i < NUM_DIRECTIONS; i++)
8549 int j = (start + i) % 4;
8550 int x = ax + xy[j][0];
8551 int y = ay + xy[j][1];
8553 if (!IN_LEV_FIELD(x, y))
8556 if (IS_FREE(x, y) ||
8557 CAN_GROW_INTO(Feld[x][y]) ||
8558 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8559 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8565 else if (IS_PLAYER(x, y))
8566 waiting_for_player = TRUE;
8569 if (newax == ax && neway == ay) /* amoeba cannot grow */
8571 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8573 Feld[ax][ay] = EL_AMOEBA_DEAD;
8574 DrawLevelField(ax, ay);
8575 AmoebaCnt[AmoebaNr[ax][ay]]--;
8577 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8579 if (element == EL_AMOEBA_FULL)
8580 AmoebeUmwandeln(ax, ay);
8581 else if (element == EL_BD_AMOEBA)
8582 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8587 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8589 /* amoeba gets larger by growing in some direction */
8591 int new_group_nr = AmoebaNr[ax][ay];
8594 if (new_group_nr == 0)
8596 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8597 printf("AmoebeAbleger(): This should never happen!\n");
8602 AmoebaNr[newax][neway] = new_group_nr;
8603 AmoebaCnt[new_group_nr]++;
8604 AmoebaCnt2[new_group_nr]++;
8606 /* if amoeba touches other amoeba(s) after growing, unify them */
8607 AmoebenVereinigen(newax, neway);
8609 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8611 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8617 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8618 (neway == lev_fieldy - 1 && newax != ax))
8620 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8621 Store[newax][neway] = element;
8623 else if (neway == ay || element == EL_EMC_DRIPPER)
8625 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8627 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8631 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8632 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8633 Store[ax][ay] = EL_AMOEBA_DROP;
8634 ContinueMoving(ax, ay);
8638 DrawLevelField(newax, neway);
8641 void Life(int ax, int ay)
8645 int element = Feld[ax][ay];
8646 int graphic = el2img(element);
8647 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8649 boolean changed = FALSE;
8651 if (IS_ANIMATED(graphic))
8652 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8657 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8658 MovDelay[ax][ay] = life_time;
8660 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8663 if (MovDelay[ax][ay])
8667 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8669 int xx = ax+x1, yy = ay+y1;
8672 if (!IN_LEV_FIELD(xx, yy))
8675 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8677 int x = xx+x2, y = yy+y2;
8679 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8682 if (((Feld[x][y] == element ||
8683 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8685 (IS_FREE(x, y) && Stop[x][y]))
8689 if (xx == ax && yy == ay) /* field in the middle */
8691 if (nachbarn < life_parameter[0] ||
8692 nachbarn > life_parameter[1])
8694 Feld[xx][yy] = EL_EMPTY;
8696 DrawLevelField(xx, yy);
8697 Stop[xx][yy] = TRUE;
8701 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8702 { /* free border field */
8703 if (nachbarn >= life_parameter[2] &&
8704 nachbarn <= life_parameter[3])
8706 Feld[xx][yy] = element;
8707 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8709 DrawLevelField(xx, yy);
8710 Stop[xx][yy] = TRUE;
8717 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8718 SND_GAME_OF_LIFE_GROWING);
8721 static void InitRobotWheel(int x, int y)
8723 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8726 static void RunRobotWheel(int x, int y)
8728 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8731 static void StopRobotWheel(int x, int y)
8733 if (ZX == x && ZY == y)
8737 static void InitTimegateWheel(int x, int y)
8739 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8742 static void RunTimegateWheel(int x, int y)
8744 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8747 static void InitMagicBallDelay(int x, int y)
8750 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8752 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8756 static void ActivateMagicBall(int bx, int by)
8760 if (level.ball_random)
8762 int pos_border = RND(8); /* select one of the eight border elements */
8763 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8764 int xx = pos_content % 3;
8765 int yy = pos_content / 3;
8770 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8771 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8775 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8777 int xx = x - bx + 1;
8778 int yy = y - by + 1;
8780 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8781 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8785 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8788 void CheckExit(int x, int y)
8790 if (local_player->gems_still_needed > 0 ||
8791 local_player->sokobanfields_still_needed > 0 ||
8792 local_player->lights_still_needed > 0)
8794 int element = Feld[x][y];
8795 int graphic = el2img(element);
8797 if (IS_ANIMATED(graphic))
8798 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8803 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8806 Feld[x][y] = EL_EXIT_OPENING;
8808 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8811 void CheckExitEM(int x, int y)
8813 if (local_player->gems_still_needed > 0 ||
8814 local_player->sokobanfields_still_needed > 0 ||
8815 local_player->lights_still_needed > 0)
8817 int element = Feld[x][y];
8818 int graphic = el2img(element);
8820 if (IS_ANIMATED(graphic))
8821 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8826 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8829 Feld[x][y] = EL_EM_EXIT_OPENING;
8831 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8834 void CheckExitSteel(int x, int y)
8836 if (local_player->gems_still_needed > 0 ||
8837 local_player->sokobanfields_still_needed > 0 ||
8838 local_player->lights_still_needed > 0)
8840 int element = Feld[x][y];
8841 int graphic = el2img(element);
8843 if (IS_ANIMATED(graphic))
8844 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8849 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8852 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8854 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8857 void CheckExitSteelEM(int x, int y)
8859 if (local_player->gems_still_needed > 0 ||
8860 local_player->sokobanfields_still_needed > 0 ||
8861 local_player->lights_still_needed > 0)
8863 int element = Feld[x][y];
8864 int graphic = el2img(element);
8866 if (IS_ANIMATED(graphic))
8867 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8872 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8875 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8877 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8880 void CheckExitSP(int x, int y)
8882 if (local_player->gems_still_needed > 0)
8884 int element = Feld[x][y];
8885 int graphic = el2img(element);
8887 if (IS_ANIMATED(graphic))
8888 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8893 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8896 Feld[x][y] = EL_SP_EXIT_OPENING;
8898 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8901 static void CloseAllOpenTimegates()
8905 SCAN_PLAYFIELD(x, y)
8907 int element = Feld[x][y];
8909 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8911 Feld[x][y] = EL_TIMEGATE_CLOSING;
8913 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8918 void DrawTwinkleOnField(int x, int y)
8920 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8923 if (Feld[x][y] == EL_BD_DIAMOND)
8926 if (MovDelay[x][y] == 0) /* next animation frame */
8927 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8929 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8933 if (setup.direct_draw && MovDelay[x][y])
8934 SetDrawtoField(DRAW_BUFFERED);
8936 DrawLevelElementAnimation(x, y, Feld[x][y]);
8938 if (MovDelay[x][y] != 0)
8940 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8941 10 - MovDelay[x][y]);
8943 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8945 if (setup.direct_draw)
8949 dest_x = FX + SCREENX(x) * TILEX;
8950 dest_y = FY + SCREENY(y) * TILEY;
8952 BlitBitmap(drawto_field, window,
8953 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8954 SetDrawtoField(DRAW_DIRECT);
8960 void MauerWaechst(int x, int y)
8964 if (!MovDelay[x][y]) /* next animation frame */
8965 MovDelay[x][y] = 3 * delay;
8967 if (MovDelay[x][y]) /* wait some time before next frame */
8971 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8973 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8974 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8976 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8979 if (!MovDelay[x][y])
8981 if (MovDir[x][y] == MV_LEFT)
8983 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8984 DrawLevelField(x - 1, y);
8986 else if (MovDir[x][y] == MV_RIGHT)
8988 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8989 DrawLevelField(x + 1, y);
8991 else if (MovDir[x][y] == MV_UP)
8993 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8994 DrawLevelField(x, y - 1);
8998 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8999 DrawLevelField(x, y + 1);
9002 Feld[x][y] = Store[x][y];
9004 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9005 DrawLevelField(x, y);
9010 void MauerAbleger(int ax, int ay)
9012 int element = Feld[ax][ay];
9013 int graphic = el2img(element);
9014 boolean oben_frei = FALSE, unten_frei = FALSE;
9015 boolean links_frei = FALSE, rechts_frei = FALSE;
9016 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9017 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9018 boolean new_wall = FALSE;
9020 if (IS_ANIMATED(graphic))
9021 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9023 if (!MovDelay[ax][ay]) /* start building new wall */
9024 MovDelay[ax][ay] = 6;
9026 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9029 if (MovDelay[ax][ay])
9033 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9035 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9037 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9039 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9042 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9043 element == EL_EXPANDABLE_WALL_ANY)
9047 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9048 Store[ax][ay-1] = element;
9049 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9050 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9051 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9052 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9057 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9058 Store[ax][ay+1] = element;
9059 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9060 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9061 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9062 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9067 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9068 element == EL_EXPANDABLE_WALL_ANY ||
9069 element == EL_EXPANDABLE_WALL ||
9070 element == EL_BD_EXPANDABLE_WALL)
9074 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9075 Store[ax-1][ay] = element;
9076 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9077 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9078 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9079 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9085 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9086 Store[ax+1][ay] = element;
9087 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9088 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9089 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9090 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9095 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9096 DrawLevelField(ax, ay);
9098 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9100 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9101 unten_massiv = TRUE;
9102 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9103 links_massiv = TRUE;
9104 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9105 rechts_massiv = TRUE;
9107 if (((oben_massiv && unten_massiv) ||
9108 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9109 element == EL_EXPANDABLE_WALL) &&
9110 ((links_massiv && rechts_massiv) ||
9111 element == EL_EXPANDABLE_WALL_VERTICAL))
9112 Feld[ax][ay] = EL_WALL;
9115 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9118 void MauerAblegerStahl(int ax, int ay)
9120 int element = Feld[ax][ay];
9121 int graphic = el2img(element);
9122 boolean oben_frei = FALSE, unten_frei = FALSE;
9123 boolean links_frei = FALSE, rechts_frei = FALSE;
9124 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9125 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9126 boolean new_wall = FALSE;
9128 if (IS_ANIMATED(graphic))
9129 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9131 if (!MovDelay[ax][ay]) /* start building new wall */
9132 MovDelay[ax][ay] = 6;
9134 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9137 if (MovDelay[ax][ay])
9141 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9143 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9145 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9147 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9150 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9151 element == EL_EXPANDABLE_STEELWALL_ANY)
9155 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9156 Store[ax][ay-1] = element;
9157 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9158 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9159 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9160 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9165 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9166 Store[ax][ay+1] = element;
9167 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9168 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9169 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9170 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9175 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9176 element == EL_EXPANDABLE_STEELWALL_ANY)
9180 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9181 Store[ax-1][ay] = element;
9182 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9183 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9184 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9185 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9191 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9192 Store[ax+1][ay] = element;
9193 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9194 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9195 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9196 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9201 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9203 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9204 unten_massiv = TRUE;
9205 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9206 links_massiv = TRUE;
9207 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9208 rechts_massiv = TRUE;
9210 if (((oben_massiv && unten_massiv) ||
9211 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9212 ((links_massiv && rechts_massiv) ||
9213 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9214 Feld[ax][ay] = EL_WALL;
9217 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9220 void CheckForDragon(int x, int y)
9223 boolean dragon_found = FALSE;
9224 static int xy[4][2] =
9232 for (i = 0; i < NUM_DIRECTIONS; i++)
9234 for (j = 0; j < 4; j++)
9236 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9238 if (IN_LEV_FIELD(xx, yy) &&
9239 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9241 if (Feld[xx][yy] == EL_DRAGON)
9242 dragon_found = TRUE;
9251 for (i = 0; i < NUM_DIRECTIONS; i++)
9253 for (j = 0; j < 3; j++)
9255 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9257 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9259 Feld[xx][yy] = EL_EMPTY;
9260 DrawLevelField(xx, yy);
9269 static void InitBuggyBase(int x, int y)
9271 int element = Feld[x][y];
9272 int activating_delay = FRAMES_PER_SECOND / 4;
9275 (element == EL_SP_BUGGY_BASE ?
9276 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9277 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9279 element == EL_SP_BUGGY_BASE_ACTIVE ?
9280 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9283 static void WarnBuggyBase(int x, int y)
9286 static int xy[4][2] =
9294 for (i = 0; i < NUM_DIRECTIONS; i++)
9296 int xx = x + xy[i][0];
9297 int yy = y + xy[i][1];
9299 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9301 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9308 static void InitTrap(int x, int y)
9310 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9313 static void ActivateTrap(int x, int y)
9315 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9318 static void ChangeActiveTrap(int x, int y)
9320 int graphic = IMG_TRAP_ACTIVE;
9322 /* if new animation frame was drawn, correct crumbled sand border */
9323 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9324 DrawLevelFieldCrumbledSand(x, y);
9327 static int getSpecialActionElement(int element, int number, int base_element)
9329 return (element != EL_EMPTY ? element :
9330 number != -1 ? base_element + number - 1 :
9334 static int getModifiedActionNumber(int value_old, int operator, int operand,
9335 int value_min, int value_max)
9337 int value_new = (operator == CA_MODE_SET ? operand :
9338 operator == CA_MODE_ADD ? value_old + operand :
9339 operator == CA_MODE_SUBTRACT ? value_old - operand :
9340 operator == CA_MODE_MULTIPLY ? value_old * operand :
9341 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9342 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9345 return (value_new < value_min ? value_min :
9346 value_new > value_max ? value_max :
9350 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9352 struct ElementInfo *ei = &element_info[element];
9353 struct ElementChangeInfo *change = &ei->change_page[page];
9354 int target_element = change->target_element;
9355 int action_type = change->action_type;
9356 int action_mode = change->action_mode;
9357 int action_arg = change->action_arg;
9360 if (!change->has_action)
9363 /* ---------- determine action paramater values -------------------------- */
9365 int level_time_value =
9366 (level.time > 0 ? TimeLeft :
9369 int action_arg_element =
9370 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9371 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9372 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9375 int action_arg_direction =
9376 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9377 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9378 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9379 change->actual_trigger_side :
9380 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9381 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9384 int action_arg_number_min =
9385 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9388 int action_arg_number_max =
9389 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9390 action_type == CA_SET_LEVEL_GEMS ? 999 :
9391 action_type == CA_SET_LEVEL_TIME ? 9999 :
9392 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9393 action_type == CA_SET_CE_VALUE ? 9999 :
9394 action_type == CA_SET_CE_SCORE ? 9999 :
9397 int action_arg_number_reset =
9398 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9399 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9400 action_type == CA_SET_LEVEL_TIME ? level.time :
9401 action_type == CA_SET_LEVEL_SCORE ? 0 :
9402 #if USE_NEW_CUSTOM_VALUE
9403 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9405 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9407 action_type == CA_SET_CE_SCORE ? 0 :
9410 int action_arg_number =
9411 (action_arg <= CA_ARG_MAX ? action_arg :
9412 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9413 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9414 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9415 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9416 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9417 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9418 #if USE_NEW_CUSTOM_VALUE
9419 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9421 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9423 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9424 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9425 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9426 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9427 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9428 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9429 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9430 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9431 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9432 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9433 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9436 int action_arg_number_old =
9437 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9438 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9439 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9440 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9441 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9444 int action_arg_number_new =
9445 getModifiedActionNumber(action_arg_number_old,
9446 action_mode, action_arg_number,
9447 action_arg_number_min, action_arg_number_max);
9449 int trigger_player_bits =
9450 (change->actual_trigger_player >= EL_PLAYER_1 &&
9451 change->actual_trigger_player <= EL_PLAYER_4 ?
9452 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9455 int action_arg_player_bits =
9456 (action_arg >= CA_ARG_PLAYER_1 &&
9457 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9458 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9461 /* ---------- execute action -------------------------------------------- */
9463 switch (action_type)
9470 /* ---------- level actions ------------------------------------------- */
9472 case CA_RESTART_LEVEL:
9474 game.restart_level = TRUE;
9479 case CA_SHOW_ENVELOPE:
9481 int element = getSpecialActionElement(action_arg_element,
9482 action_arg_number, EL_ENVELOPE_1);
9484 if (IS_ENVELOPE(element))
9485 local_player->show_envelope = element;
9490 case CA_SET_LEVEL_TIME:
9492 if (level.time > 0) /* only modify limited time value */
9494 TimeLeft = action_arg_number_new;
9497 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9499 DisplayGameControlValues();
9501 DrawGameValue_Time(TimeLeft);
9504 if (!TimeLeft && setup.time_limit)
9505 for (i = 0; i < MAX_PLAYERS; i++)
9506 KillPlayer(&stored_player[i]);
9512 case CA_SET_LEVEL_SCORE:
9514 local_player->score = action_arg_number_new;
9517 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9519 DisplayGameControlValues();
9521 DrawGameValue_Score(local_player->score);
9527 case CA_SET_LEVEL_GEMS:
9529 local_player->gems_still_needed = action_arg_number_new;
9532 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9534 DisplayGameControlValues();
9536 DrawGameValue_Emeralds(local_player->gems_still_needed);
9542 #if !USE_PLAYER_GRAVITY
9543 case CA_SET_LEVEL_GRAVITY:
9545 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9546 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9547 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9553 case CA_SET_LEVEL_WIND:
9555 game.wind_direction = action_arg_direction;
9560 /* ---------- player actions ------------------------------------------ */
9562 case CA_MOVE_PLAYER:
9564 /* automatically move to the next field in specified direction */
9565 for (i = 0; i < MAX_PLAYERS; i++)
9566 if (trigger_player_bits & (1 << i))
9567 stored_player[i].programmed_action = action_arg_direction;
9572 case CA_EXIT_PLAYER:
9574 for (i = 0; i < MAX_PLAYERS; i++)
9575 if (action_arg_player_bits & (1 << i))
9576 PlayerWins(&stored_player[i]);
9581 case CA_KILL_PLAYER:
9583 for (i = 0; i < MAX_PLAYERS; i++)
9584 if (action_arg_player_bits & (1 << i))
9585 KillPlayer(&stored_player[i]);
9590 case CA_SET_PLAYER_KEYS:
9592 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9593 int element = getSpecialActionElement(action_arg_element,
9594 action_arg_number, EL_KEY_1);
9596 if (IS_KEY(element))
9598 for (i = 0; i < MAX_PLAYERS; i++)
9600 if (trigger_player_bits & (1 << i))
9602 stored_player[i].key[KEY_NR(element)] = key_state;
9604 DrawGameDoorValues();
9612 case CA_SET_PLAYER_SPEED:
9614 for (i = 0; i < MAX_PLAYERS; i++)
9616 if (trigger_player_bits & (1 << i))
9618 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9620 if (action_arg == CA_ARG_SPEED_FASTER &&
9621 stored_player[i].cannot_move)
9623 action_arg_number = STEPSIZE_VERY_SLOW;
9625 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9626 action_arg == CA_ARG_SPEED_FASTER)
9628 action_arg_number = 2;
9629 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9632 else if (action_arg == CA_ARG_NUMBER_RESET)
9634 action_arg_number = level.initial_player_stepsize[i];
9638 getModifiedActionNumber(move_stepsize,
9641 action_arg_number_min,
9642 action_arg_number_max);
9644 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9651 case CA_SET_PLAYER_SHIELD:
9653 for (i = 0; i < MAX_PLAYERS; i++)
9655 if (trigger_player_bits & (1 << i))
9657 if (action_arg == CA_ARG_SHIELD_OFF)
9659 stored_player[i].shield_normal_time_left = 0;
9660 stored_player[i].shield_deadly_time_left = 0;
9662 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9664 stored_player[i].shield_normal_time_left = 999999;
9666 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9668 stored_player[i].shield_normal_time_left = 999999;
9669 stored_player[i].shield_deadly_time_left = 999999;
9677 #if USE_PLAYER_GRAVITY
9678 case CA_SET_PLAYER_GRAVITY:
9680 for (i = 0; i < MAX_PLAYERS; i++)
9682 if (trigger_player_bits & (1 << i))
9684 stored_player[i].gravity =
9685 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9686 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9687 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9688 stored_player[i].gravity);
9696 case CA_SET_PLAYER_ARTWORK:
9698 for (i = 0; i < MAX_PLAYERS; i++)
9700 if (trigger_player_bits & (1 << i))
9702 int artwork_element = action_arg_element;
9704 if (action_arg == CA_ARG_ELEMENT_RESET)
9706 (level.use_artwork_element[i] ? level.artwork_element[i] :
9707 stored_player[i].element_nr);
9709 #if USE_GFX_RESET_PLAYER_ARTWORK
9710 if (stored_player[i].artwork_element != artwork_element)
9711 stored_player[i].Frame = 0;
9714 stored_player[i].artwork_element = artwork_element;
9716 SetPlayerWaiting(&stored_player[i], FALSE);
9718 /* set number of special actions for bored and sleeping animation */
9719 stored_player[i].num_special_action_bored =
9720 get_num_special_action(artwork_element,
9721 ACTION_BORING_1, ACTION_BORING_LAST);
9722 stored_player[i].num_special_action_sleeping =
9723 get_num_special_action(artwork_element,
9724 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9731 /* ---------- CE actions ---------------------------------------------- */
9733 case CA_SET_CE_VALUE:
9735 #if USE_NEW_CUSTOM_VALUE
9736 int last_ce_value = CustomValue[x][y];
9738 CustomValue[x][y] = action_arg_number_new;
9740 if (CustomValue[x][y] != last_ce_value)
9742 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9743 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9745 if (CustomValue[x][y] == 0)
9747 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9748 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9756 case CA_SET_CE_SCORE:
9758 #if USE_NEW_CUSTOM_VALUE
9759 int last_ce_score = ei->collect_score;
9761 ei->collect_score = action_arg_number_new;
9763 if (ei->collect_score != last_ce_score)
9765 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9766 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9768 if (ei->collect_score == 0)
9772 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9773 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9776 This is a very special case that seems to be a mixture between
9777 CheckElementChange() and CheckTriggeredElementChange(): while
9778 the first one only affects single elements that are triggered
9779 directly, the second one affects multiple elements in the playfield
9780 that are triggered indirectly by another element. This is a third
9781 case: Changing the CE score always affects multiple identical CEs,
9782 so every affected CE must be checked, not only the single CE for
9783 which the CE score was changed in the first place (as every instance
9784 of that CE shares the same CE score, and therefore also can change)!
9786 SCAN_PLAYFIELD(xx, yy)
9788 if (Feld[xx][yy] == element)
9789 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9790 CE_SCORE_GETS_ZERO);
9799 /* ---------- engine actions ------------------------------------------ */
9801 case CA_SET_ENGINE_SCAN_MODE:
9803 InitPlayfieldScanMode(action_arg);
9813 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9815 int old_element = Feld[x][y];
9816 int new_element = GetElementFromGroupElement(element);
9817 int previous_move_direction = MovDir[x][y];
9818 #if USE_NEW_CUSTOM_VALUE
9819 int last_ce_value = CustomValue[x][y];
9821 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9822 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9823 boolean add_player_onto_element = (new_element_is_player &&
9824 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9825 /* this breaks SnakeBite when a snake is
9826 halfway through a door that closes */
9827 /* NOW FIXED AT LEVEL INIT IN files.c */
9828 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9830 IS_WALKABLE(old_element));
9833 /* check if element under the player changes from accessible to unaccessible
9834 (needed for special case of dropping element which then changes) */
9835 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9836 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9844 if (!add_player_onto_element)
9846 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9847 RemoveMovingField(x, y);
9851 Feld[x][y] = new_element;
9853 #if !USE_GFX_RESET_GFX_ANIMATION
9854 ResetGfxAnimation(x, y);
9855 ResetRandomAnimationValue(x, y);
9858 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9859 MovDir[x][y] = previous_move_direction;
9861 #if USE_NEW_CUSTOM_VALUE
9862 if (element_info[new_element].use_last_ce_value)
9863 CustomValue[x][y] = last_ce_value;
9866 InitField_WithBug1(x, y, FALSE);
9868 new_element = Feld[x][y]; /* element may have changed */
9870 #if USE_GFX_RESET_GFX_ANIMATION
9871 ResetGfxAnimation(x, y);
9872 ResetRandomAnimationValue(x, y);
9875 DrawLevelField(x, y);
9877 if (GFX_CRUMBLED(new_element))
9878 DrawLevelFieldCrumbledSandNeighbours(x, y);
9882 /* check if element under the player changes from accessible to unaccessible
9883 (needed for special case of dropping element which then changes) */
9884 /* (must be checked after creating new element for walkable group elements) */
9885 #if USE_FIX_KILLED_BY_NON_WALKABLE
9886 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9887 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9894 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9895 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9904 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9905 if (new_element_is_player)
9906 RelocatePlayer(x, y, new_element);
9909 ChangeCount[x][y]++; /* count number of changes in the same frame */
9911 TestIfBadThingTouchesPlayer(x, y);
9912 TestIfPlayerTouchesCustomElement(x, y);
9913 TestIfElementTouchesCustomElement(x, y);
9916 static void CreateField(int x, int y, int element)
9918 CreateFieldExt(x, y, element, FALSE);
9921 static void CreateElementFromChange(int x, int y, int element)
9923 element = GET_VALID_RUNTIME_ELEMENT(element);
9925 #if USE_STOP_CHANGED_ELEMENTS
9926 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9928 int old_element = Feld[x][y];
9930 /* prevent changed element from moving in same engine frame
9931 unless both old and new element can either fall or move */
9932 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9933 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9938 CreateFieldExt(x, y, element, TRUE);
9941 static boolean ChangeElement(int x, int y, int element, int page)
9943 struct ElementInfo *ei = &element_info[element];
9944 struct ElementChangeInfo *change = &ei->change_page[page];
9945 int ce_value = CustomValue[x][y];
9946 int ce_score = ei->collect_score;
9948 int old_element = Feld[x][y];
9950 /* always use default change event to prevent running into a loop */
9951 if (ChangeEvent[x][y] == -1)
9952 ChangeEvent[x][y] = CE_DELAY;
9954 if (ChangeEvent[x][y] == CE_DELAY)
9956 /* reset actual trigger element, trigger player and action element */
9957 change->actual_trigger_element = EL_EMPTY;
9958 change->actual_trigger_player = EL_PLAYER_1;
9959 change->actual_trigger_side = CH_SIDE_NONE;
9960 change->actual_trigger_ce_value = 0;
9961 change->actual_trigger_ce_score = 0;
9964 /* do not change elements more than a specified maximum number of changes */
9965 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9968 ChangeCount[x][y]++; /* count number of changes in the same frame */
9970 if (change->explode)
9977 if (change->use_target_content)
9979 boolean complete_replace = TRUE;
9980 boolean can_replace[3][3];
9983 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9986 boolean is_walkable;
9987 boolean is_diggable;
9988 boolean is_collectible;
9989 boolean is_removable;
9990 boolean is_destructible;
9991 int ex = x + xx - 1;
9992 int ey = y + yy - 1;
9993 int content_element = change->target_content.e[xx][yy];
9996 can_replace[xx][yy] = TRUE;
9998 if (ex == x && ey == y) /* do not check changing element itself */
10001 if (content_element == EL_EMPTY_SPACE)
10003 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10008 if (!IN_LEV_FIELD(ex, ey))
10010 can_replace[xx][yy] = FALSE;
10011 complete_replace = FALSE;
10018 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10019 e = MovingOrBlocked2Element(ex, ey);
10021 is_empty = (IS_FREE(ex, ey) ||
10022 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10024 is_walkable = (is_empty || IS_WALKABLE(e));
10025 is_diggable = (is_empty || IS_DIGGABLE(e));
10026 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10027 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10028 is_removable = (is_diggable || is_collectible);
10030 can_replace[xx][yy] =
10031 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10032 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10033 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10034 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10035 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10036 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10037 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10039 if (!can_replace[xx][yy])
10040 complete_replace = FALSE;
10043 if (!change->only_if_complete || complete_replace)
10045 boolean something_has_changed = FALSE;
10047 if (change->only_if_complete && change->use_random_replace &&
10048 RND(100) < change->random_percentage)
10051 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10053 int ex = x + xx - 1;
10054 int ey = y + yy - 1;
10055 int content_element;
10057 if (can_replace[xx][yy] && (!change->use_random_replace ||
10058 RND(100) < change->random_percentage))
10060 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10061 RemoveMovingField(ex, ey);
10063 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10065 content_element = change->target_content.e[xx][yy];
10066 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10067 ce_value, ce_score);
10069 CreateElementFromChange(ex, ey, target_element);
10071 something_has_changed = TRUE;
10073 /* for symmetry reasons, freeze newly created border elements */
10074 if (ex != x || ey != y)
10075 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10079 if (something_has_changed)
10081 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10082 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10088 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10089 ce_value, ce_score);
10091 if (element == EL_DIAGONAL_GROWING ||
10092 element == EL_DIAGONAL_SHRINKING)
10094 target_element = Store[x][y];
10096 Store[x][y] = EL_EMPTY;
10099 CreateElementFromChange(x, y, target_element);
10101 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10102 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10105 /* this uses direct change before indirect change */
10106 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10111 #if USE_NEW_DELAYED_ACTION
10113 static void HandleElementChange(int x, int y, int page)
10115 int element = MovingOrBlocked2Element(x, y);
10116 struct ElementInfo *ei = &element_info[element];
10117 struct ElementChangeInfo *change = &ei->change_page[page];
10120 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10121 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10124 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10125 x, y, element, element_info[element].token_name);
10126 printf("HandleElementChange(): This should never happen!\n");
10131 /* this can happen with classic bombs on walkable, changing elements */
10132 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10135 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10136 ChangeDelay[x][y] = 0;
10142 if (ChangeDelay[x][y] == 0) /* initialize element change */
10144 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10146 if (change->can_change)
10149 /* !!! not clear why graphic animation should be reset at all here !!! */
10150 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10151 #if USE_GFX_RESET_WHEN_NOT_MOVING
10152 /* when a custom element is about to change (for example by change delay),
10153 do not reset graphic animation when the custom element is moving */
10154 if (!IS_MOVING(x, y))
10157 ResetGfxAnimation(x, y);
10158 ResetRandomAnimationValue(x, y);
10162 if (change->pre_change_function)
10163 change->pre_change_function(x, y);
10167 ChangeDelay[x][y]--;
10169 if (ChangeDelay[x][y] != 0) /* continue element change */
10171 if (change->can_change)
10173 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10175 if (IS_ANIMATED(graphic))
10176 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10178 if (change->change_function)
10179 change->change_function(x, y);
10182 else /* finish element change */
10184 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10186 page = ChangePage[x][y];
10187 ChangePage[x][y] = -1;
10189 change = &ei->change_page[page];
10192 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10194 ChangeDelay[x][y] = 1; /* try change after next move step */
10195 ChangePage[x][y] = page; /* remember page to use for change */
10200 if (change->can_change)
10202 if (ChangeElement(x, y, element, page))
10204 if (change->post_change_function)
10205 change->post_change_function(x, y);
10209 if (change->has_action)
10210 ExecuteCustomElementAction(x, y, element, page);
10216 static void HandleElementChange(int x, int y, int page)
10218 int element = MovingOrBlocked2Element(x, y);
10219 struct ElementInfo *ei = &element_info[element];
10220 struct ElementChangeInfo *change = &ei->change_page[page];
10223 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10226 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10227 x, y, element, element_info[element].token_name);
10228 printf("HandleElementChange(): This should never happen!\n");
10233 /* this can happen with classic bombs on walkable, changing elements */
10234 if (!CAN_CHANGE(element))
10237 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10238 ChangeDelay[x][y] = 0;
10244 if (ChangeDelay[x][y] == 0) /* initialize element change */
10246 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10248 ResetGfxAnimation(x, y);
10249 ResetRandomAnimationValue(x, y);
10251 if (change->pre_change_function)
10252 change->pre_change_function(x, y);
10255 ChangeDelay[x][y]--;
10257 if (ChangeDelay[x][y] != 0) /* continue element change */
10259 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10261 if (IS_ANIMATED(graphic))
10262 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10264 if (change->change_function)
10265 change->change_function(x, y);
10267 else /* finish element change */
10269 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10271 page = ChangePage[x][y];
10272 ChangePage[x][y] = -1;
10274 change = &ei->change_page[page];
10277 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10279 ChangeDelay[x][y] = 1; /* try change after next move step */
10280 ChangePage[x][y] = page; /* remember page to use for change */
10285 if (ChangeElement(x, y, element, page))
10287 if (change->post_change_function)
10288 change->post_change_function(x, y);
10295 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10296 int trigger_element,
10298 int trigger_player,
10302 boolean change_done_any = FALSE;
10303 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10306 if (!(trigger_events[trigger_element][trigger_event]))
10310 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10311 trigger_event, recursion_loop_depth, recursion_loop_detected,
10312 recursion_loop_element, EL_NAME(recursion_loop_element));
10315 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10317 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10319 int element = EL_CUSTOM_START + i;
10320 boolean change_done = FALSE;
10323 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10324 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10327 for (p = 0; p < element_info[element].num_change_pages; p++)
10329 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10331 if (change->can_change_or_has_action &&
10332 change->has_event[trigger_event] &&
10333 change->trigger_side & trigger_side &&
10334 change->trigger_player & trigger_player &&
10335 change->trigger_page & trigger_page_bits &&
10336 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10338 change->actual_trigger_element = trigger_element;
10339 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10340 change->actual_trigger_side = trigger_side;
10341 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10342 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10344 if ((change->can_change && !change_done) || change->has_action)
10348 SCAN_PLAYFIELD(x, y)
10350 if (Feld[x][y] == element)
10352 if (change->can_change && !change_done)
10354 ChangeDelay[x][y] = 1;
10355 ChangeEvent[x][y] = trigger_event;
10357 HandleElementChange(x, y, p);
10359 #if USE_NEW_DELAYED_ACTION
10360 else if (change->has_action)
10362 ExecuteCustomElementAction(x, y, element, p);
10363 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10366 if (change->has_action)
10368 ExecuteCustomElementAction(x, y, element, p);
10369 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10375 if (change->can_change)
10377 change_done = TRUE;
10378 change_done_any = TRUE;
10385 RECURSION_LOOP_DETECTION_END();
10387 return change_done_any;
10390 static boolean CheckElementChangeExt(int x, int y,
10392 int trigger_element,
10394 int trigger_player,
10397 boolean change_done = FALSE;
10400 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10401 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10404 if (Feld[x][y] == EL_BLOCKED)
10406 Blocked2Moving(x, y, &x, &y);
10407 element = Feld[x][y];
10411 /* check if element has already changed */
10412 if (Feld[x][y] != element)
10415 /* check if element has already changed or is about to change after moving */
10416 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10417 Feld[x][y] != element) ||
10419 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10420 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10421 ChangePage[x][y] != -1)))
10426 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10427 trigger_event, recursion_loop_depth, recursion_loop_detected,
10428 recursion_loop_element, EL_NAME(recursion_loop_element));
10431 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10433 for (p = 0; p < element_info[element].num_change_pages; p++)
10435 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10437 /* check trigger element for all events where the element that is checked
10438 for changing interacts with a directly adjacent element -- this is
10439 different to element changes that affect other elements to change on the
10440 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10441 boolean check_trigger_element =
10442 (trigger_event == CE_TOUCHING_X ||
10443 trigger_event == CE_HITTING_X ||
10444 trigger_event == CE_HIT_BY_X ||
10446 /* this one was forgotten until 3.2.3 */
10447 trigger_event == CE_DIGGING_X);
10450 if (change->can_change_or_has_action &&
10451 change->has_event[trigger_event] &&
10452 change->trigger_side & trigger_side &&
10453 change->trigger_player & trigger_player &&
10454 (!check_trigger_element ||
10455 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10457 change->actual_trigger_element = trigger_element;
10458 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10459 change->actual_trigger_side = trigger_side;
10460 change->actual_trigger_ce_value = CustomValue[x][y];
10461 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10463 /* special case: trigger element not at (x,y) position for some events */
10464 if (check_trigger_element)
10476 { 0, 0 }, { 0, 0 }, { 0, 0 },
10480 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10481 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10483 change->actual_trigger_ce_value = CustomValue[xx][yy];
10484 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10487 if (change->can_change && !change_done)
10489 ChangeDelay[x][y] = 1;
10490 ChangeEvent[x][y] = trigger_event;
10492 HandleElementChange(x, y, p);
10494 change_done = TRUE;
10496 #if USE_NEW_DELAYED_ACTION
10497 else if (change->has_action)
10499 ExecuteCustomElementAction(x, y, element, p);
10500 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10503 if (change->has_action)
10505 ExecuteCustomElementAction(x, y, element, p);
10506 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10512 RECURSION_LOOP_DETECTION_END();
10514 return change_done;
10517 static void PlayPlayerSound(struct PlayerInfo *player)
10519 int jx = player->jx, jy = player->jy;
10520 int sound_element = player->artwork_element;
10521 int last_action = player->last_action_waiting;
10522 int action = player->action_waiting;
10524 if (player->is_waiting)
10526 if (action != last_action)
10527 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10529 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10533 if (action != last_action)
10534 StopSound(element_info[sound_element].sound[last_action]);
10536 if (last_action == ACTION_SLEEPING)
10537 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10541 static void PlayAllPlayersSound()
10545 for (i = 0; i < MAX_PLAYERS; i++)
10546 if (stored_player[i].active)
10547 PlayPlayerSound(&stored_player[i]);
10550 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10552 boolean last_waiting = player->is_waiting;
10553 int move_dir = player->MovDir;
10555 player->dir_waiting = move_dir;
10556 player->last_action_waiting = player->action_waiting;
10560 if (!last_waiting) /* not waiting -> waiting */
10562 player->is_waiting = TRUE;
10564 player->frame_counter_bored =
10566 game.player_boring_delay_fixed +
10567 GetSimpleRandom(game.player_boring_delay_random);
10568 player->frame_counter_sleeping =
10570 game.player_sleeping_delay_fixed +
10571 GetSimpleRandom(game.player_sleeping_delay_random);
10573 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10576 if (game.player_sleeping_delay_fixed +
10577 game.player_sleeping_delay_random > 0 &&
10578 player->anim_delay_counter == 0 &&
10579 player->post_delay_counter == 0 &&
10580 FrameCounter >= player->frame_counter_sleeping)
10581 player->is_sleeping = TRUE;
10582 else if (game.player_boring_delay_fixed +
10583 game.player_boring_delay_random > 0 &&
10584 FrameCounter >= player->frame_counter_bored)
10585 player->is_bored = TRUE;
10587 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10588 player->is_bored ? ACTION_BORING :
10591 if (player->is_sleeping && player->use_murphy)
10593 /* special case for sleeping Murphy when leaning against non-free tile */
10595 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10596 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10597 !IS_MOVING(player->jx - 1, player->jy)))
10598 move_dir = MV_LEFT;
10599 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10600 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10601 !IS_MOVING(player->jx + 1, player->jy)))
10602 move_dir = MV_RIGHT;
10604 player->is_sleeping = FALSE;
10606 player->dir_waiting = move_dir;
10609 if (player->is_sleeping)
10611 if (player->num_special_action_sleeping > 0)
10613 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10615 int last_special_action = player->special_action_sleeping;
10616 int num_special_action = player->num_special_action_sleeping;
10617 int special_action =
10618 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10619 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10620 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10621 last_special_action + 1 : ACTION_SLEEPING);
10622 int special_graphic =
10623 el_act_dir2img(player->artwork_element, special_action, move_dir);
10625 player->anim_delay_counter =
10626 graphic_info[special_graphic].anim_delay_fixed +
10627 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10628 player->post_delay_counter =
10629 graphic_info[special_graphic].post_delay_fixed +
10630 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10632 player->special_action_sleeping = special_action;
10635 if (player->anim_delay_counter > 0)
10637 player->action_waiting = player->special_action_sleeping;
10638 player->anim_delay_counter--;
10640 else if (player->post_delay_counter > 0)
10642 player->post_delay_counter--;
10646 else if (player->is_bored)
10648 if (player->num_special_action_bored > 0)
10650 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10652 int special_action =
10653 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10654 int special_graphic =
10655 el_act_dir2img(player->artwork_element, special_action, move_dir);
10657 player->anim_delay_counter =
10658 graphic_info[special_graphic].anim_delay_fixed +
10659 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10660 player->post_delay_counter =
10661 graphic_info[special_graphic].post_delay_fixed +
10662 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10664 player->special_action_bored = special_action;
10667 if (player->anim_delay_counter > 0)
10669 player->action_waiting = player->special_action_bored;
10670 player->anim_delay_counter--;
10672 else if (player->post_delay_counter > 0)
10674 player->post_delay_counter--;
10679 else if (last_waiting) /* waiting -> not waiting */
10681 player->is_waiting = FALSE;
10682 player->is_bored = FALSE;
10683 player->is_sleeping = FALSE;
10685 player->frame_counter_bored = -1;
10686 player->frame_counter_sleeping = -1;
10688 player->anim_delay_counter = 0;
10689 player->post_delay_counter = 0;
10691 player->dir_waiting = player->MovDir;
10692 player->action_waiting = ACTION_DEFAULT;
10694 player->special_action_bored = ACTION_DEFAULT;
10695 player->special_action_sleeping = ACTION_DEFAULT;
10699 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10701 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10702 int left = player_action & JOY_LEFT;
10703 int right = player_action & JOY_RIGHT;
10704 int up = player_action & JOY_UP;
10705 int down = player_action & JOY_DOWN;
10706 int button1 = player_action & JOY_BUTTON_1;
10707 int button2 = player_action & JOY_BUTTON_2;
10708 int dx = (left ? -1 : right ? 1 : 0);
10709 int dy = (up ? -1 : down ? 1 : 0);
10711 if (!player->active || tape.pausing)
10717 snapped = SnapField(player, dx, dy);
10721 dropped = DropElement(player);
10723 moved = MovePlayer(player, dx, dy);
10726 if (tape.single_step && tape.recording && !tape.pausing)
10728 if (button1 || (dropped && !moved))
10730 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10731 SnapField(player, 0, 0); /* stop snapping */
10735 SetPlayerWaiting(player, FALSE);
10737 return player_action;
10741 /* no actions for this player (no input at player's configured device) */
10743 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10744 SnapField(player, 0, 0);
10745 CheckGravityMovementWhenNotMoving(player);
10747 if (player->MovPos == 0)
10748 SetPlayerWaiting(player, TRUE);
10750 if (player->MovPos == 0) /* needed for tape.playing */
10751 player->is_moving = FALSE;
10753 player->is_dropping = FALSE;
10754 player->is_dropping_pressed = FALSE;
10755 player->drop_pressed_delay = 0;
10761 static void CheckLevelTime()
10765 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10767 if (level.native_em_level->lev->home == 0) /* all players at home */
10769 PlayerWins(local_player);
10771 AllPlayersGone = TRUE;
10773 level.native_em_level->lev->home = -1;
10776 if (level.native_em_level->ply[0]->alive == 0 &&
10777 level.native_em_level->ply[1]->alive == 0 &&
10778 level.native_em_level->ply[2]->alive == 0 &&
10779 level.native_em_level->ply[3]->alive == 0) /* all dead */
10780 AllPlayersGone = TRUE;
10783 if (TimeFrames >= FRAMES_PER_SECOND)
10788 for (i = 0; i < MAX_PLAYERS; i++)
10790 struct PlayerInfo *player = &stored_player[i];
10792 if (SHIELD_ON(player))
10794 player->shield_normal_time_left--;
10796 if (player->shield_deadly_time_left > 0)
10797 player->shield_deadly_time_left--;
10801 if (!local_player->LevelSolved && !level.use_step_counter)
10809 if (TimeLeft <= 10 && setup.time_limit)
10810 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10813 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10815 DisplayGameControlValues();
10817 DrawGameValue_Time(TimeLeft);
10820 if (!TimeLeft && setup.time_limit)
10822 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10823 level.native_em_level->lev->killed_out_of_time = TRUE;
10825 for (i = 0; i < MAX_PLAYERS; i++)
10826 KillPlayer(&stored_player[i]);
10830 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10832 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10834 DisplayGameControlValues();
10837 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10838 DrawGameValue_Time(TimePlayed);
10841 level.native_em_level->lev->time =
10842 (level.time == 0 ? TimePlayed : TimeLeft);
10845 if (tape.recording || tape.playing)
10846 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10849 DrawGameDoorValues();
10852 void AdvanceFrameAndPlayerCounters(int player_nr)
10856 /* advance frame counters (global frame counter and time frame counter) */
10860 /* advance player counters (counters for move delay, move animation etc.) */
10861 for (i = 0; i < MAX_PLAYERS; i++)
10863 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10864 int move_delay_value = stored_player[i].move_delay_value;
10865 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10867 if (!advance_player_counters) /* not all players may be affected */
10870 #if USE_NEW_PLAYER_ANIM
10871 if (move_frames == 0) /* less than one move per game frame */
10873 int stepsize = TILEX / move_delay_value;
10874 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10875 int count = (stored_player[i].is_moving ?
10876 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10878 if (count % delay == 0)
10883 stored_player[i].Frame += move_frames;
10885 if (stored_player[i].MovPos != 0)
10886 stored_player[i].StepFrame += move_frames;
10888 if (stored_player[i].move_delay > 0)
10889 stored_player[i].move_delay--;
10891 /* due to bugs in previous versions, counter must count up, not down */
10892 if (stored_player[i].push_delay != -1)
10893 stored_player[i].push_delay++;
10895 if (stored_player[i].drop_delay > 0)
10896 stored_player[i].drop_delay--;
10898 if (stored_player[i].is_dropping_pressed)
10899 stored_player[i].drop_pressed_delay++;
10903 void StartGameActions(boolean init_network_game, boolean record_tape,
10906 unsigned long new_random_seed = InitRND(random_seed);
10909 TapeStartRecording(new_random_seed);
10911 #if defined(NETWORK_AVALIABLE)
10912 if (init_network_game)
10914 SendToServer_StartPlaying();
10925 static unsigned long game_frame_delay = 0;
10926 unsigned long game_frame_delay_value;
10927 byte *recorded_player_action;
10928 byte summarized_player_action = 0;
10929 byte tape_action[MAX_PLAYERS];
10932 /* detect endless loops, caused by custom element programming */
10933 if (recursion_loop_detected && recursion_loop_depth == 0)
10935 char *message = getStringCat3("Internal Error ! Element ",
10936 EL_NAME(recursion_loop_element),
10937 " caused endless loop ! Quit the game ?");
10939 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10940 EL_NAME(recursion_loop_element));
10942 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10944 recursion_loop_detected = FALSE; /* if game should be continued */
10951 if (game.restart_level)
10952 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10954 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10956 if (level.native_em_level->lev->home == 0) /* all players at home */
10958 PlayerWins(local_player);
10960 AllPlayersGone = TRUE;
10962 level.native_em_level->lev->home = -1;
10965 if (level.native_em_level->ply[0]->alive == 0 &&
10966 level.native_em_level->ply[1]->alive == 0 &&
10967 level.native_em_level->ply[2]->alive == 0 &&
10968 level.native_em_level->ply[3]->alive == 0) /* all dead */
10969 AllPlayersGone = TRUE;
10972 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10975 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10978 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10981 game_frame_delay_value =
10982 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10984 if (tape.playing && tape.warp_forward && !tape.pausing)
10985 game_frame_delay_value = 0;
10987 /* ---------- main game synchronization point ---------- */
10989 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10991 if (network_playing && !network_player_action_received)
10993 /* try to get network player actions in time */
10995 #if defined(NETWORK_AVALIABLE)
10996 /* last chance to get network player actions without main loop delay */
10997 HandleNetworking();
11000 /* game was quit by network peer */
11001 if (game_status != GAME_MODE_PLAYING)
11004 if (!network_player_action_received)
11005 return; /* failed to get network player actions in time */
11007 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11013 /* at this point we know that we really continue executing the game */
11015 network_player_action_received = FALSE;
11017 /* when playing tape, read previously recorded player input from tape data */
11018 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11021 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11026 if (tape.set_centered_player)
11028 game.centered_player_nr_next = tape.centered_player_nr_next;
11029 game.set_centered_player = TRUE;
11032 for (i = 0; i < MAX_PLAYERS; i++)
11034 summarized_player_action |= stored_player[i].action;
11036 if (!network_playing)
11037 stored_player[i].effective_action = stored_player[i].action;
11040 #if defined(NETWORK_AVALIABLE)
11041 if (network_playing)
11042 SendToServer_MovePlayer(summarized_player_action);
11045 if (!options.network && !setup.team_mode)
11046 local_player->effective_action = summarized_player_action;
11048 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11050 for (i = 0; i < MAX_PLAYERS; i++)
11051 stored_player[i].effective_action =
11052 (i == game.centered_player_nr ? summarized_player_action : 0);
11055 if (recorded_player_action != NULL)
11056 for (i = 0; i < MAX_PLAYERS; i++)
11057 stored_player[i].effective_action = recorded_player_action[i];
11059 for (i = 0; i < MAX_PLAYERS; i++)
11061 tape_action[i] = stored_player[i].effective_action;
11063 /* (this can only happen in the R'n'D game engine) */
11064 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11065 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11068 /* only record actions from input devices, but not programmed actions */
11069 if (tape.recording)
11070 TapeRecordAction(tape_action);
11072 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11074 GameActions_EM_Main();
11082 void GameActions_EM_Main()
11084 byte effective_action[MAX_PLAYERS];
11085 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11088 for (i = 0; i < MAX_PLAYERS; i++)
11089 effective_action[i] = stored_player[i].effective_action;
11091 GameActions_EM(effective_action, warp_mode);
11095 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11098 void GameActions_RND()
11100 int magic_wall_x = 0, magic_wall_y = 0;
11101 int i, x, y, element, graphic;
11103 InitPlayfieldScanModeVars();
11105 #if USE_ONE_MORE_CHANGE_PER_FRAME
11106 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11108 SCAN_PLAYFIELD(x, y)
11110 ChangeCount[x][y] = 0;
11111 ChangeEvent[x][y] = -1;
11116 if (game.set_centered_player)
11118 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11120 /* switching to "all players" only possible if all players fit to screen */
11121 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11123 game.centered_player_nr_next = game.centered_player_nr;
11124 game.set_centered_player = FALSE;
11127 /* do not switch focus to non-existing (or non-active) player */
11128 if (game.centered_player_nr_next >= 0 &&
11129 !stored_player[game.centered_player_nr_next].active)
11131 game.centered_player_nr_next = game.centered_player_nr;
11132 game.set_centered_player = FALSE;
11136 if (game.set_centered_player &&
11137 ScreenMovPos == 0) /* screen currently aligned at tile position */
11141 if (game.centered_player_nr_next == -1)
11143 setScreenCenteredToAllPlayers(&sx, &sy);
11147 sx = stored_player[game.centered_player_nr_next].jx;
11148 sy = stored_player[game.centered_player_nr_next].jy;
11151 game.centered_player_nr = game.centered_player_nr_next;
11152 game.set_centered_player = FALSE;
11154 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11155 DrawGameDoorValues();
11158 for (i = 0; i < MAX_PLAYERS; i++)
11160 int actual_player_action = stored_player[i].effective_action;
11163 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11164 - rnd_equinox_tetrachloride 048
11165 - rnd_equinox_tetrachloride_ii 096
11166 - rnd_emanuel_schmieg 002
11167 - doctor_sloan_ww 001, 020
11169 if (stored_player[i].MovPos == 0)
11170 CheckGravityMovement(&stored_player[i]);
11173 /* overwrite programmed action with tape action */
11174 if (stored_player[i].programmed_action)
11175 actual_player_action = stored_player[i].programmed_action;
11177 PlayerActions(&stored_player[i], actual_player_action);
11179 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11182 ScrollScreen(NULL, SCROLL_GO_ON);
11184 /* for backwards compatibility, the following code emulates a fixed bug that
11185 occured when pushing elements (causing elements that just made their last
11186 pushing step to already (if possible) make their first falling step in the
11187 same game frame, which is bad); this code is also needed to use the famous
11188 "spring push bug" which is used in older levels and might be wanted to be
11189 used also in newer levels, but in this case the buggy pushing code is only
11190 affecting the "spring" element and no other elements */
11192 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11194 for (i = 0; i < MAX_PLAYERS; i++)
11196 struct PlayerInfo *player = &stored_player[i];
11197 int x = player->jx;
11198 int y = player->jy;
11200 if (player->active && player->is_pushing && player->is_moving &&
11202 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11203 Feld[x][y] == EL_SPRING))
11205 ContinueMoving(x, y);
11207 /* continue moving after pushing (this is actually a bug) */
11208 if (!IS_MOVING(x, y))
11209 Stop[x][y] = FALSE;
11215 debug_print_timestamp(0, "start main loop profiling");
11218 SCAN_PLAYFIELD(x, y)
11220 ChangeCount[x][y] = 0;
11221 ChangeEvent[x][y] = -1;
11223 /* this must be handled before main playfield loop */
11224 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11227 if (MovDelay[x][y] <= 0)
11231 #if USE_NEW_SNAP_DELAY
11232 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11235 if (MovDelay[x][y] <= 0)
11238 DrawLevelField(x, y);
11240 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11246 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11248 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11249 printf("GameActions(): This should never happen!\n");
11251 ChangePage[x][y] = -1;
11255 Stop[x][y] = FALSE;
11256 if (WasJustMoving[x][y] > 0)
11257 WasJustMoving[x][y]--;
11258 if (WasJustFalling[x][y] > 0)
11259 WasJustFalling[x][y]--;
11260 if (CheckCollision[x][y] > 0)
11261 CheckCollision[x][y]--;
11262 if (CheckImpact[x][y] > 0)
11263 CheckImpact[x][y]--;
11267 /* reset finished pushing action (not done in ContinueMoving() to allow
11268 continuous pushing animation for elements with zero push delay) */
11269 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11271 ResetGfxAnimation(x, y);
11272 DrawLevelField(x, y);
11276 if (IS_BLOCKED(x, y))
11280 Blocked2Moving(x, y, &oldx, &oldy);
11281 if (!IS_MOVING(oldx, oldy))
11283 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11284 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11285 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11286 printf("GameActions(): This should never happen!\n");
11293 debug_print_timestamp(0, "- time for pre-main loop:");
11296 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11297 SCAN_PLAYFIELD(x, y)
11299 element = Feld[x][y];
11300 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11305 int element2 = element;
11306 int graphic2 = graphic;
11308 int element2 = Feld[x][y];
11309 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11311 int last_gfx_frame = GfxFrame[x][y];
11313 if (graphic_info[graphic2].anim_global_sync)
11314 GfxFrame[x][y] = FrameCounter;
11315 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11316 GfxFrame[x][y] = CustomValue[x][y];
11317 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11318 GfxFrame[x][y] = element_info[element2].collect_score;
11319 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11320 GfxFrame[x][y] = ChangeDelay[x][y];
11322 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11323 DrawLevelGraphicAnimation(x, y, graphic2);
11326 ResetGfxFrame(x, y, TRUE);
11330 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11331 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11332 ResetRandomAnimationValue(x, y);
11336 SetRandomAnimationValue(x, y);
11340 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11343 #endif // -------------------- !!! TEST ONLY !!! --------------------
11346 debug_print_timestamp(0, "- time for TEST loop: -->");
11349 SCAN_PLAYFIELD(x, y)
11351 element = Feld[x][y];
11352 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11354 ResetGfxFrame(x, y, TRUE);
11356 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11357 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11358 ResetRandomAnimationValue(x, y);
11360 SetRandomAnimationValue(x, y);
11362 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11364 if (IS_INACTIVE(element))
11366 if (IS_ANIMATED(graphic))
11367 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11372 /* this may take place after moving, so 'element' may have changed */
11373 if (IS_CHANGING(x, y) &&
11374 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11376 int page = element_info[element].event_page_nr[CE_DELAY];
11379 HandleElementChange(x, y, page);
11381 if (CAN_CHANGE(element))
11382 HandleElementChange(x, y, page);
11384 if (HAS_ACTION(element))
11385 ExecuteCustomElementAction(x, y, element, page);
11388 element = Feld[x][y];
11389 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11392 #if 0 // ---------------------------------------------------------------------
11394 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11398 element = Feld[x][y];
11399 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11401 if (IS_ANIMATED(graphic) &&
11402 !IS_MOVING(x, y) &&
11404 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11406 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11407 DrawTwinkleOnField(x, y);
11409 else if (IS_MOVING(x, y))
11410 ContinueMoving(x, y);
11417 case EL_EM_EXIT_OPEN:
11418 case EL_SP_EXIT_OPEN:
11419 case EL_STEEL_EXIT_OPEN:
11420 case EL_EM_STEEL_EXIT_OPEN:
11421 case EL_SP_TERMINAL:
11422 case EL_SP_TERMINAL_ACTIVE:
11423 case EL_EXTRA_TIME:
11424 case EL_SHIELD_NORMAL:
11425 case EL_SHIELD_DEADLY:
11426 if (IS_ANIMATED(graphic))
11427 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11430 case EL_DYNAMITE_ACTIVE:
11431 case EL_EM_DYNAMITE_ACTIVE:
11432 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11433 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11434 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11435 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11436 case EL_SP_DISK_RED_ACTIVE:
11437 CheckDynamite(x, y);
11440 case EL_AMOEBA_GROWING:
11441 AmoebeWaechst(x, y);
11444 case EL_AMOEBA_SHRINKING:
11445 AmoebaDisappearing(x, y);
11448 #if !USE_NEW_AMOEBA_CODE
11449 case EL_AMOEBA_WET:
11450 case EL_AMOEBA_DRY:
11451 case EL_AMOEBA_FULL:
11453 case EL_EMC_DRIPPER:
11454 AmoebeAbleger(x, y);
11458 case EL_GAME_OF_LIFE:
11463 case EL_EXIT_CLOSED:
11467 case EL_EM_EXIT_CLOSED:
11471 case EL_STEEL_EXIT_CLOSED:
11472 CheckExitSteel(x, y);
11475 case EL_EM_STEEL_EXIT_CLOSED:
11476 CheckExitSteelEM(x, y);
11479 case EL_SP_EXIT_CLOSED:
11483 case EL_EXPANDABLE_WALL_GROWING:
11484 case EL_EXPANDABLE_STEELWALL_GROWING:
11485 MauerWaechst(x, y);
11488 case EL_EXPANDABLE_WALL:
11489 case EL_EXPANDABLE_WALL_HORIZONTAL:
11490 case EL_EXPANDABLE_WALL_VERTICAL:
11491 case EL_EXPANDABLE_WALL_ANY:
11492 case EL_BD_EXPANDABLE_WALL:
11493 MauerAbleger(x, y);
11496 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11497 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11498 case EL_EXPANDABLE_STEELWALL_ANY:
11499 MauerAblegerStahl(x, y);
11503 CheckForDragon(x, y);
11509 case EL_ELEMENT_SNAPPING:
11510 case EL_DIAGONAL_SHRINKING:
11511 case EL_DIAGONAL_GROWING:
11514 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11516 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11521 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11522 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11527 #else // ---------------------------------------------------------------------
11529 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11533 element = Feld[x][y];
11534 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11536 if (IS_ANIMATED(graphic) &&
11537 !IS_MOVING(x, y) &&
11539 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11541 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11542 DrawTwinkleOnField(x, y);
11544 else if ((element == EL_ACID ||
11545 element == EL_EXIT_OPEN ||
11546 element == EL_EM_EXIT_OPEN ||
11547 element == EL_SP_EXIT_OPEN ||
11548 element == EL_STEEL_EXIT_OPEN ||
11549 element == EL_EM_STEEL_EXIT_OPEN ||
11550 element == EL_SP_TERMINAL ||
11551 element == EL_SP_TERMINAL_ACTIVE ||
11552 element == EL_EXTRA_TIME ||
11553 element == EL_SHIELD_NORMAL ||
11554 element == EL_SHIELD_DEADLY) &&
11555 IS_ANIMATED(graphic))
11556 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11557 else if (IS_MOVING(x, y))
11558 ContinueMoving(x, y);
11559 else if (IS_ACTIVE_BOMB(element))
11560 CheckDynamite(x, y);
11561 else if (element == EL_AMOEBA_GROWING)
11562 AmoebeWaechst(x, y);
11563 else if (element == EL_AMOEBA_SHRINKING)
11564 AmoebaDisappearing(x, y);
11566 #if !USE_NEW_AMOEBA_CODE
11567 else if (IS_AMOEBALIVE(element))
11568 AmoebeAbleger(x, y);
11571 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11573 else if (element == EL_EXIT_CLOSED)
11575 else if (element == EL_EM_EXIT_CLOSED)
11577 else if (element == EL_STEEL_EXIT_CLOSED)
11578 CheckExitSteel(x, y);
11579 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11580 CheckExitSteelEM(x, y);
11581 else if (element == EL_SP_EXIT_CLOSED)
11583 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11584 element == EL_EXPANDABLE_STEELWALL_GROWING)
11585 MauerWaechst(x, y);
11586 else if (element == EL_EXPANDABLE_WALL ||
11587 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11588 element == EL_EXPANDABLE_WALL_VERTICAL ||
11589 element == EL_EXPANDABLE_WALL_ANY ||
11590 element == EL_BD_EXPANDABLE_WALL)
11591 MauerAbleger(x, y);
11592 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11593 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11594 element == EL_EXPANDABLE_STEELWALL_ANY)
11595 MauerAblegerStahl(x, y);
11596 else if (element == EL_FLAMES)
11597 CheckForDragon(x, y);
11598 else if (element == EL_EXPLOSION)
11599 ; /* drawing of correct explosion animation is handled separately */
11600 else if (element == EL_ELEMENT_SNAPPING ||
11601 element == EL_DIAGONAL_SHRINKING ||
11602 element == EL_DIAGONAL_GROWING)
11604 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11606 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11608 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11609 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11611 #endif // ---------------------------------------------------------------------
11613 if (IS_BELT_ACTIVE(element))
11614 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11616 if (game.magic_wall_active)
11618 int jx = local_player->jx, jy = local_player->jy;
11620 /* play the element sound at the position nearest to the player */
11621 if ((element == EL_MAGIC_WALL_FULL ||
11622 element == EL_MAGIC_WALL_ACTIVE ||
11623 element == EL_MAGIC_WALL_EMPTYING ||
11624 element == EL_BD_MAGIC_WALL_FULL ||
11625 element == EL_BD_MAGIC_WALL_ACTIVE ||
11626 element == EL_BD_MAGIC_WALL_EMPTYING ||
11627 element == EL_DC_MAGIC_WALL_FULL ||
11628 element == EL_DC_MAGIC_WALL_ACTIVE ||
11629 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11630 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11639 debug_print_timestamp(0, "- time for MAIN loop: -->");
11642 #if USE_NEW_AMOEBA_CODE
11643 /* new experimental amoeba growth stuff */
11644 if (!(FrameCounter % 8))
11646 static unsigned long random = 1684108901;
11648 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11650 x = RND(lev_fieldx);
11651 y = RND(lev_fieldy);
11652 element = Feld[x][y];
11654 if (!IS_PLAYER(x,y) &&
11655 (element == EL_EMPTY ||
11656 CAN_GROW_INTO(element) ||
11657 element == EL_QUICKSAND_EMPTY ||
11658 element == EL_QUICKSAND_FAST_EMPTY ||
11659 element == EL_ACID_SPLASH_LEFT ||
11660 element == EL_ACID_SPLASH_RIGHT))
11662 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11663 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11664 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11665 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11666 Feld[x][y] = EL_AMOEBA_DROP;
11669 random = random * 129 + 1;
11675 if (game.explosions_delayed)
11678 game.explosions_delayed = FALSE;
11680 SCAN_PLAYFIELD(x, y)
11682 element = Feld[x][y];
11684 if (ExplodeField[x][y])
11685 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11686 else if (element == EL_EXPLOSION)
11687 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11689 ExplodeField[x][y] = EX_TYPE_NONE;
11692 game.explosions_delayed = TRUE;
11695 if (game.magic_wall_active)
11697 if (!(game.magic_wall_time_left % 4))
11699 int element = Feld[magic_wall_x][magic_wall_y];
11701 if (element == EL_BD_MAGIC_WALL_FULL ||
11702 element == EL_BD_MAGIC_WALL_ACTIVE ||
11703 element == EL_BD_MAGIC_WALL_EMPTYING)
11704 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11705 else if (element == EL_DC_MAGIC_WALL_FULL ||
11706 element == EL_DC_MAGIC_WALL_ACTIVE ||
11707 element == EL_DC_MAGIC_WALL_EMPTYING)
11708 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11710 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11713 if (game.magic_wall_time_left > 0)
11715 game.magic_wall_time_left--;
11716 if (!game.magic_wall_time_left)
11718 SCAN_PLAYFIELD(x, y)
11720 element = Feld[x][y];
11722 if (element == EL_MAGIC_WALL_ACTIVE ||
11723 element == EL_MAGIC_WALL_FULL)
11725 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11726 DrawLevelField(x, y);
11728 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11729 element == EL_BD_MAGIC_WALL_FULL)
11731 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11732 DrawLevelField(x, y);
11734 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11735 element == EL_DC_MAGIC_WALL_FULL)
11737 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11738 DrawLevelField(x, y);
11742 game.magic_wall_active = FALSE;
11747 if (game.light_time_left > 0)
11749 game.light_time_left--;
11751 if (game.light_time_left == 0)
11752 RedrawAllLightSwitchesAndInvisibleElements();
11755 if (game.timegate_time_left > 0)
11757 game.timegate_time_left--;
11759 if (game.timegate_time_left == 0)
11760 CloseAllOpenTimegates();
11763 if (game.lenses_time_left > 0)
11765 game.lenses_time_left--;
11767 if (game.lenses_time_left == 0)
11768 RedrawAllInvisibleElementsForLenses();
11771 if (game.magnify_time_left > 0)
11773 game.magnify_time_left--;
11775 if (game.magnify_time_left == 0)
11776 RedrawAllInvisibleElementsForMagnifier();
11779 for (i = 0; i < MAX_PLAYERS; i++)
11781 struct PlayerInfo *player = &stored_player[i];
11783 if (SHIELD_ON(player))
11785 if (player->shield_deadly_time_left)
11786 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11787 else if (player->shield_normal_time_left)
11788 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11795 PlayAllPlayersSound();
11797 if (options.debug) /* calculate frames per second */
11799 static unsigned long fps_counter = 0;
11800 static int fps_frames = 0;
11801 unsigned long fps_delay_ms = Counter() - fps_counter;
11805 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11807 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11810 fps_counter = Counter();
11813 redraw_mask |= REDRAW_FPS;
11816 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11818 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11820 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11822 local_player->show_envelope = 0;
11826 debug_print_timestamp(0, "stop main loop profiling ");
11827 printf("----------------------------------------------------------\n");
11830 /* use random number generator in every frame to make it less predictable */
11831 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11835 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11837 int min_x = x, min_y = y, max_x = x, max_y = y;
11840 for (i = 0; i < MAX_PLAYERS; i++)
11842 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11844 if (!stored_player[i].active || &stored_player[i] == player)
11847 min_x = MIN(min_x, jx);
11848 min_y = MIN(min_y, jy);
11849 max_x = MAX(max_x, jx);
11850 max_y = MAX(max_y, jy);
11853 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11856 static boolean AllPlayersInVisibleScreen()
11860 for (i = 0; i < MAX_PLAYERS; i++)
11862 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11864 if (!stored_player[i].active)
11867 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11874 void ScrollLevel(int dx, int dy)
11877 static Bitmap *bitmap_db_field2 = NULL;
11878 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11885 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11886 /* only horizontal XOR vertical scroll direction allowed */
11887 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11892 if (bitmap_db_field2 == NULL)
11893 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11895 /* needed when blitting directly to same bitmap -- should not be needed with
11896 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11897 BlitBitmap(drawto_field, bitmap_db_field2,
11898 FX + TILEX * (dx == -1) - softscroll_offset,
11899 FY + TILEY * (dy == -1) - softscroll_offset,
11900 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11901 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11902 FX + TILEX * (dx == 1) - softscroll_offset,
11903 FY + TILEY * (dy == 1) - softscroll_offset);
11904 BlitBitmap(bitmap_db_field2, drawto_field,
11905 FX + TILEX * (dx == 1) - softscroll_offset,
11906 FY + TILEY * (dy == 1) - softscroll_offset,
11907 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11908 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11909 FX + TILEX * (dx == 1) - softscroll_offset,
11910 FY + TILEY * (dy == 1) - softscroll_offset);
11915 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11916 int xsize = (BX2 - BX1 + 1);
11917 int ysize = (BY2 - BY1 + 1);
11918 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11919 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11920 int step = (start < end ? +1 : -1);
11922 for (i = start; i != end; i += step)
11924 BlitBitmap(drawto_field, drawto_field,
11925 FX + TILEX * (dx != 0 ? i + step : 0),
11926 FY + TILEY * (dy != 0 ? i + step : 0),
11927 TILEX * (dx != 0 ? 1 : xsize),
11928 TILEY * (dy != 0 ? 1 : ysize),
11929 FX + TILEX * (dx != 0 ? i : 0),
11930 FY + TILEY * (dy != 0 ? i : 0));
11935 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11937 BlitBitmap(drawto_field, drawto_field,
11938 FX + TILEX * (dx == -1) - softscroll_offset,
11939 FY + TILEY * (dy == -1) - softscroll_offset,
11940 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11941 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11942 FX + TILEX * (dx == 1) - softscroll_offset,
11943 FY + TILEY * (dy == 1) - softscroll_offset);
11949 x = (dx == 1 ? BX1 : BX2);
11950 for (y = BY1; y <= BY2; y++)
11951 DrawScreenField(x, y);
11956 y = (dy == 1 ? BY1 : BY2);
11957 for (x = BX1; x <= BX2; x++)
11958 DrawScreenField(x, y);
11961 redraw_mask |= REDRAW_FIELD;
11964 static boolean canFallDown(struct PlayerInfo *player)
11966 int jx = player->jx, jy = player->jy;
11968 return (IN_LEV_FIELD(jx, jy + 1) &&
11969 (IS_FREE(jx, jy + 1) ||
11970 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11971 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11972 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11975 static boolean canPassField(int x, int y, int move_dir)
11977 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11978 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11979 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11980 int nextx = x + dx;
11981 int nexty = y + dy;
11982 int element = Feld[x][y];
11984 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11985 !CAN_MOVE(element) &&
11986 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11987 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11988 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11991 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11993 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11994 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11995 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11999 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12000 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12001 (IS_DIGGABLE(Feld[newx][newy]) ||
12002 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12003 canPassField(newx, newy, move_dir)));
12006 static void CheckGravityMovement(struct PlayerInfo *player)
12008 #if USE_PLAYER_GRAVITY
12009 if (player->gravity && !player->programmed_action)
12011 if (game.gravity && !player->programmed_action)
12014 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12015 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12016 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12017 int jx = player->jx, jy = player->jy;
12018 boolean player_is_moving_to_valid_field =
12019 (!player_is_snapping &&
12020 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12021 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12022 boolean player_can_fall_down = canFallDown(player);
12024 if (player_can_fall_down &&
12025 !player_is_moving_to_valid_field)
12026 player->programmed_action = MV_DOWN;
12030 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12032 return CheckGravityMovement(player);
12034 #if USE_PLAYER_GRAVITY
12035 if (player->gravity && !player->programmed_action)
12037 if (game.gravity && !player->programmed_action)
12040 int jx = player->jx, jy = player->jy;
12041 boolean field_under_player_is_free =
12042 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12043 boolean player_is_standing_on_valid_field =
12044 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12045 (IS_WALKABLE(Feld[jx][jy]) &&
12046 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12048 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12049 player->programmed_action = MV_DOWN;
12054 MovePlayerOneStep()
12055 -----------------------------------------------------------------------------
12056 dx, dy: direction (non-diagonal) to try to move the player to
12057 real_dx, real_dy: direction as read from input device (can be diagonal)
12060 boolean MovePlayerOneStep(struct PlayerInfo *player,
12061 int dx, int dy, int real_dx, int real_dy)
12063 int jx = player->jx, jy = player->jy;
12064 int new_jx = jx + dx, new_jy = jy + dy;
12065 #if !USE_FIXED_DONT_RUN_INTO
12069 boolean player_can_move = !player->cannot_move;
12071 if (!player->active || (!dx && !dy))
12072 return MP_NO_ACTION;
12074 player->MovDir = (dx < 0 ? MV_LEFT :
12075 dx > 0 ? MV_RIGHT :
12077 dy > 0 ? MV_DOWN : MV_NONE);
12079 if (!IN_LEV_FIELD(new_jx, new_jy))
12080 return MP_NO_ACTION;
12082 if (!player_can_move)
12084 if (player->MovPos == 0)
12086 player->is_moving = FALSE;
12087 player->is_digging = FALSE;
12088 player->is_collecting = FALSE;
12089 player->is_snapping = FALSE;
12090 player->is_pushing = FALSE;
12095 if (!options.network && game.centered_player_nr == -1 &&
12096 !AllPlayersInSight(player, new_jx, new_jy))
12097 return MP_NO_ACTION;
12099 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12100 return MP_NO_ACTION;
12103 #if !USE_FIXED_DONT_RUN_INTO
12104 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12106 /* (moved to DigField()) */
12107 if (player_can_move && DONT_RUN_INTO(element))
12109 if (element == EL_ACID && dx == 0 && dy == 1)
12111 SplashAcid(new_jx, new_jy);
12112 Feld[jx][jy] = EL_PLAYER_1;
12113 InitMovingField(jx, jy, MV_DOWN);
12114 Store[jx][jy] = EL_ACID;
12115 ContinueMoving(jx, jy);
12116 BuryPlayer(player);
12119 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12125 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12126 if (can_move != MP_MOVING)
12129 /* check if DigField() has caused relocation of the player */
12130 if (player->jx != jx || player->jy != jy)
12131 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12133 StorePlayer[jx][jy] = 0;
12134 player->last_jx = jx;
12135 player->last_jy = jy;
12136 player->jx = new_jx;
12137 player->jy = new_jy;
12138 StorePlayer[new_jx][new_jy] = player->element_nr;
12140 if (player->move_delay_value_next != -1)
12142 player->move_delay_value = player->move_delay_value_next;
12143 player->move_delay_value_next = -1;
12147 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12149 player->step_counter++;
12151 PlayerVisit[jx][jy] = FrameCounter;
12153 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12154 player->is_moving = TRUE;
12158 /* should better be called in MovePlayer(), but this breaks some tapes */
12159 ScrollPlayer(player, SCROLL_INIT);
12165 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12167 int jx = player->jx, jy = player->jy;
12168 int old_jx = jx, old_jy = jy;
12169 int moved = MP_NO_ACTION;
12171 if (!player->active)
12176 if (player->MovPos == 0)
12178 player->is_moving = FALSE;
12179 player->is_digging = FALSE;
12180 player->is_collecting = FALSE;
12181 player->is_snapping = FALSE;
12182 player->is_pushing = FALSE;
12188 if (player->move_delay > 0)
12191 player->move_delay = -1; /* set to "uninitialized" value */
12193 /* store if player is automatically moved to next field */
12194 player->is_auto_moving = (player->programmed_action != MV_NONE);
12196 /* remove the last programmed player action */
12197 player->programmed_action = 0;
12199 if (player->MovPos)
12201 /* should only happen if pre-1.2 tape recordings are played */
12202 /* this is only for backward compatibility */
12204 int original_move_delay_value = player->move_delay_value;
12207 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12211 /* scroll remaining steps with finest movement resolution */
12212 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12214 while (player->MovPos)
12216 ScrollPlayer(player, SCROLL_GO_ON);
12217 ScrollScreen(NULL, SCROLL_GO_ON);
12219 AdvanceFrameAndPlayerCounters(player->index_nr);
12225 player->move_delay_value = original_move_delay_value;
12228 player->is_active = FALSE;
12230 if (player->last_move_dir & MV_HORIZONTAL)
12232 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12233 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12237 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12238 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12241 #if USE_FIXED_BORDER_RUNNING_GFX
12242 if (!moved && !player->is_active)
12244 player->is_moving = FALSE;
12245 player->is_digging = FALSE;
12246 player->is_collecting = FALSE;
12247 player->is_snapping = FALSE;
12248 player->is_pushing = FALSE;
12256 if (moved & MP_MOVING && !ScreenMovPos &&
12257 (player->index_nr == game.centered_player_nr ||
12258 game.centered_player_nr == -1))
12260 if (moved & MP_MOVING && !ScreenMovPos &&
12261 (player == local_player || !options.network))
12264 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12265 int offset = game.scroll_delay_value;
12267 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12269 /* actual player has left the screen -- scroll in that direction */
12270 if (jx != old_jx) /* player has moved horizontally */
12271 scroll_x += (jx - old_jx);
12272 else /* player has moved vertically */
12273 scroll_y += (jy - old_jy);
12277 if (jx != old_jx) /* player has moved horizontally */
12279 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12280 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12281 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12283 /* don't scroll over playfield boundaries */
12284 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12285 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12287 /* don't scroll more than one field at a time */
12288 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12290 /* don't scroll against the player's moving direction */
12291 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12292 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12293 scroll_x = old_scroll_x;
12295 else /* player has moved vertically */
12297 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12298 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12299 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12301 /* don't scroll over playfield boundaries */
12302 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12303 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12305 /* don't scroll more than one field at a time */
12306 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12308 /* don't scroll against the player's moving direction */
12309 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12310 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12311 scroll_y = old_scroll_y;
12315 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12318 if (!options.network && game.centered_player_nr == -1 &&
12319 !AllPlayersInVisibleScreen())
12321 scroll_x = old_scroll_x;
12322 scroll_y = old_scroll_y;
12326 if (!options.network && !AllPlayersInVisibleScreen())
12328 scroll_x = old_scroll_x;
12329 scroll_y = old_scroll_y;
12334 ScrollScreen(player, SCROLL_INIT);
12335 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12340 player->StepFrame = 0;
12342 if (moved & MP_MOVING)
12344 if (old_jx != jx && old_jy == jy)
12345 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12346 else if (old_jx == jx && old_jy != jy)
12347 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12349 DrawLevelField(jx, jy); /* for "crumbled sand" */
12351 player->last_move_dir = player->MovDir;
12352 player->is_moving = TRUE;
12353 player->is_snapping = FALSE;
12354 player->is_switching = FALSE;
12355 player->is_dropping = FALSE;
12356 player->is_dropping_pressed = FALSE;
12357 player->drop_pressed_delay = 0;
12360 /* should better be called here than above, but this breaks some tapes */
12361 ScrollPlayer(player, SCROLL_INIT);
12366 CheckGravityMovementWhenNotMoving(player);
12368 player->is_moving = FALSE;
12370 /* at this point, the player is allowed to move, but cannot move right now
12371 (e.g. because of something blocking the way) -- ensure that the player
12372 is also allowed to move in the next frame (in old versions before 3.1.1,
12373 the player was forced to wait again for eight frames before next try) */
12375 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12376 player->move_delay = 0; /* allow direct movement in the next frame */
12379 if (player->move_delay == -1) /* not yet initialized by DigField() */
12380 player->move_delay = player->move_delay_value;
12382 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12384 TestIfPlayerTouchesBadThing(jx, jy);
12385 TestIfPlayerTouchesCustomElement(jx, jy);
12388 if (!player->active)
12389 RemovePlayer(player);
12394 void ScrollPlayer(struct PlayerInfo *player, int mode)
12396 int jx = player->jx, jy = player->jy;
12397 int last_jx = player->last_jx, last_jy = player->last_jy;
12398 int move_stepsize = TILEX / player->move_delay_value;
12400 #if USE_NEW_PLAYER_SPEED
12401 if (!player->active)
12404 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12407 if (!player->active || player->MovPos == 0)
12411 if (mode == SCROLL_INIT)
12413 player->actual_frame_counter = FrameCounter;
12414 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12416 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12417 Feld[last_jx][last_jy] == EL_EMPTY)
12419 int last_field_block_delay = 0; /* start with no blocking at all */
12420 int block_delay_adjustment = player->block_delay_adjustment;
12422 /* if player blocks last field, add delay for exactly one move */
12423 if (player->block_last_field)
12425 last_field_block_delay += player->move_delay_value;
12427 /* when blocking enabled, prevent moving up despite gravity */
12428 #if USE_PLAYER_GRAVITY
12429 if (player->gravity && player->MovDir == MV_UP)
12430 block_delay_adjustment = -1;
12432 if (game.gravity && player->MovDir == MV_UP)
12433 block_delay_adjustment = -1;
12437 /* add block delay adjustment (also possible when not blocking) */
12438 last_field_block_delay += block_delay_adjustment;
12440 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12441 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12444 #if USE_NEW_PLAYER_SPEED
12445 if (player->MovPos != 0) /* player has not yet reached destination */
12451 else if (!FrameReached(&player->actual_frame_counter, 1))
12454 #if USE_NEW_PLAYER_SPEED
12455 if (player->MovPos != 0)
12457 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12458 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12460 /* before DrawPlayer() to draw correct player graphic for this case */
12461 if (player->MovPos == 0)
12462 CheckGravityMovement(player);
12465 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12466 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12468 /* before DrawPlayer() to draw correct player graphic for this case */
12469 if (player->MovPos == 0)
12470 CheckGravityMovement(player);
12473 if (player->MovPos == 0) /* player reached destination field */
12475 if (player->move_delay_reset_counter > 0)
12477 player->move_delay_reset_counter--;
12479 if (player->move_delay_reset_counter == 0)
12481 /* continue with normal speed after quickly moving through gate */
12482 HALVE_PLAYER_SPEED(player);
12484 /* be able to make the next move without delay */
12485 player->move_delay = 0;
12489 player->last_jx = jx;
12490 player->last_jy = jy;
12492 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12493 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12494 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12495 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12496 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12497 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12499 DrawPlayer(player); /* needed here only to cleanup last field */
12500 RemovePlayer(player);
12502 if (local_player->friends_still_needed == 0 ||
12503 IS_SP_ELEMENT(Feld[jx][jy]))
12504 PlayerWins(player);
12507 /* this breaks one level: "machine", level 000 */
12509 int move_direction = player->MovDir;
12510 int enter_side = MV_DIR_OPPOSITE(move_direction);
12511 int leave_side = move_direction;
12512 int old_jx = last_jx;
12513 int old_jy = last_jy;
12514 int old_element = Feld[old_jx][old_jy];
12515 int new_element = Feld[jx][jy];
12517 if (IS_CUSTOM_ELEMENT(old_element))
12518 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12520 player->index_bit, leave_side);
12522 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12523 CE_PLAYER_LEAVES_X,
12524 player->index_bit, leave_side);
12526 if (IS_CUSTOM_ELEMENT(new_element))
12527 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12528 player->index_bit, enter_side);
12530 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12531 CE_PLAYER_ENTERS_X,
12532 player->index_bit, enter_side);
12534 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12535 CE_MOVE_OF_X, move_direction);
12538 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12540 TestIfPlayerTouchesBadThing(jx, jy);
12541 TestIfPlayerTouchesCustomElement(jx, jy);
12543 /* needed because pushed element has not yet reached its destination,
12544 so it would trigger a change event at its previous field location */
12545 if (!player->is_pushing)
12546 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12548 if (!player->active)
12549 RemovePlayer(player);
12552 if (!local_player->LevelSolved && level.use_step_counter)
12562 if (TimeLeft <= 10 && setup.time_limit)
12563 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12566 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12568 DisplayGameControlValues();
12570 DrawGameValue_Time(TimeLeft);
12573 if (!TimeLeft && setup.time_limit)
12574 for (i = 0; i < MAX_PLAYERS; i++)
12575 KillPlayer(&stored_player[i]);
12578 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12580 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12582 DisplayGameControlValues();
12585 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12586 DrawGameValue_Time(TimePlayed);
12590 if (tape.single_step && tape.recording && !tape.pausing &&
12591 !player->programmed_action)
12592 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12596 void ScrollScreen(struct PlayerInfo *player, int mode)
12598 static unsigned long screen_frame_counter = 0;
12600 if (mode == SCROLL_INIT)
12602 /* set scrolling step size according to actual player's moving speed */
12603 ScrollStepSize = TILEX / player->move_delay_value;
12605 screen_frame_counter = FrameCounter;
12606 ScreenMovDir = player->MovDir;
12607 ScreenMovPos = player->MovPos;
12608 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12611 else if (!FrameReached(&screen_frame_counter, 1))
12616 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12617 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12618 redraw_mask |= REDRAW_FIELD;
12621 ScreenMovDir = MV_NONE;
12624 void TestIfPlayerTouchesCustomElement(int x, int y)
12626 static int xy[4][2] =
12633 static int trigger_sides[4][2] =
12635 /* center side border side */
12636 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12637 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12638 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12639 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12641 static int touch_dir[4] =
12643 MV_LEFT | MV_RIGHT,
12648 int center_element = Feld[x][y]; /* should always be non-moving! */
12651 for (i = 0; i < NUM_DIRECTIONS; i++)
12653 int xx = x + xy[i][0];
12654 int yy = y + xy[i][1];
12655 int center_side = trigger_sides[i][0];
12656 int border_side = trigger_sides[i][1];
12657 int border_element;
12659 if (!IN_LEV_FIELD(xx, yy))
12662 if (IS_PLAYER(x, y))
12664 struct PlayerInfo *player = PLAYERINFO(x, y);
12666 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12667 border_element = Feld[xx][yy]; /* may be moving! */
12668 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12669 border_element = Feld[xx][yy];
12670 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12671 border_element = MovingOrBlocked2Element(xx, yy);
12673 continue; /* center and border element do not touch */
12675 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12676 player->index_bit, border_side);
12677 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12678 CE_PLAYER_TOUCHES_X,
12679 player->index_bit, border_side);
12681 else if (IS_PLAYER(xx, yy))
12683 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12685 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12687 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12688 continue; /* center and border element do not touch */
12691 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12692 player->index_bit, center_side);
12693 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12694 CE_PLAYER_TOUCHES_X,
12695 player->index_bit, center_side);
12701 #if USE_ELEMENT_TOUCHING_BUGFIX
12703 void TestIfElementTouchesCustomElement(int x, int y)
12705 static int xy[4][2] =
12712 static int trigger_sides[4][2] =
12714 /* center side border side */
12715 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12716 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12717 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12718 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12720 static int touch_dir[4] =
12722 MV_LEFT | MV_RIGHT,
12727 boolean change_center_element = FALSE;
12728 int center_element = Feld[x][y]; /* should always be non-moving! */
12729 int border_element_old[NUM_DIRECTIONS];
12732 for (i = 0; i < NUM_DIRECTIONS; i++)
12734 int xx = x + xy[i][0];
12735 int yy = y + xy[i][1];
12736 int border_element;
12738 border_element_old[i] = -1;
12740 if (!IN_LEV_FIELD(xx, yy))
12743 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12744 border_element = Feld[xx][yy]; /* may be moving! */
12745 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12746 border_element = Feld[xx][yy];
12747 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12748 border_element = MovingOrBlocked2Element(xx, yy);
12750 continue; /* center and border element do not touch */
12752 border_element_old[i] = border_element;
12755 for (i = 0; i < NUM_DIRECTIONS; i++)
12757 int xx = x + xy[i][0];
12758 int yy = y + xy[i][1];
12759 int center_side = trigger_sides[i][0];
12760 int border_element = border_element_old[i];
12762 if (border_element == -1)
12765 /* check for change of border element */
12766 CheckElementChangeBySide(xx, yy, border_element, center_element,
12767 CE_TOUCHING_X, center_side);
12770 for (i = 0; i < NUM_DIRECTIONS; i++)
12772 int border_side = trigger_sides[i][1];
12773 int border_element = border_element_old[i];
12775 if (border_element == -1)
12778 /* check for change of center element (but change it only once) */
12779 if (!change_center_element)
12780 change_center_element =
12781 CheckElementChangeBySide(x, y, center_element, border_element,
12782 CE_TOUCHING_X, border_side);
12788 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12790 static int xy[4][2] =
12797 static int trigger_sides[4][2] =
12799 /* center side border side */
12800 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12801 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12802 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12803 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12805 static int touch_dir[4] =
12807 MV_LEFT | MV_RIGHT,
12812 boolean change_center_element = FALSE;
12813 int center_element = Feld[x][y]; /* should always be non-moving! */
12816 for (i = 0; i < NUM_DIRECTIONS; i++)
12818 int xx = x + xy[i][0];
12819 int yy = y + xy[i][1];
12820 int center_side = trigger_sides[i][0];
12821 int border_side = trigger_sides[i][1];
12822 int border_element;
12824 if (!IN_LEV_FIELD(xx, yy))
12827 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12828 border_element = Feld[xx][yy]; /* may be moving! */
12829 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12830 border_element = Feld[xx][yy];
12831 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12832 border_element = MovingOrBlocked2Element(xx, yy);
12834 continue; /* center and border element do not touch */
12836 /* check for change of center element (but change it only once) */
12837 if (!change_center_element)
12838 change_center_element =
12839 CheckElementChangeBySide(x, y, center_element, border_element,
12840 CE_TOUCHING_X, border_side);
12842 /* check for change of border element */
12843 CheckElementChangeBySide(xx, yy, border_element, center_element,
12844 CE_TOUCHING_X, center_side);
12850 void TestIfElementHitsCustomElement(int x, int y, int direction)
12852 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12853 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12854 int hitx = x + dx, hity = y + dy;
12855 int hitting_element = Feld[x][y];
12856 int touched_element;
12858 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12861 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12862 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12864 if (IN_LEV_FIELD(hitx, hity))
12866 int opposite_direction = MV_DIR_OPPOSITE(direction);
12867 int hitting_side = direction;
12868 int touched_side = opposite_direction;
12869 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12870 MovDir[hitx][hity] != direction ||
12871 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12877 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12878 CE_HITTING_X, touched_side);
12880 CheckElementChangeBySide(hitx, hity, touched_element,
12881 hitting_element, CE_HIT_BY_X, hitting_side);
12883 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12884 CE_HIT_BY_SOMETHING, opposite_direction);
12888 /* "hitting something" is also true when hitting the playfield border */
12889 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12890 CE_HITTING_SOMETHING, direction);
12894 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12896 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12897 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12898 int hitx = x + dx, hity = y + dy;
12899 int hitting_element = Feld[x][y];
12900 int touched_element;
12902 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12903 !IS_FREE(hitx, hity) &&
12904 (!IS_MOVING(hitx, hity) ||
12905 MovDir[hitx][hity] != direction ||
12906 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12909 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12913 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12917 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12918 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12920 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12921 EP_CAN_SMASH_EVERYTHING, direction);
12923 if (IN_LEV_FIELD(hitx, hity))
12925 int opposite_direction = MV_DIR_OPPOSITE(direction);
12926 int hitting_side = direction;
12927 int touched_side = opposite_direction;
12929 int touched_element = MovingOrBlocked2Element(hitx, hity);
12932 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12933 MovDir[hitx][hity] != direction ||
12934 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12943 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12944 CE_SMASHED_BY_SOMETHING, opposite_direction);
12946 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12947 CE_OTHER_IS_SMASHING, touched_side);
12949 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12950 CE_OTHER_GETS_SMASHED, hitting_side);
12956 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12958 int i, kill_x = -1, kill_y = -1;
12960 int bad_element = -1;
12961 static int test_xy[4][2] =
12968 static int test_dir[4] =
12976 for (i = 0; i < NUM_DIRECTIONS; i++)
12978 int test_x, test_y, test_move_dir, test_element;
12980 test_x = good_x + test_xy[i][0];
12981 test_y = good_y + test_xy[i][1];
12983 if (!IN_LEV_FIELD(test_x, test_y))
12987 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12989 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12991 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12992 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12994 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12995 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12999 bad_element = test_element;
13005 if (kill_x != -1 || kill_y != -1)
13007 if (IS_PLAYER(good_x, good_y))
13009 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13011 if (player->shield_deadly_time_left > 0 &&
13012 !IS_INDESTRUCTIBLE(bad_element))
13013 Bang(kill_x, kill_y);
13014 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13015 KillPlayer(player);
13018 Bang(good_x, good_y);
13022 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13024 int i, kill_x = -1, kill_y = -1;
13025 int bad_element = Feld[bad_x][bad_y];
13026 static int test_xy[4][2] =
13033 static int touch_dir[4] =
13035 MV_LEFT | MV_RIGHT,
13040 static int test_dir[4] =
13048 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13051 for (i = 0; i < NUM_DIRECTIONS; i++)
13053 int test_x, test_y, test_move_dir, test_element;
13055 test_x = bad_x + test_xy[i][0];
13056 test_y = bad_y + test_xy[i][1];
13057 if (!IN_LEV_FIELD(test_x, test_y))
13061 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13063 test_element = Feld[test_x][test_y];
13065 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13066 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13068 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13069 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13071 /* good thing is player or penguin that does not move away */
13072 if (IS_PLAYER(test_x, test_y))
13074 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13076 if (bad_element == EL_ROBOT && player->is_moving)
13077 continue; /* robot does not kill player if he is moving */
13079 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13081 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13082 continue; /* center and border element do not touch */
13089 else if (test_element == EL_PENGUIN)
13098 if (kill_x != -1 || kill_y != -1)
13100 if (IS_PLAYER(kill_x, kill_y))
13102 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13104 if (player->shield_deadly_time_left > 0 &&
13105 !IS_INDESTRUCTIBLE(bad_element))
13106 Bang(bad_x, bad_y);
13107 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13108 KillPlayer(player);
13111 Bang(kill_x, kill_y);
13115 void TestIfPlayerTouchesBadThing(int x, int y)
13117 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13120 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13122 TestIfGoodThingHitsBadThing(x, y, move_dir);
13125 void TestIfBadThingTouchesPlayer(int x, int y)
13127 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13130 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13132 TestIfBadThingHitsGoodThing(x, y, move_dir);
13135 void TestIfFriendTouchesBadThing(int x, int y)
13137 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13140 void TestIfBadThingTouchesFriend(int x, int y)
13142 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13145 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13147 int i, kill_x = bad_x, kill_y = bad_y;
13148 static int xy[4][2] =
13156 for (i = 0; i < NUM_DIRECTIONS; i++)
13160 x = bad_x + xy[i][0];
13161 y = bad_y + xy[i][1];
13162 if (!IN_LEV_FIELD(x, y))
13165 element = Feld[x][y];
13166 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13167 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13175 if (kill_x != bad_x || kill_y != bad_y)
13176 Bang(bad_x, bad_y);
13179 void KillPlayer(struct PlayerInfo *player)
13181 int jx = player->jx, jy = player->jy;
13183 if (!player->active)
13186 /* the following code was introduced to prevent an infinite loop when calling
13188 -> CheckTriggeredElementChangeExt()
13189 -> ExecuteCustomElementAction()
13191 -> (infinitely repeating the above sequence of function calls)
13192 which occurs when killing the player while having a CE with the setting
13193 "kill player X when explosion of <player X>"; the solution using a new
13194 field "player->killed" was chosen for backwards compatibility, although
13195 clever use of the fields "player->active" etc. would probably also work */
13197 if (player->killed)
13201 player->killed = TRUE;
13203 /* remove accessible field at the player's position */
13204 Feld[jx][jy] = EL_EMPTY;
13206 /* deactivate shield (else Bang()/Explode() would not work right) */
13207 player->shield_normal_time_left = 0;
13208 player->shield_deadly_time_left = 0;
13211 BuryPlayer(player);
13214 static void KillPlayerUnlessEnemyProtected(int x, int y)
13216 if (!PLAYER_ENEMY_PROTECTED(x, y))
13217 KillPlayer(PLAYERINFO(x, y));
13220 static void KillPlayerUnlessExplosionProtected(int x, int y)
13222 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13223 KillPlayer(PLAYERINFO(x, y));
13226 void BuryPlayer(struct PlayerInfo *player)
13228 int jx = player->jx, jy = player->jy;
13230 if (!player->active)
13233 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13234 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13236 player->GameOver = TRUE;
13237 RemovePlayer(player);
13240 void RemovePlayer(struct PlayerInfo *player)
13242 int jx = player->jx, jy = player->jy;
13243 int i, found = FALSE;
13245 player->present = FALSE;
13246 player->active = FALSE;
13248 if (!ExplodeField[jx][jy])
13249 StorePlayer[jx][jy] = 0;
13251 if (player->is_moving)
13252 DrawLevelField(player->last_jx, player->last_jy);
13254 for (i = 0; i < MAX_PLAYERS; i++)
13255 if (stored_player[i].active)
13259 AllPlayersGone = TRUE;
13265 #if USE_NEW_SNAP_DELAY
13266 static void setFieldForSnapping(int x, int y, int element, int direction)
13268 struct ElementInfo *ei = &element_info[element];
13269 int direction_bit = MV_DIR_TO_BIT(direction);
13270 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13271 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13272 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13274 Feld[x][y] = EL_ELEMENT_SNAPPING;
13275 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13277 ResetGfxAnimation(x, y);
13279 GfxElement[x][y] = element;
13280 GfxAction[x][y] = action;
13281 GfxDir[x][y] = direction;
13282 GfxFrame[x][y] = -1;
13287 =============================================================================
13288 checkDiagonalPushing()
13289 -----------------------------------------------------------------------------
13290 check if diagonal input device direction results in pushing of object
13291 (by checking if the alternative direction is walkable, diggable, ...)
13292 =============================================================================
13295 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13296 int x, int y, int real_dx, int real_dy)
13298 int jx, jy, dx, dy, xx, yy;
13300 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13303 /* diagonal direction: check alternative direction */
13308 xx = jx + (dx == 0 ? real_dx : 0);
13309 yy = jy + (dy == 0 ? real_dy : 0);
13311 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13315 =============================================================================
13317 -----------------------------------------------------------------------------
13318 x, y: field next to player (non-diagonal) to try to dig to
13319 real_dx, real_dy: direction as read from input device (can be diagonal)
13320 =============================================================================
13323 int DigField(struct PlayerInfo *player,
13324 int oldx, int oldy, int x, int y,
13325 int real_dx, int real_dy, int mode)
13327 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13328 boolean player_was_pushing = player->is_pushing;
13329 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13330 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13331 int jx = oldx, jy = oldy;
13332 int dx = x - jx, dy = y - jy;
13333 int nextx = x + dx, nexty = y + dy;
13334 int move_direction = (dx == -1 ? MV_LEFT :
13335 dx == +1 ? MV_RIGHT :
13337 dy == +1 ? MV_DOWN : MV_NONE);
13338 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13339 int dig_side = MV_DIR_OPPOSITE(move_direction);
13340 int old_element = Feld[jx][jy];
13341 #if USE_FIXED_DONT_RUN_INTO
13342 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13348 if (is_player) /* function can also be called by EL_PENGUIN */
13350 if (player->MovPos == 0)
13352 player->is_digging = FALSE;
13353 player->is_collecting = FALSE;
13356 if (player->MovPos == 0) /* last pushing move finished */
13357 player->is_pushing = FALSE;
13359 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13361 player->is_switching = FALSE;
13362 player->push_delay = -1;
13364 return MP_NO_ACTION;
13368 #if !USE_FIXED_DONT_RUN_INTO
13369 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13370 return MP_NO_ACTION;
13373 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13374 old_element = Back[jx][jy];
13376 /* in case of element dropped at player position, check background */
13377 else if (Back[jx][jy] != EL_EMPTY &&
13378 game.engine_version >= VERSION_IDENT(2,2,0,0))
13379 old_element = Back[jx][jy];
13381 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13382 return MP_NO_ACTION; /* field has no opening in this direction */
13384 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13385 return MP_NO_ACTION; /* field has no opening in this direction */
13387 #if USE_FIXED_DONT_RUN_INTO
13388 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13392 Feld[jx][jy] = player->artwork_element;
13393 InitMovingField(jx, jy, MV_DOWN);
13394 Store[jx][jy] = EL_ACID;
13395 ContinueMoving(jx, jy);
13396 BuryPlayer(player);
13398 return MP_DONT_RUN_INTO;
13402 #if USE_FIXED_DONT_RUN_INTO
13403 if (player_can_move && DONT_RUN_INTO(element))
13405 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13407 return MP_DONT_RUN_INTO;
13411 #if USE_FIXED_DONT_RUN_INTO
13412 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13413 return MP_NO_ACTION;
13416 #if !USE_FIXED_DONT_RUN_INTO
13417 element = Feld[x][y];
13420 collect_count = element_info[element].collect_count_initial;
13422 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13423 return MP_NO_ACTION;
13425 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13426 player_can_move = player_can_move_or_snap;
13428 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13429 game.engine_version >= VERSION_IDENT(2,2,0,0))
13431 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13432 player->index_bit, dig_side);
13433 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13434 player->index_bit, dig_side);
13436 if (element == EL_DC_LANDMINE)
13439 if (Feld[x][y] != element) /* field changed by snapping */
13442 return MP_NO_ACTION;
13445 #if USE_PLAYER_GRAVITY
13446 if (player->gravity && is_player && !player->is_auto_moving &&
13447 canFallDown(player) && move_direction != MV_DOWN &&
13448 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13449 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13451 if (game.gravity && is_player && !player->is_auto_moving &&
13452 canFallDown(player) && move_direction != MV_DOWN &&
13453 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13454 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13457 if (player_can_move &&
13458 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13460 int sound_element = SND_ELEMENT(element);
13461 int sound_action = ACTION_WALKING;
13463 if (IS_RND_GATE(element))
13465 if (!player->key[RND_GATE_NR(element)])
13466 return MP_NO_ACTION;
13468 else if (IS_RND_GATE_GRAY(element))
13470 if (!player->key[RND_GATE_GRAY_NR(element)])
13471 return MP_NO_ACTION;
13473 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13475 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13476 return MP_NO_ACTION;
13478 else if (element == EL_EXIT_OPEN ||
13479 element == EL_EM_EXIT_OPEN ||
13480 element == EL_STEEL_EXIT_OPEN ||
13481 element == EL_EM_STEEL_EXIT_OPEN ||
13482 element == EL_SP_EXIT_OPEN ||
13483 element == EL_SP_EXIT_OPENING)
13485 sound_action = ACTION_PASSING; /* player is passing exit */
13487 else if (element == EL_EMPTY)
13489 sound_action = ACTION_MOVING; /* nothing to walk on */
13492 /* play sound from background or player, whatever is available */
13493 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13494 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13496 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13498 else if (player_can_move &&
13499 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13501 if (!ACCESS_FROM(element, opposite_direction))
13502 return MP_NO_ACTION; /* field not accessible from this direction */
13504 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13505 return MP_NO_ACTION;
13507 if (IS_EM_GATE(element))
13509 if (!player->key[EM_GATE_NR(element)])
13510 return MP_NO_ACTION;
13512 else if (IS_EM_GATE_GRAY(element))
13514 if (!player->key[EM_GATE_GRAY_NR(element)])
13515 return MP_NO_ACTION;
13517 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13519 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13520 return MP_NO_ACTION;
13522 else if (IS_EMC_GATE(element))
13524 if (!player->key[EMC_GATE_NR(element)])
13525 return MP_NO_ACTION;
13527 else if (IS_EMC_GATE_GRAY(element))
13529 if (!player->key[EMC_GATE_GRAY_NR(element)])
13530 return MP_NO_ACTION;
13532 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13534 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13535 return MP_NO_ACTION;
13537 else if (element == EL_DC_GATE_WHITE ||
13538 element == EL_DC_GATE_WHITE_GRAY ||
13539 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13541 if (player->num_white_keys == 0)
13542 return MP_NO_ACTION;
13544 player->num_white_keys--;
13546 else if (IS_SP_PORT(element))
13548 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13549 element == EL_SP_GRAVITY_PORT_RIGHT ||
13550 element == EL_SP_GRAVITY_PORT_UP ||
13551 element == EL_SP_GRAVITY_PORT_DOWN)
13552 #if USE_PLAYER_GRAVITY
13553 player->gravity = !player->gravity;
13555 game.gravity = !game.gravity;
13557 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13558 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13559 element == EL_SP_GRAVITY_ON_PORT_UP ||
13560 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13561 #if USE_PLAYER_GRAVITY
13562 player->gravity = TRUE;
13564 game.gravity = TRUE;
13566 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13567 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13568 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13569 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13570 #if USE_PLAYER_GRAVITY
13571 player->gravity = FALSE;
13573 game.gravity = FALSE;
13577 /* automatically move to the next field with double speed */
13578 player->programmed_action = move_direction;
13580 if (player->move_delay_reset_counter == 0)
13582 player->move_delay_reset_counter = 2; /* two double speed steps */
13584 DOUBLE_PLAYER_SPEED(player);
13587 PlayLevelSoundAction(x, y, ACTION_PASSING);
13589 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13593 if (mode != DF_SNAP)
13595 GfxElement[x][y] = GFX_ELEMENT(element);
13596 player->is_digging = TRUE;
13599 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13601 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13602 player->index_bit, dig_side);
13604 if (mode == DF_SNAP)
13606 #if USE_NEW_SNAP_DELAY
13607 if (level.block_snap_field)
13608 setFieldForSnapping(x, y, element, move_direction);
13610 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13612 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13615 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13616 player->index_bit, dig_side);
13619 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13623 if (is_player && mode != DF_SNAP)
13625 GfxElement[x][y] = element;
13626 player->is_collecting = TRUE;
13629 if (element == EL_SPEED_PILL)
13631 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13633 else if (element == EL_EXTRA_TIME && level.time > 0)
13635 TimeLeft += level.extra_time;
13638 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13640 DisplayGameControlValues();
13642 DrawGameValue_Time(TimeLeft);
13645 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13647 player->shield_normal_time_left += level.shield_normal_time;
13648 if (element == EL_SHIELD_DEADLY)
13649 player->shield_deadly_time_left += level.shield_deadly_time;
13651 else if (element == EL_DYNAMITE ||
13652 element == EL_EM_DYNAMITE ||
13653 element == EL_SP_DISK_RED)
13655 if (player->inventory_size < MAX_INVENTORY_SIZE)
13656 player->inventory_element[player->inventory_size++] = element;
13658 DrawGameDoorValues();
13660 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13662 player->dynabomb_count++;
13663 player->dynabombs_left++;
13665 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13667 player->dynabomb_size++;
13669 else if (element == EL_DYNABOMB_INCREASE_POWER)
13671 player->dynabomb_xl = TRUE;
13673 else if (IS_KEY(element))
13675 player->key[KEY_NR(element)] = TRUE;
13677 DrawGameDoorValues();
13679 else if (element == EL_DC_KEY_WHITE)
13681 player->num_white_keys++;
13683 /* display white keys? */
13684 /* DrawGameDoorValues(); */
13686 else if (IS_ENVELOPE(element))
13688 player->show_envelope = element;
13690 else if (element == EL_EMC_LENSES)
13692 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13694 RedrawAllInvisibleElementsForLenses();
13696 else if (element == EL_EMC_MAGNIFIER)
13698 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13700 RedrawAllInvisibleElementsForMagnifier();
13702 else if (IS_DROPPABLE(element) ||
13703 IS_THROWABLE(element)) /* can be collected and dropped */
13707 if (collect_count == 0)
13708 player->inventory_infinite_element = element;
13710 for (i = 0; i < collect_count; i++)
13711 if (player->inventory_size < MAX_INVENTORY_SIZE)
13712 player->inventory_element[player->inventory_size++] = element;
13714 DrawGameDoorValues();
13716 else if (collect_count > 0)
13718 local_player->gems_still_needed -= collect_count;
13719 if (local_player->gems_still_needed < 0)
13720 local_player->gems_still_needed = 0;
13723 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13725 DisplayGameControlValues();
13727 DrawGameValue_Emeralds(local_player->gems_still_needed);
13731 RaiseScoreElement(element);
13732 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13735 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13736 player->index_bit, dig_side);
13738 if (mode == DF_SNAP)
13740 #if USE_NEW_SNAP_DELAY
13741 if (level.block_snap_field)
13742 setFieldForSnapping(x, y, element, move_direction);
13744 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13746 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13749 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13750 player->index_bit, dig_side);
13753 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13755 if (mode == DF_SNAP && element != EL_BD_ROCK)
13756 return MP_NO_ACTION;
13758 if (CAN_FALL(element) && dy)
13759 return MP_NO_ACTION;
13761 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13762 !(element == EL_SPRING && level.use_spring_bug))
13763 return MP_NO_ACTION;
13765 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13766 ((move_direction & MV_VERTICAL &&
13767 ((element_info[element].move_pattern & MV_LEFT &&
13768 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13769 (element_info[element].move_pattern & MV_RIGHT &&
13770 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13771 (move_direction & MV_HORIZONTAL &&
13772 ((element_info[element].move_pattern & MV_UP &&
13773 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13774 (element_info[element].move_pattern & MV_DOWN &&
13775 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13776 return MP_NO_ACTION;
13778 /* do not push elements already moving away faster than player */
13779 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13780 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13781 return MP_NO_ACTION;
13783 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13785 if (player->push_delay_value == -1 || !player_was_pushing)
13786 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13788 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13790 if (player->push_delay_value == -1)
13791 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13793 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13795 if (!player->is_pushing)
13796 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13799 player->is_pushing = TRUE;
13800 player->is_active = TRUE;
13802 if (!(IN_LEV_FIELD(nextx, nexty) &&
13803 (IS_FREE(nextx, nexty) ||
13804 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13805 IS_SB_ELEMENT(element)))))
13806 return MP_NO_ACTION;
13808 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13809 return MP_NO_ACTION;
13811 if (player->push_delay == -1) /* new pushing; restart delay */
13812 player->push_delay = 0;
13814 if (player->push_delay < player->push_delay_value &&
13815 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13816 element != EL_SPRING && element != EL_BALLOON)
13818 /* make sure that there is no move delay before next try to push */
13819 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13820 player->move_delay = 0;
13822 return MP_NO_ACTION;
13825 if (IS_SB_ELEMENT(element))
13827 if (element == EL_SOKOBAN_FIELD_FULL)
13829 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13830 local_player->sokobanfields_still_needed++;
13833 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13835 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13836 local_player->sokobanfields_still_needed--;
13839 Feld[x][y] = EL_SOKOBAN_OBJECT;
13841 if (Back[x][y] == Back[nextx][nexty])
13842 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13843 else if (Back[x][y] != 0)
13844 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13847 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13850 if (local_player->sokobanfields_still_needed == 0 &&
13851 game.emulation == EMU_SOKOBAN)
13853 PlayerWins(player);
13855 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13859 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13861 InitMovingField(x, y, move_direction);
13862 GfxAction[x][y] = ACTION_PUSHING;
13864 if (mode == DF_SNAP)
13865 ContinueMoving(x, y);
13867 MovPos[x][y] = (dx != 0 ? dx : dy);
13869 Pushed[x][y] = TRUE;
13870 Pushed[nextx][nexty] = TRUE;
13872 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13873 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13875 player->push_delay_value = -1; /* get new value later */
13877 /* check for element change _after_ element has been pushed */
13878 if (game.use_change_when_pushing_bug)
13880 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13881 player->index_bit, dig_side);
13882 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13883 player->index_bit, dig_side);
13886 else if (IS_SWITCHABLE(element))
13888 if (PLAYER_SWITCHING(player, x, y))
13890 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13891 player->index_bit, dig_side);
13896 player->is_switching = TRUE;
13897 player->switch_x = x;
13898 player->switch_y = y;
13900 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13902 if (element == EL_ROBOT_WHEEL)
13904 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13908 DrawLevelField(x, y);
13910 else if (element == EL_SP_TERMINAL)
13914 SCAN_PLAYFIELD(xx, yy)
13916 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13918 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13919 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13922 else if (IS_BELT_SWITCH(element))
13924 ToggleBeltSwitch(x, y);
13926 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13927 element == EL_SWITCHGATE_SWITCH_DOWN ||
13928 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13929 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13931 ToggleSwitchgateSwitch(x, y);
13933 else if (element == EL_LIGHT_SWITCH ||
13934 element == EL_LIGHT_SWITCH_ACTIVE)
13936 ToggleLightSwitch(x, y);
13938 else if (element == EL_TIMEGATE_SWITCH ||
13939 element == EL_DC_TIMEGATE_SWITCH)
13941 ActivateTimegateSwitch(x, y);
13943 else if (element == EL_BALLOON_SWITCH_LEFT ||
13944 element == EL_BALLOON_SWITCH_RIGHT ||
13945 element == EL_BALLOON_SWITCH_UP ||
13946 element == EL_BALLOON_SWITCH_DOWN ||
13947 element == EL_BALLOON_SWITCH_NONE ||
13948 element == EL_BALLOON_SWITCH_ANY)
13950 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13951 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13952 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13953 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13954 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13957 else if (element == EL_LAMP)
13959 Feld[x][y] = EL_LAMP_ACTIVE;
13960 local_player->lights_still_needed--;
13962 ResetGfxAnimation(x, y);
13963 DrawLevelField(x, y);
13965 else if (element == EL_TIME_ORB_FULL)
13967 Feld[x][y] = EL_TIME_ORB_EMPTY;
13969 if (level.time > 0 || level.use_time_orb_bug)
13971 TimeLeft += level.time_orb_time;
13974 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13976 DisplayGameControlValues();
13978 DrawGameValue_Time(TimeLeft);
13982 ResetGfxAnimation(x, y);
13983 DrawLevelField(x, y);
13985 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13986 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13990 game.ball_state = !game.ball_state;
13992 SCAN_PLAYFIELD(xx, yy)
13994 int e = Feld[xx][yy];
13996 if (game.ball_state)
13998 if (e == EL_EMC_MAGIC_BALL)
13999 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14000 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14001 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14005 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14006 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14007 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14008 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14013 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14014 player->index_bit, dig_side);
14016 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14017 player->index_bit, dig_side);
14019 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14020 player->index_bit, dig_side);
14026 if (!PLAYER_SWITCHING(player, x, y))
14028 player->is_switching = TRUE;
14029 player->switch_x = x;
14030 player->switch_y = y;
14032 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14033 player->index_bit, dig_side);
14034 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14035 player->index_bit, dig_side);
14037 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14038 player->index_bit, dig_side);
14039 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14040 player->index_bit, dig_side);
14043 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14044 player->index_bit, dig_side);
14045 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14046 player->index_bit, dig_side);
14048 return MP_NO_ACTION;
14051 player->push_delay = -1;
14053 if (is_player) /* function can also be called by EL_PENGUIN */
14055 if (Feld[x][y] != element) /* really digged/collected something */
14057 player->is_collecting = !player->is_digging;
14058 player->is_active = TRUE;
14065 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14067 int jx = player->jx, jy = player->jy;
14068 int x = jx + dx, y = jy + dy;
14069 int snap_direction = (dx == -1 ? MV_LEFT :
14070 dx == +1 ? MV_RIGHT :
14072 dy == +1 ? MV_DOWN : MV_NONE);
14073 boolean can_continue_snapping = (level.continuous_snapping &&
14074 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14076 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14079 if (!player->active || !IN_LEV_FIELD(x, y))
14087 if (player->MovPos == 0)
14088 player->is_pushing = FALSE;
14090 player->is_snapping = FALSE;
14092 if (player->MovPos == 0)
14094 player->is_moving = FALSE;
14095 player->is_digging = FALSE;
14096 player->is_collecting = FALSE;
14102 #if USE_NEW_CONTINUOUS_SNAPPING
14103 /* prevent snapping with already pressed snap key when not allowed */
14104 if (player->is_snapping && !can_continue_snapping)
14107 if (player->is_snapping)
14111 player->MovDir = snap_direction;
14113 if (player->MovPos == 0)
14115 player->is_moving = FALSE;
14116 player->is_digging = FALSE;
14117 player->is_collecting = FALSE;
14120 player->is_dropping = FALSE;
14121 player->is_dropping_pressed = FALSE;
14122 player->drop_pressed_delay = 0;
14124 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14127 player->is_snapping = TRUE;
14128 player->is_active = TRUE;
14130 if (player->MovPos == 0)
14132 player->is_moving = FALSE;
14133 player->is_digging = FALSE;
14134 player->is_collecting = FALSE;
14137 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14138 DrawLevelField(player->last_jx, player->last_jy);
14140 DrawLevelField(x, y);
14145 boolean DropElement(struct PlayerInfo *player)
14147 int old_element, new_element;
14148 int dropx = player->jx, dropy = player->jy;
14149 int drop_direction = player->MovDir;
14150 int drop_side = drop_direction;
14152 int drop_element = get_next_drop_element(player);
14154 int drop_element = (player->inventory_size > 0 ?
14155 player->inventory_element[player->inventory_size - 1] :
14156 player->inventory_infinite_element != EL_UNDEFINED ?
14157 player->inventory_infinite_element :
14158 player->dynabombs_left > 0 ?
14159 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14163 player->is_dropping_pressed = TRUE;
14165 /* do not drop an element on top of another element; when holding drop key
14166 pressed without moving, dropped element must move away before the next
14167 element can be dropped (this is especially important if the next element
14168 is dynamite, which can be placed on background for historical reasons) */
14169 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14172 if (IS_THROWABLE(drop_element))
14174 dropx += GET_DX_FROM_DIR(drop_direction);
14175 dropy += GET_DY_FROM_DIR(drop_direction);
14177 if (!IN_LEV_FIELD(dropx, dropy))
14181 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14182 new_element = drop_element; /* default: no change when dropping */
14184 /* check if player is active, not moving and ready to drop */
14185 if (!player->active || player->MovPos || player->drop_delay > 0)
14188 /* check if player has anything that can be dropped */
14189 if (new_element == EL_UNDEFINED)
14192 /* check if drop key was pressed long enough for EM style dynamite */
14193 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14196 /* check if anything can be dropped at the current position */
14197 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14200 /* collected custom elements can only be dropped on empty fields */
14201 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14204 if (old_element != EL_EMPTY)
14205 Back[dropx][dropy] = old_element; /* store old element on this field */
14207 ResetGfxAnimation(dropx, dropy);
14208 ResetRandomAnimationValue(dropx, dropy);
14210 if (player->inventory_size > 0 ||
14211 player->inventory_infinite_element != EL_UNDEFINED)
14213 if (player->inventory_size > 0)
14215 player->inventory_size--;
14217 DrawGameDoorValues();
14219 if (new_element == EL_DYNAMITE)
14220 new_element = EL_DYNAMITE_ACTIVE;
14221 else if (new_element == EL_EM_DYNAMITE)
14222 new_element = EL_EM_DYNAMITE_ACTIVE;
14223 else if (new_element == EL_SP_DISK_RED)
14224 new_element = EL_SP_DISK_RED_ACTIVE;
14227 Feld[dropx][dropy] = new_element;
14229 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14230 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14231 el2img(Feld[dropx][dropy]), 0);
14233 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14235 /* needed if previous element just changed to "empty" in the last frame */
14236 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14238 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14239 player->index_bit, drop_side);
14240 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14242 player->index_bit, drop_side);
14244 TestIfElementTouchesCustomElement(dropx, dropy);
14246 else /* player is dropping a dyna bomb */
14248 player->dynabombs_left--;
14250 Feld[dropx][dropy] = new_element;
14252 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14253 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14254 el2img(Feld[dropx][dropy]), 0);
14256 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14259 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14260 InitField_WithBug1(dropx, dropy, FALSE);
14262 new_element = Feld[dropx][dropy]; /* element might have changed */
14264 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14265 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14267 int move_direction, nextx, nexty;
14269 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14270 MovDir[dropx][dropy] = drop_direction;
14272 move_direction = MovDir[dropx][dropy];
14273 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14274 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14276 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14278 #if USE_FIX_IMPACT_COLLISION
14279 /* do not cause impact style collision by dropping elements that can fall */
14280 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14282 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14286 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14287 player->is_dropping = TRUE;
14289 player->drop_pressed_delay = 0;
14290 player->is_dropping_pressed = FALSE;
14292 player->drop_x = dropx;
14293 player->drop_y = dropy;
14298 /* ------------------------------------------------------------------------- */
14299 /* game sound playing functions */
14300 /* ------------------------------------------------------------------------- */
14302 static int *loop_sound_frame = NULL;
14303 static int *loop_sound_volume = NULL;
14305 void InitPlayLevelSound()
14307 int num_sounds = getSoundListSize();
14309 checked_free(loop_sound_frame);
14310 checked_free(loop_sound_volume);
14312 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14313 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14316 static void PlayLevelSound(int x, int y, int nr)
14318 int sx = SCREENX(x), sy = SCREENY(y);
14319 int volume, stereo_position;
14320 int max_distance = 8;
14321 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14323 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14324 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14327 if (!IN_LEV_FIELD(x, y) ||
14328 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14329 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14332 volume = SOUND_MAX_VOLUME;
14334 if (!IN_SCR_FIELD(sx, sy))
14336 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14337 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14339 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14342 stereo_position = (SOUND_MAX_LEFT +
14343 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14344 (SCR_FIELDX + 2 * max_distance));
14346 if (IS_LOOP_SOUND(nr))
14348 /* This assures that quieter loop sounds do not overwrite louder ones,
14349 while restarting sound volume comparison with each new game frame. */
14351 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14354 loop_sound_volume[nr] = volume;
14355 loop_sound_frame[nr] = FrameCounter;
14358 PlaySoundExt(nr, volume, stereo_position, type);
14361 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14363 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14364 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14365 y < LEVELY(BY1) ? LEVELY(BY1) :
14366 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14370 static void PlayLevelSoundAction(int x, int y, int action)
14372 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14375 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14377 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14379 if (sound_effect != SND_UNDEFINED)
14380 PlayLevelSound(x, y, sound_effect);
14383 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14386 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14388 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14389 PlayLevelSound(x, y, sound_effect);
14392 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14394 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14396 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14397 PlayLevelSound(x, y, sound_effect);
14400 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14402 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14404 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14405 StopSound(sound_effect);
14408 static void PlayLevelMusic()
14410 if (levelset.music[level_nr] != MUS_UNDEFINED)
14411 PlayMusic(levelset.music[level_nr]); /* from config file */
14413 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14416 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14418 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14419 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14420 int x = xx - 1 - offset;
14421 int y = yy - 1 - offset;
14426 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14430 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14434 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14438 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14442 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14446 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14450 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14453 case SAMPLE_android_clone:
14454 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14457 case SAMPLE_android_move:
14458 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14461 case SAMPLE_spring:
14462 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14466 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14470 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14473 case SAMPLE_eater_eat:
14474 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14478 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14481 case SAMPLE_collect:
14482 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14485 case SAMPLE_diamond:
14486 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14489 case SAMPLE_squash:
14490 /* !!! CHECK THIS !!! */
14492 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14494 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14498 case SAMPLE_wonderfall:
14499 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14503 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14507 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14511 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14515 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14519 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14523 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14526 case SAMPLE_wonder:
14527 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14531 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14534 case SAMPLE_exit_open:
14535 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14538 case SAMPLE_exit_leave:
14539 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14542 case SAMPLE_dynamite:
14543 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14547 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14551 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14555 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14559 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14563 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14567 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14571 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14577 void ChangeTime(int value)
14579 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14583 /* EMC game engine uses value from time counter of RND game engine */
14584 level.native_em_level->lev->time = *time;
14586 DrawGameValue_Time(*time);
14589 void RaiseScore(int value)
14591 /* EMC game engine and RND game engine have separate score counters */
14592 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14593 &level.native_em_level->lev->score : &local_player->score);
14597 DrawGameValue_Score(*score);
14601 void RaiseScore(int value)
14603 local_player->score += value;
14606 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14608 DisplayGameControlValues();
14610 DrawGameValue_Score(local_player->score);
14614 void RaiseScoreElement(int element)
14619 case EL_BD_DIAMOND:
14620 case EL_EMERALD_YELLOW:
14621 case EL_EMERALD_RED:
14622 case EL_EMERALD_PURPLE:
14623 case EL_SP_INFOTRON:
14624 RaiseScore(level.score[SC_EMERALD]);
14627 RaiseScore(level.score[SC_DIAMOND]);
14630 RaiseScore(level.score[SC_CRYSTAL]);
14633 RaiseScore(level.score[SC_PEARL]);
14636 case EL_BD_BUTTERFLY:
14637 case EL_SP_ELECTRON:
14638 RaiseScore(level.score[SC_BUG]);
14641 case EL_BD_FIREFLY:
14642 case EL_SP_SNIKSNAK:
14643 RaiseScore(level.score[SC_SPACESHIP]);
14646 case EL_DARK_YAMYAM:
14647 RaiseScore(level.score[SC_YAMYAM]);
14650 RaiseScore(level.score[SC_ROBOT]);
14653 RaiseScore(level.score[SC_PACMAN]);
14656 RaiseScore(level.score[SC_NUT]);
14659 case EL_EM_DYNAMITE:
14660 case EL_SP_DISK_RED:
14661 case EL_DYNABOMB_INCREASE_NUMBER:
14662 case EL_DYNABOMB_INCREASE_SIZE:
14663 case EL_DYNABOMB_INCREASE_POWER:
14664 RaiseScore(level.score[SC_DYNAMITE]);
14666 case EL_SHIELD_NORMAL:
14667 case EL_SHIELD_DEADLY:
14668 RaiseScore(level.score[SC_SHIELD]);
14670 case EL_EXTRA_TIME:
14671 RaiseScore(level.extra_time_score);
14685 case EL_DC_KEY_WHITE:
14686 RaiseScore(level.score[SC_KEY]);
14689 RaiseScore(element_info[element].collect_score);
14694 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14696 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14698 #if defined(NETWORK_AVALIABLE)
14699 if (options.network)
14700 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14709 FadeSkipNextFadeIn();
14711 fading = fading_none;
14715 OpenDoor(DOOR_CLOSE_1);
14718 game_status = GAME_MODE_MAIN;
14721 DrawAndFadeInMainMenu(REDRAW_FIELD);
14729 FadeOut(REDRAW_FIELD);
14732 game_status = GAME_MODE_MAIN;
14734 DrawAndFadeInMainMenu(REDRAW_FIELD);
14738 else /* continue playing the game */
14740 if (tape.playing && tape.deactivate_display)
14741 TapeDeactivateDisplayOff(TRUE);
14743 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14745 if (tape.playing && tape.deactivate_display)
14746 TapeDeactivateDisplayOn();
14750 void RequestQuitGame(boolean ask_if_really_quit)
14752 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14753 boolean skip_request = AllPlayersGone || quick_quit;
14755 RequestQuitGameExt(skip_request, quick_quit,
14756 "Do you really want to quit the game ?");
14760 /* ------------------------------------------------------------------------- */
14761 /* random generator functions */
14762 /* ------------------------------------------------------------------------- */
14764 unsigned int InitEngineRandom_RND(long seed)
14766 game.num_random_calls = 0;
14769 unsigned int rnd_seed = InitEngineRandom(seed);
14771 printf("::: START RND: %d\n", rnd_seed);
14776 return InitEngineRandom(seed);
14782 unsigned int RND(int max)
14786 game.num_random_calls++;
14788 return GetEngineRandom(max);
14795 /* ------------------------------------------------------------------------- */
14796 /* game engine snapshot handling functions */
14797 /* ------------------------------------------------------------------------- */
14799 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14801 struct EngineSnapshotInfo
14803 /* runtime values for custom element collect score */
14804 int collect_score[NUM_CUSTOM_ELEMENTS];
14806 /* runtime values for group element choice position */
14807 int choice_pos[NUM_GROUP_ELEMENTS];
14809 /* runtime values for belt position animations */
14810 int belt_graphic[4 * NUM_BELT_PARTS];
14811 int belt_anim_mode[4 * NUM_BELT_PARTS];
14814 struct EngineSnapshotNodeInfo
14821 static struct EngineSnapshotInfo engine_snapshot_rnd;
14822 static ListNode *engine_snapshot_list = NULL;
14823 static char *snapshot_level_identifier = NULL;
14824 static int snapshot_level_nr = -1;
14826 void FreeEngineSnapshot()
14828 while (engine_snapshot_list != NULL)
14829 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14832 setString(&snapshot_level_identifier, NULL);
14833 snapshot_level_nr = -1;
14836 static void SaveEngineSnapshotValues_RND()
14838 static int belt_base_active_element[4] =
14840 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14841 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14842 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14843 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14847 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14849 int element = EL_CUSTOM_START + i;
14851 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14854 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14856 int element = EL_GROUP_START + i;
14858 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14861 for (i = 0; i < 4; i++)
14863 for (j = 0; j < NUM_BELT_PARTS; j++)
14865 int element = belt_base_active_element[i] + j;
14866 int graphic = el2img(element);
14867 int anim_mode = graphic_info[graphic].anim_mode;
14869 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14870 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14875 static void LoadEngineSnapshotValues_RND()
14877 unsigned long num_random_calls = game.num_random_calls;
14880 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14882 int element = EL_CUSTOM_START + i;
14884 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14887 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14889 int element = EL_GROUP_START + i;
14891 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14894 for (i = 0; i < 4; i++)
14896 for (j = 0; j < NUM_BELT_PARTS; j++)
14898 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14899 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14901 graphic_info[graphic].anim_mode = anim_mode;
14905 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14907 InitRND(tape.random_seed);
14908 for (i = 0; i < num_random_calls; i++)
14912 if (game.num_random_calls != num_random_calls)
14914 Error(ERR_INFO, "number of random calls out of sync");
14915 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14916 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14917 Error(ERR_EXIT, "this should not happen -- please debug");
14921 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14923 struct EngineSnapshotNodeInfo *bi =
14924 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14926 bi->buffer_orig = buffer;
14927 bi->buffer_copy = checked_malloc(size);
14930 memcpy(bi->buffer_copy, buffer, size);
14932 addNodeToList(&engine_snapshot_list, NULL, bi);
14935 void SaveEngineSnapshot()
14937 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14939 if (level_editor_test_game) /* do not save snapshots from editor */
14942 /* copy some special values to a structure better suited for the snapshot */
14944 SaveEngineSnapshotValues_RND();
14945 SaveEngineSnapshotValues_EM();
14947 /* save values stored in special snapshot structure */
14949 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14950 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14952 /* save further RND engine values */
14954 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14955 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14956 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14958 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14959 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14960 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14961 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14963 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14964 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14965 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14966 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14967 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14969 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14970 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14971 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14973 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14975 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14977 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14978 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14980 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14981 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14982 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14983 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14984 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14985 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14986 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14987 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14988 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14989 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14990 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14991 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14992 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14993 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14994 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14995 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14996 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14997 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14999 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15000 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15002 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15003 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15004 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15006 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15007 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15009 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15010 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15011 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15012 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15013 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15015 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15016 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15018 /* save level identification information */
15020 setString(&snapshot_level_identifier, leveldir_current->identifier);
15021 snapshot_level_nr = level_nr;
15024 ListNode *node = engine_snapshot_list;
15027 while (node != NULL)
15029 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15034 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15038 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15040 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15043 void LoadEngineSnapshot()
15045 ListNode *node = engine_snapshot_list;
15047 if (engine_snapshot_list == NULL)
15050 while (node != NULL)
15052 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15057 /* restore special values from snapshot structure */
15059 LoadEngineSnapshotValues_RND();
15060 LoadEngineSnapshotValues_EM();
15063 boolean CheckEngineSnapshot()
15065 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15066 snapshot_level_nr == level_nr);
15070 /* ---------- new game button stuff ---------------------------------------- */
15072 /* graphic position values for game buttons */
15073 #define GAME_BUTTON_XSIZE 30
15074 #define GAME_BUTTON_YSIZE 30
15075 #define GAME_BUTTON_XPOS 5
15076 #define GAME_BUTTON_YPOS 215
15077 #define SOUND_BUTTON_XPOS 5
15078 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15080 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15081 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15082 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15083 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15084 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15085 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15093 } gamebutton_info[NUM_GAME_BUTTONS] =
15097 &game.button.stop.x, &game.button.stop.y,
15098 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15103 &game.button.pause.x, &game.button.pause.y,
15104 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15105 GAME_CTRL_ID_PAUSE,
15109 &game.button.play.x, &game.button.play.y,
15110 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15115 &game.button.sound_music.x, &game.button.sound_music.y,
15116 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15117 SOUND_CTRL_ID_MUSIC,
15118 "background music on/off"
15121 &game.button.sound_loops.x, &game.button.sound_loops.y,
15122 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15123 SOUND_CTRL_ID_LOOPS,
15124 "sound loops on/off"
15127 &game.button.sound_simple.x,&game.button.sound_simple.y,
15128 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15129 SOUND_CTRL_ID_SIMPLE,
15130 "normal sounds on/off"
15134 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15139 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15140 GAME_CTRL_ID_PAUSE,
15144 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15149 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15150 SOUND_CTRL_ID_MUSIC,
15151 "background music on/off"
15154 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15155 SOUND_CTRL_ID_LOOPS,
15156 "sound loops on/off"
15159 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15160 SOUND_CTRL_ID_SIMPLE,
15161 "normal sounds on/off"
15166 void CreateGameButtons()
15170 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15172 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15173 struct GadgetInfo *gi;
15176 unsigned long event_mask;
15178 int gd_xoffset, gd_yoffset;
15179 int gd_x1, gd_x2, gd_y1, gd_y2;
15182 x = DX + *gamebutton_info[i].x;
15183 y = DY + *gamebutton_info[i].y;
15184 gd_xoffset = gamebutton_info[i].gd_x;
15185 gd_yoffset = gamebutton_info[i].gd_y;
15186 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15187 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15189 if (id == GAME_CTRL_ID_STOP ||
15190 id == GAME_CTRL_ID_PAUSE ||
15191 id == GAME_CTRL_ID_PLAY)
15193 button_type = GD_TYPE_NORMAL_BUTTON;
15195 event_mask = GD_EVENT_RELEASED;
15196 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15197 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15201 button_type = GD_TYPE_CHECK_BUTTON;
15203 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15204 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15205 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15206 event_mask = GD_EVENT_PRESSED;
15207 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15208 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15211 gi = CreateGadget(GDI_CUSTOM_ID, id,
15212 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15217 GDI_X, DX + gd_xoffset,
15218 GDI_Y, DY + gd_yoffset,
15220 GDI_WIDTH, GAME_BUTTON_XSIZE,
15221 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15222 GDI_TYPE, button_type,
15223 GDI_STATE, GD_BUTTON_UNPRESSED,
15224 GDI_CHECKED, checked,
15225 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15226 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15227 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15228 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15229 GDI_EVENT_MASK, event_mask,
15230 GDI_CALLBACK_ACTION, HandleGameButtons,
15234 Error(ERR_EXIT, "cannot create gadget");
15236 game_gadget[id] = gi;
15240 void FreeGameButtons()
15244 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15245 FreeGadget(game_gadget[i]);
15248 static void MapGameButtons()
15252 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15253 MapGadget(game_gadget[i]);
15256 void UnmapGameButtons()
15260 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15261 UnmapGadget(game_gadget[i]);
15264 static void HandleGameButtons(struct GadgetInfo *gi)
15266 int id = gi->custom_id;
15268 if (game_status != GAME_MODE_PLAYING)
15273 case GAME_CTRL_ID_STOP:
15277 RequestQuitGame(TRUE);
15280 case GAME_CTRL_ID_PAUSE:
15281 if (options.network)
15283 #if defined(NETWORK_AVALIABLE)
15285 SendToServer_ContinuePlaying();
15287 SendToServer_PausePlaying();
15291 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15294 case GAME_CTRL_ID_PLAY:
15297 #if defined(NETWORK_AVALIABLE)
15298 if (options.network)
15299 SendToServer_ContinuePlaying();
15303 tape.pausing = FALSE;
15304 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15309 case SOUND_CTRL_ID_MUSIC:
15310 if (setup.sound_music)
15312 setup.sound_music = FALSE;
15315 else if (audio.music_available)
15317 setup.sound = setup.sound_music = TRUE;
15319 SetAudioMode(setup.sound);
15325 case SOUND_CTRL_ID_LOOPS:
15326 if (setup.sound_loops)
15327 setup.sound_loops = FALSE;
15328 else if (audio.loops_available)
15330 setup.sound = setup.sound_loops = TRUE;
15331 SetAudioMode(setup.sound);
15335 case SOUND_CTRL_ID_SIMPLE:
15336 if (setup.sound_simple)
15337 setup.sound_simple = FALSE;
15338 else if (audio.sound_available)
15340 setup.sound = setup.sound_simple = TRUE;
15341 SetAudioMode(setup.sound);