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 game_status = GAME_MODE_PSEUDO_PANEL;
2032 for (i = 0; game_controls[i].nr != -1; i++)
2034 int nr = game_controls[i].nr;
2035 int type = game_controls[i].type;
2036 struct TextPosInfo *pos = game_controls[i].pos;
2037 int value = game_control_value[nr];
2038 int last_value = last_game_control_value[nr];
2039 int size = pos->size;
2040 int font = pos->font;
2042 if (value == last_value)
2045 last_game_control_value[nr] = value;
2048 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2051 if (PANEL_DEACTIVATED(pos))
2054 if (type == TYPE_INTEGER)
2056 if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
2058 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2060 if (use_dynamic_size) /* use dynamic number of digits */
2062 int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
2063 int size1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
2064 int size2 = size1 + 1;
2065 int font1 = pos->font;
2066 int font2 = pos->font_alt;
2068 size = (value < value_change ? size1 : size2);
2069 font = (value < value_change ? font1 : font2);
2071 /* clear background if value just changed its size (dynamic digits) */
2072 if ((last_value < value_change) != (value < value_change))
2074 int width1 = size1 * getFontWidth(font1);
2075 int width2 = size2 * getFontWidth(font2);
2076 int max_width = MAX(width1, width2);
2077 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2079 pos->width = max_width;
2081 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2082 max_width, max_height);
2086 pos->width = size * getFontWidth(font);
2089 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2091 else if (type == TYPE_ELEMENT)
2093 int dst_x = PANEL_XPOS(pos);
2094 int dst_y = PANEL_YPOS(pos);
2096 if (value == EL_UNDEFINED || value == EL_EMPTY)
2098 int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2099 int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2101 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2102 size, size, dst_x, dst_y);
2106 int graphic = el2panelimg(value);
2108 DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, size);
2111 else if (type == TYPE_STRING)
2113 char *s = (nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name :
2114 nr == GAME_CONTROL_LEVEL_NAME ? level.name :
2115 nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2119 char *s_cut = getStringCopyN(s, size);
2121 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2127 redraw_mask |= REDRAW_DOOR_1;
2130 game_status = GAME_MODE_PLAYING;
2133 void DrawGameValue_Emeralds(int value)
2135 struct TextPosInfo *pos = &game.panel.gems;
2137 int font_nr = pos->font;
2139 int font_nr = FONT_TEXT_2;
2141 int font_width = getFontWidth(font_nr);
2142 int chars = pos->size;
2145 return; /* !!! USE NEW STUFF !!! */
2148 if (PANEL_DEACTIVATED(pos))
2151 pos->width = chars * font_width;
2153 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2156 void DrawGameValue_Dynamite(int value)
2158 struct TextPosInfo *pos = &game.panel.inventory_count;
2160 int font_nr = pos->font;
2162 int font_nr = FONT_TEXT_2;
2164 int font_width = getFontWidth(font_nr);
2165 int chars = pos->size;
2168 return; /* !!! USE NEW STUFF !!! */
2171 if (PANEL_DEACTIVATED(pos))
2174 pos->width = chars * font_width;
2176 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2179 void DrawGameValue_Score(int value)
2181 struct TextPosInfo *pos = &game.panel.score;
2183 int font_nr = pos->font;
2185 int font_nr = FONT_TEXT_2;
2187 int font_width = getFontWidth(font_nr);
2188 int chars = pos->size;
2191 return; /* !!! USE NEW STUFF !!! */
2194 if (PANEL_DEACTIVATED(pos))
2197 pos->width = chars * font_width;
2199 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2202 void DrawGameValue_Time(int value)
2204 struct TextPosInfo *pos = &game.panel.time;
2205 static int last_value = -1;
2208 int chars = pos->size;
2210 int font1_nr = pos->font;
2211 int font2_nr = pos->font_alt;
2213 int font1_nr = FONT_TEXT_2;
2214 int font2_nr = FONT_TEXT_1;
2216 int font_nr = font1_nr;
2217 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2220 return; /* !!! USE NEW STUFF !!! */
2223 if (PANEL_DEACTIVATED(pos))
2226 if (use_dynamic_chars) /* use dynamic number of chars */
2228 chars = (value < 1000 ? chars1 : chars2);
2229 font_nr = (value < 1000 ? font1_nr : font2_nr);
2232 /* clear background if value just changed its size (dynamic chars only) */
2233 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2235 int width1 = chars1 * getFontWidth(font1_nr);
2236 int width2 = chars2 * getFontWidth(font2_nr);
2237 int max_width = MAX(width1, width2);
2238 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2240 pos->width = max_width;
2242 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2243 max_width, max_height);
2246 pos->width = chars * getFontWidth(font_nr);
2248 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2253 void DrawGameValue_Level(int value)
2255 struct TextPosInfo *pos = &game.panel.level_number;
2258 int chars = pos->size;
2260 int font1_nr = pos->font;
2261 int font2_nr = pos->font_alt;
2263 int font1_nr = FONT_TEXT_2;
2264 int font2_nr = FONT_TEXT_1;
2266 int font_nr = font1_nr;
2267 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2270 return; /* !!! USE NEW STUFF !!! */
2273 if (PANEL_DEACTIVATED(pos))
2276 if (use_dynamic_chars) /* use dynamic number of chars */
2278 chars = (level_nr < 100 ? chars1 : chars2);
2279 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2282 pos->width = chars * getFontWidth(font_nr);
2284 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2287 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2290 struct TextPosInfo *pos = &game.panel.keys;
2293 int base_key_graphic = EL_KEY_1;
2298 return; /* !!! USE NEW STUFF !!! */
2302 if (PANEL_DEACTIVATED(pos))
2307 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2308 base_key_graphic = EL_EM_KEY_1;
2312 pos->width = 4 * MINI_TILEX;
2316 for (i = 0; i < MAX_NUM_KEYS; i++)
2318 /* currently only 4 of 8 possible keys are displayed */
2319 for (i = 0; i < STD_NUM_KEYS; i++)
2323 struct TextPosInfo *pos = &game.panel.key[i];
2325 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2326 int src_y = DOOR_GFX_PAGEY1 + 123;
2328 int dst_x = PANEL_XPOS(pos);
2329 int dst_y = PANEL_YPOS(pos);
2331 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2332 int dst_y = PANEL_YPOS(pos);
2336 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2337 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2339 int graphic = el2edimg(element);
2343 if (PANEL_DEACTIVATED(pos))
2348 /* masked blit with tiles from half-size scaled bitmap does not work yet
2349 (no mask bitmap created for these sizes after loading and scaling) --
2350 solution: load without creating mask, scale, then create final mask */
2352 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2353 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2358 int graphic = el2edimg(base_key_graphic + i);
2363 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2365 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2366 dst_x - src_x, dst_y - src_y);
2367 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2373 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2375 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2376 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2379 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2381 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2382 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2390 void DrawGameValue_Emeralds(int value)
2392 int font_nr = FONT_TEXT_2;
2393 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2395 if (PANEL_DEACTIVATED(game.panel.gems))
2398 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2401 void DrawGameValue_Dynamite(int value)
2403 int font_nr = FONT_TEXT_2;
2404 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2406 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2409 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2412 void DrawGameValue_Score(int value)
2414 int font_nr = FONT_TEXT_2;
2415 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2417 if (PANEL_DEACTIVATED(game.panel.score))
2420 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2423 void DrawGameValue_Time(int value)
2425 int font1_nr = FONT_TEXT_2;
2427 int font2_nr = FONT_TEXT_1;
2429 int font2_nr = FONT_LEVEL_NUMBER;
2431 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2432 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2434 if (PANEL_DEACTIVATED(game.panel.time))
2437 /* clear background if value just changed its size */
2438 if (value == 999 || value == 1000)
2439 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2442 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2444 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2447 void DrawGameValue_Level(int value)
2449 int font1_nr = FONT_TEXT_2;
2451 int font2_nr = FONT_TEXT_1;
2453 int font2_nr = FONT_LEVEL_NUMBER;
2456 if (PANEL_DEACTIVATED(game.panel.level))
2460 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2462 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2465 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2467 int base_key_graphic = EL_KEY_1;
2470 if (PANEL_DEACTIVATED(game.panel.keys))
2473 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2474 base_key_graphic = EL_EM_KEY_1;
2476 /* currently only 4 of 8 possible keys are displayed */
2477 for (i = 0; i < STD_NUM_KEYS; i++)
2479 int x = XX_KEYS + i * MINI_TILEX;
2483 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2485 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2486 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2492 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2495 int key[MAX_NUM_KEYS];
2498 /* prevent EM engine from updating time/score values parallel to GameWon() */
2499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2500 local_player->LevelSolved)
2503 for (i = 0; i < MAX_NUM_KEYS; i++)
2504 key[i] = key_bits & (1 << i);
2506 DrawGameValue_Level(level_nr);
2508 DrawGameValue_Emeralds(emeralds);
2509 DrawGameValue_Dynamite(dynamite);
2510 DrawGameValue_Score(score);
2511 DrawGameValue_Time(time);
2513 DrawGameValue_Keys(key);
2516 void DrawGameDoorValues()
2518 UpdateGameControlValues();
2519 DisplayGameControlValues();
2522 void DrawGameDoorValues_OLD()
2524 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2525 int dynamite_value = 0;
2526 int score_value = (local_player->LevelSolved ? local_player->score_final :
2527 local_player->score);
2528 int gems_value = local_player->gems_still_needed;
2532 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2534 DrawGameDoorValues_EM();
2539 if (game.centered_player_nr == -1)
2541 for (i = 0; i < MAX_PLAYERS; i++)
2543 for (j = 0; j < MAX_NUM_KEYS; j++)
2544 if (stored_player[i].key[j])
2545 key_bits |= (1 << j);
2547 dynamite_value += stored_player[i].inventory_size;
2552 int player_nr = game.centered_player_nr;
2554 for (i = 0; i < MAX_NUM_KEYS; i++)
2555 if (stored_player[player_nr].key[i])
2556 key_bits |= (1 << i);
2558 dynamite_value = stored_player[player_nr].inventory_size;
2561 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2567 =============================================================================
2569 -----------------------------------------------------------------------------
2570 initialize game engine due to level / tape version number
2571 =============================================================================
2574 static void InitGameEngine()
2576 int i, j, k, l, x, y;
2578 /* set game engine from tape file when re-playing, else from level file */
2579 game.engine_version = (tape.playing ? tape.engine_version :
2580 level.game_version);
2582 /* ---------------------------------------------------------------------- */
2583 /* set flags for bugs and changes according to active game engine version */
2584 /* ---------------------------------------------------------------------- */
2587 Summary of bugfix/change:
2588 Fixed handling for custom elements that change when pushed by the player.
2590 Fixed/changed in version:
2594 Before 3.1.0, custom elements that "change when pushing" changed directly
2595 after the player started pushing them (until then handled in "DigField()").
2596 Since 3.1.0, these custom elements are not changed until the "pushing"
2597 move of the element is finished (now handled in "ContinueMoving()").
2599 Affected levels/tapes:
2600 The first condition is generally needed for all levels/tapes before version
2601 3.1.0, which might use the old behaviour before it was changed; known tapes
2602 that are affected are some tapes from the level set "Walpurgis Gardens" by
2604 The second condition is an exception from the above case and is needed for
2605 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2606 above (including some development versions of 3.1.0), but before it was
2607 known that this change would break tapes like the above and was fixed in
2608 3.1.1, so that the changed behaviour was active although the engine version
2609 while recording maybe was before 3.1.0. There is at least one tape that is
2610 affected by this exception, which is the tape for the one-level set "Bug
2611 Machine" by Juergen Bonhagen.
2614 game.use_change_when_pushing_bug =
2615 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2617 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2618 tape.game_version < VERSION_IDENT(3,1,1,0)));
2621 Summary of bugfix/change:
2622 Fixed handling for blocking the field the player leaves when moving.
2624 Fixed/changed in version:
2628 Before 3.1.1, when "block last field when moving" was enabled, the field
2629 the player is leaving when moving was blocked for the time of the move,
2630 and was directly unblocked afterwards. This resulted in the last field
2631 being blocked for exactly one less than the number of frames of one player
2632 move. Additionally, even when blocking was disabled, the last field was
2633 blocked for exactly one frame.
2634 Since 3.1.1, due to changes in player movement handling, the last field
2635 is not blocked at all when blocking is disabled. When blocking is enabled,
2636 the last field is blocked for exactly the number of frames of one player
2637 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2638 last field is blocked for exactly one more than the number of frames of
2641 Affected levels/tapes:
2642 (!!! yet to be determined -- probably many !!!)
2645 game.use_block_last_field_bug =
2646 (game.engine_version < VERSION_IDENT(3,1,1,0));
2649 Summary of bugfix/change:
2650 Changed behaviour of CE changes with multiple changes per single frame.
2652 Fixed/changed in version:
2656 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2657 This resulted in race conditions where CEs seem to behave strange in some
2658 situations (where triggered CE changes were just skipped because there was
2659 already a CE change on that tile in the playfield in that engine frame).
2660 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2661 (The number of changes per frame must be limited in any case, because else
2662 it is easily possible to define CE changes that would result in an infinite
2663 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2664 should be set large enough so that it would only be reached in cases where
2665 the corresponding CE change conditions run into a loop. Therefore, it seems
2666 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2667 maximal number of change pages for custom elements.)
2669 Affected levels/tapes:
2673 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2674 game.max_num_changes_per_frame = 1;
2676 game.max_num_changes_per_frame =
2677 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2680 /* ---------------------------------------------------------------------- */
2682 /* default scan direction: scan playfield from top/left to bottom/right */
2683 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2685 /* dynamically adjust element properties according to game engine version */
2686 InitElementPropertiesEngine(game.engine_version);
2689 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2690 printf(" tape version == %06d [%s] [file: %06d]\n",
2691 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2693 printf(" => game.engine_version == %06d\n", game.engine_version);
2696 /* ---------- initialize player's initial move delay --------------------- */
2698 /* dynamically adjust player properties according to level information */
2699 for (i = 0; i < MAX_PLAYERS; i++)
2700 game.initial_move_delay_value[i] =
2701 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2703 /* dynamically adjust player properties according to game engine version */
2704 for (i = 0; i < MAX_PLAYERS; i++)
2705 game.initial_move_delay[i] =
2706 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2707 game.initial_move_delay_value[i] : 0);
2709 /* ---------- initialize player's initial push delay --------------------- */
2711 /* dynamically adjust player properties according to game engine version */
2712 game.initial_push_delay_value =
2713 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2715 /* ---------- initialize changing elements ------------------------------- */
2717 /* initialize changing elements information */
2718 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2720 struct ElementInfo *ei = &element_info[i];
2722 /* this pointer might have been changed in the level editor */
2723 ei->change = &ei->change_page[0];
2725 if (!IS_CUSTOM_ELEMENT(i))
2727 ei->change->target_element = EL_EMPTY_SPACE;
2728 ei->change->delay_fixed = 0;
2729 ei->change->delay_random = 0;
2730 ei->change->delay_frames = 1;
2733 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2735 ei->has_change_event[j] = FALSE;
2737 ei->event_page_nr[j] = 0;
2738 ei->event_page[j] = &ei->change_page[0];
2742 /* add changing elements from pre-defined list */
2743 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2745 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2746 struct ElementInfo *ei = &element_info[ch_delay->element];
2748 ei->change->target_element = ch_delay->target_element;
2749 ei->change->delay_fixed = ch_delay->change_delay;
2751 ei->change->pre_change_function = ch_delay->pre_change_function;
2752 ei->change->change_function = ch_delay->change_function;
2753 ei->change->post_change_function = ch_delay->post_change_function;
2755 ei->change->can_change = TRUE;
2756 ei->change->can_change_or_has_action = TRUE;
2758 ei->has_change_event[CE_DELAY] = TRUE;
2760 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2761 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2764 /* ---------- initialize internal run-time variables ------------- */
2766 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2768 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2770 for (j = 0; j < ei->num_change_pages; j++)
2772 ei->change_page[j].can_change_or_has_action =
2773 (ei->change_page[j].can_change |
2774 ei->change_page[j].has_action);
2778 /* add change events from custom element configuration */
2779 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2781 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2783 for (j = 0; j < ei->num_change_pages; j++)
2785 if (!ei->change_page[j].can_change_or_has_action)
2788 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2790 /* only add event page for the first page found with this event */
2791 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2793 ei->has_change_event[k] = TRUE;
2795 ei->event_page_nr[k] = j;
2796 ei->event_page[k] = &ei->change_page[j];
2802 /* ---------- initialize run-time trigger player and element ------------- */
2804 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2806 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2808 for (j = 0; j < ei->num_change_pages; j++)
2810 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2811 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2812 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2813 ei->change_page[j].actual_trigger_ce_value = 0;
2814 ei->change_page[j].actual_trigger_ce_score = 0;
2818 /* ---------- initialize trigger events ---------------------------------- */
2820 /* initialize trigger events information */
2821 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2822 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2823 trigger_events[i][j] = FALSE;
2825 /* add trigger events from element change event properties */
2826 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2828 struct ElementInfo *ei = &element_info[i];
2830 for (j = 0; j < ei->num_change_pages; j++)
2832 if (!ei->change_page[j].can_change_or_has_action)
2835 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2837 int trigger_element = ei->change_page[j].trigger_element;
2839 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2841 if (ei->change_page[j].has_event[k])
2843 if (IS_GROUP_ELEMENT(trigger_element))
2845 struct ElementGroupInfo *group =
2846 element_info[trigger_element].group;
2848 for (l = 0; l < group->num_elements_resolved; l++)
2849 trigger_events[group->element_resolved[l]][k] = TRUE;
2851 else if (trigger_element == EL_ANY_ELEMENT)
2852 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2853 trigger_events[l][k] = TRUE;
2855 trigger_events[trigger_element][k] = TRUE;
2862 /* ---------- initialize push delay -------------------------------------- */
2864 /* initialize push delay values to default */
2865 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867 if (!IS_CUSTOM_ELEMENT(i))
2869 /* set default push delay values (corrected since version 3.0.7-1) */
2870 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2872 element_info[i].push_delay_fixed = 2;
2873 element_info[i].push_delay_random = 8;
2877 element_info[i].push_delay_fixed = 8;
2878 element_info[i].push_delay_random = 8;
2883 /* set push delay value for certain elements from pre-defined list */
2884 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2886 int e = push_delay_list[i].element;
2888 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2889 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2892 /* set push delay value for Supaplex elements for newer engine versions */
2893 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2895 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2897 if (IS_SP_ELEMENT(i))
2899 /* set SP push delay to just enough to push under a falling zonk */
2900 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2902 element_info[i].push_delay_fixed = delay;
2903 element_info[i].push_delay_random = 0;
2908 /* ---------- initialize move stepsize ----------------------------------- */
2910 /* initialize move stepsize values to default */
2911 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2912 if (!IS_CUSTOM_ELEMENT(i))
2913 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2915 /* set move stepsize value for certain elements from pre-defined list */
2916 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2918 int e = move_stepsize_list[i].element;
2920 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2923 /* ---------- initialize collect score ----------------------------------- */
2925 /* initialize collect score values for custom elements from initial value */
2926 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2927 if (IS_CUSTOM_ELEMENT(i))
2928 element_info[i].collect_score = element_info[i].collect_score_initial;
2930 /* ---------- initialize collect count ----------------------------------- */
2932 /* initialize collect count values for non-custom elements */
2933 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2934 if (!IS_CUSTOM_ELEMENT(i))
2935 element_info[i].collect_count_initial = 0;
2937 /* add collect count values for all elements from pre-defined list */
2938 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2939 element_info[collect_count_list[i].element].collect_count_initial =
2940 collect_count_list[i].count;
2942 /* ---------- initialize access direction -------------------------------- */
2944 /* initialize access direction values to default (access from every side) */
2945 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2946 if (!IS_CUSTOM_ELEMENT(i))
2947 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2949 /* set access direction value for certain elements from pre-defined list */
2950 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2951 element_info[access_direction_list[i].element].access_direction =
2952 access_direction_list[i].direction;
2954 /* ---------- initialize explosion content ------------------------------- */
2955 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2957 if (IS_CUSTOM_ELEMENT(i))
2960 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2962 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2964 element_info[i].content.e[x][y] =
2965 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2966 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2967 i == EL_PLAYER_3 ? EL_EMERALD :
2968 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2969 i == EL_MOLE ? EL_EMERALD_RED :
2970 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2971 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2972 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2973 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2974 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2975 i == EL_WALL_EMERALD ? EL_EMERALD :
2976 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2977 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2978 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2979 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2980 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2981 i == EL_WALL_PEARL ? EL_PEARL :
2982 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2987 /* ---------- initialize recursion detection ------------------------------ */
2988 recursion_loop_depth = 0;
2989 recursion_loop_detected = FALSE;
2990 recursion_loop_element = EL_UNDEFINED;
2992 /* ---------- initialize graphics engine ---------------------------------- */
2993 game.scroll_delay_value =
2994 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2995 setup.scroll_delay ? setup.scroll_delay_value : 0);
2996 game.scroll_delay_value =
2997 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3000 int get_num_special_action(int element, int action_first, int action_last)
3002 int num_special_action = 0;
3005 for (i = action_first; i <= action_last; i++)
3007 boolean found = FALSE;
3009 for (j = 0; j < NUM_DIRECTIONS; j++)
3010 if (el_act_dir2img(element, i, j) !=
3011 el_act_dir2img(element, ACTION_DEFAULT, j))
3015 num_special_action++;
3020 return num_special_action;
3025 =============================================================================
3027 -----------------------------------------------------------------------------
3028 initialize and start new game
3029 =============================================================================
3034 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3035 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3036 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3038 boolean do_fading = (game_status == GAME_MODE_MAIN);
3042 game_status = GAME_MODE_PLAYING;
3045 InitGameControlValues();
3047 /* don't play tapes over network */
3048 network_playing = (options.network && !tape.playing);
3050 for (i = 0; i < MAX_PLAYERS; i++)
3052 struct PlayerInfo *player = &stored_player[i];
3054 player->index_nr = i;
3055 player->index_bit = (1 << i);
3056 player->element_nr = EL_PLAYER_1 + i;
3058 player->present = FALSE;
3059 player->active = FALSE;
3060 player->killed = FALSE;
3063 player->effective_action = 0;
3064 player->programmed_action = 0;
3067 player->score_final = 0;
3069 player->gems_still_needed = level.gems_needed;
3070 player->sokobanfields_still_needed = 0;
3071 player->lights_still_needed = 0;
3072 player->friends_still_needed = 0;
3074 for (j = 0; j < MAX_NUM_KEYS; j++)
3075 player->key[j] = FALSE;
3077 player->num_white_keys = 0;
3079 player->dynabomb_count = 0;
3080 player->dynabomb_size = 1;
3081 player->dynabombs_left = 0;
3082 player->dynabomb_xl = FALSE;
3084 player->MovDir = MV_NONE;
3087 player->GfxDir = MV_NONE;
3088 player->GfxAction = ACTION_DEFAULT;
3090 player->StepFrame = 0;
3092 player->use_murphy = FALSE;
3093 player->artwork_element =
3094 (level.use_artwork_element[i] ? level.artwork_element[i] :
3095 player->element_nr);
3097 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3098 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3100 player->gravity = level.initial_player_gravity[i];
3102 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3104 player->actual_frame_counter = 0;
3106 player->step_counter = 0;
3108 player->last_move_dir = MV_NONE;
3110 player->is_active = FALSE;
3112 player->is_waiting = FALSE;
3113 player->is_moving = FALSE;
3114 player->is_auto_moving = FALSE;
3115 player->is_digging = FALSE;
3116 player->is_snapping = FALSE;
3117 player->is_collecting = FALSE;
3118 player->is_pushing = FALSE;
3119 player->is_switching = FALSE;
3120 player->is_dropping = FALSE;
3121 player->is_dropping_pressed = FALSE;
3123 player->is_bored = FALSE;
3124 player->is_sleeping = FALSE;
3126 player->frame_counter_bored = -1;
3127 player->frame_counter_sleeping = -1;
3129 player->anim_delay_counter = 0;
3130 player->post_delay_counter = 0;
3132 player->dir_waiting = MV_NONE;
3133 player->action_waiting = ACTION_DEFAULT;
3134 player->last_action_waiting = ACTION_DEFAULT;
3135 player->special_action_bored = ACTION_DEFAULT;
3136 player->special_action_sleeping = ACTION_DEFAULT;
3138 player->switch_x = -1;
3139 player->switch_y = -1;
3141 player->drop_x = -1;
3142 player->drop_y = -1;
3144 player->show_envelope = 0;
3146 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3148 player->push_delay = -1; /* initialized when pushing starts */
3149 player->push_delay_value = game.initial_push_delay_value;
3151 player->drop_delay = 0;
3152 player->drop_pressed_delay = 0;
3154 player->last_jx = -1;
3155 player->last_jy = -1;
3159 player->shield_normal_time_left = 0;
3160 player->shield_deadly_time_left = 0;
3162 player->inventory_infinite_element = EL_UNDEFINED;
3163 player->inventory_size = 0;
3165 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3166 SnapField(player, 0, 0);
3168 player->LevelSolved = FALSE;
3169 player->GameOver = FALSE;
3171 player->LevelSolved_GameWon = FALSE;
3172 player->LevelSolved_GameEnd = FALSE;
3173 player->LevelSolved_PanelOff = FALSE;
3174 player->LevelSolved_SaveTape = FALSE;
3175 player->LevelSolved_SaveScore = FALSE;
3178 network_player_action_received = FALSE;
3180 #if defined(NETWORK_AVALIABLE)
3181 /* initial null action */
3182 if (network_playing)
3183 SendToServer_MovePlayer(MV_NONE);
3192 TimeLeft = level.time;
3195 ScreenMovDir = MV_NONE;
3199 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3201 AllPlayersGone = FALSE;
3203 game.yamyam_content_nr = 0;
3204 game.magic_wall_active = FALSE;
3205 game.magic_wall_time_left = 0;
3206 game.light_time_left = 0;
3207 game.timegate_time_left = 0;
3208 game.switchgate_pos = 0;
3209 game.wind_direction = level.wind_direction_initial;
3211 #if !USE_PLAYER_GRAVITY
3212 game.gravity = FALSE;
3213 game.explosions_delayed = TRUE;
3216 game.lenses_time_left = 0;
3217 game.magnify_time_left = 0;
3219 game.ball_state = level.ball_state_initial;
3220 game.ball_content_nr = 0;
3222 game.envelope_active = FALSE;
3224 /* set focus to local player for network games, else to all players */
3225 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3226 game.centered_player_nr_next = game.centered_player_nr;
3227 game.set_centered_player = FALSE;
3229 if (network_playing && tape.recording)
3231 /* store client dependent player focus when recording network games */
3232 tape.centered_player_nr_next = game.centered_player_nr_next;
3233 tape.set_centered_player = TRUE;
3236 for (i = 0; i < NUM_BELTS; i++)
3238 game.belt_dir[i] = MV_NONE;
3239 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3242 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3243 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3245 SCAN_PLAYFIELD(x, y)
3247 Feld[x][y] = level.field[x][y];
3248 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3249 ChangeDelay[x][y] = 0;
3250 ChangePage[x][y] = -1;
3251 #if USE_NEW_CUSTOM_VALUE
3252 CustomValue[x][y] = 0; /* initialized in InitField() */
3254 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3256 WasJustMoving[x][y] = 0;
3257 WasJustFalling[x][y] = 0;
3258 CheckCollision[x][y] = 0;
3259 CheckImpact[x][y] = 0;
3261 Pushed[x][y] = FALSE;
3263 ChangeCount[x][y] = 0;
3264 ChangeEvent[x][y] = -1;
3266 ExplodePhase[x][y] = 0;
3267 ExplodeDelay[x][y] = 0;
3268 ExplodeField[x][y] = EX_TYPE_NONE;
3270 RunnerVisit[x][y] = 0;
3271 PlayerVisit[x][y] = 0;
3274 GfxRandom[x][y] = INIT_GFX_RANDOM();
3275 GfxElement[x][y] = EL_UNDEFINED;
3276 GfxAction[x][y] = ACTION_DEFAULT;
3277 GfxDir[x][y] = MV_NONE;
3280 SCAN_PLAYFIELD(x, y)
3282 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3284 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3286 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3289 InitField(x, y, TRUE);
3294 for (i = 0; i < MAX_PLAYERS; i++)
3296 struct PlayerInfo *player = &stored_player[i];
3298 /* set number of special actions for bored and sleeping animation */
3299 player->num_special_action_bored =
3300 get_num_special_action(player->artwork_element,
3301 ACTION_BORING_1, ACTION_BORING_LAST);
3302 player->num_special_action_sleeping =
3303 get_num_special_action(player->artwork_element,
3304 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3307 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3308 emulate_sb ? EMU_SOKOBAN :
3309 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3311 #if USE_NEW_ALL_SLIPPERY
3312 /* initialize type of slippery elements */
3313 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3315 if (!IS_CUSTOM_ELEMENT(i))
3317 /* default: elements slip down either to the left or right randomly */
3318 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3320 /* SP style elements prefer to slip down on the left side */
3321 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3322 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3324 /* BD style elements prefer to slip down on the left side */
3325 if (game.emulation == EMU_BOULDERDASH)
3326 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3331 /* initialize explosion and ignition delay */
3332 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3334 if (!IS_CUSTOM_ELEMENT(i))
3337 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3338 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3339 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3340 int last_phase = (num_phase + 1) * delay;
3341 int half_phase = (num_phase / 2) * delay;
3343 element_info[i].explosion_delay = last_phase - 1;
3344 element_info[i].ignition_delay = half_phase;
3346 if (i == EL_BLACK_ORB)
3347 element_info[i].ignition_delay = 1;
3351 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3352 element_info[i].explosion_delay = 1;
3354 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3355 element_info[i].ignition_delay = 1;
3359 /* correct non-moving belts to start moving left */
3360 for (i = 0; i < NUM_BELTS; i++)
3361 if (game.belt_dir[i] == MV_NONE)
3362 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3364 /* check if any connected player was not found in playfield */
3365 for (i = 0; i < MAX_PLAYERS; i++)
3367 struct PlayerInfo *player = &stored_player[i];
3369 if (player->connected && !player->present)
3371 for (j = 0; j < MAX_PLAYERS; j++)
3373 struct PlayerInfo *some_player = &stored_player[j];
3374 int jx = some_player->jx, jy = some_player->jy;
3376 /* assign first free player found that is present in the playfield */
3377 if (some_player->present && !some_player->connected)
3379 player->present = TRUE;
3380 player->active = TRUE;
3382 some_player->present = FALSE;
3383 some_player->active = FALSE;
3385 player->artwork_element = some_player->artwork_element;
3387 player->block_last_field = some_player->block_last_field;
3388 player->block_delay_adjustment = some_player->block_delay_adjustment;
3390 StorePlayer[jx][jy] = player->element_nr;
3391 player->jx = player->last_jx = jx;
3392 player->jy = player->last_jy = jy;
3402 /* when playing a tape, eliminate all players who do not participate */
3404 for (i = 0; i < MAX_PLAYERS; i++)
3406 if (stored_player[i].active && !tape.player_participates[i])
3408 struct PlayerInfo *player = &stored_player[i];
3409 int jx = player->jx, jy = player->jy;
3411 player->active = FALSE;
3412 StorePlayer[jx][jy] = 0;
3413 Feld[jx][jy] = EL_EMPTY;
3417 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3419 /* when in single player mode, eliminate all but the first active player */
3421 for (i = 0; i < MAX_PLAYERS; i++)
3423 if (stored_player[i].active)
3425 for (j = i + 1; j < MAX_PLAYERS; j++)
3427 if (stored_player[j].active)
3429 struct PlayerInfo *player = &stored_player[j];
3430 int jx = player->jx, jy = player->jy;
3432 player->active = FALSE;
3433 player->present = FALSE;
3435 StorePlayer[jx][jy] = 0;
3436 Feld[jx][jy] = EL_EMPTY;
3443 /* when recording the game, store which players take part in the game */
3446 for (i = 0; i < MAX_PLAYERS; i++)
3447 if (stored_player[i].active)
3448 tape.player_participates[i] = TRUE;
3453 for (i = 0; i < MAX_PLAYERS; i++)
3455 struct PlayerInfo *player = &stored_player[i];
3457 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3462 if (local_player == player)
3463 printf("Player %d is local player.\n", i+1);
3467 if (BorderElement == EL_EMPTY)
3470 SBX_Right = lev_fieldx - SCR_FIELDX;
3472 SBY_Lower = lev_fieldy - SCR_FIELDY;
3477 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3479 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3482 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3483 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3485 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3486 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3488 /* if local player not found, look for custom element that might create
3489 the player (make some assumptions about the right custom element) */
3490 if (!local_player->present)
3492 int start_x = 0, start_y = 0;
3493 int found_rating = 0;
3494 int found_element = EL_UNDEFINED;
3495 int player_nr = local_player->index_nr;
3497 SCAN_PLAYFIELD(x, y)
3499 int element = Feld[x][y];
3504 if (level.use_start_element[player_nr] &&
3505 level.start_element[player_nr] == element &&
3512 found_element = element;
3515 if (!IS_CUSTOM_ELEMENT(element))
3518 if (CAN_CHANGE(element))
3520 for (i = 0; i < element_info[element].num_change_pages; i++)
3522 /* check for player created from custom element as single target */
3523 content = element_info[element].change_page[i].target_element;
3524 is_player = ELEM_IS_PLAYER(content);
3526 if (is_player && (found_rating < 3 ||
3527 (found_rating == 3 && element < found_element)))
3533 found_element = element;
3538 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3540 /* check for player created from custom element as explosion content */
3541 content = element_info[element].content.e[xx][yy];
3542 is_player = ELEM_IS_PLAYER(content);
3544 if (is_player && (found_rating < 2 ||
3545 (found_rating == 2 && element < found_element)))
3547 start_x = x + xx - 1;
3548 start_y = y + yy - 1;
3551 found_element = element;
3554 if (!CAN_CHANGE(element))
3557 for (i = 0; i < element_info[element].num_change_pages; i++)
3559 /* check for player created from custom element as extended target */
3561 element_info[element].change_page[i].target_content.e[xx][yy];
3563 is_player = ELEM_IS_PLAYER(content);
3565 if (is_player && (found_rating < 1 ||
3566 (found_rating == 1 && element < found_element)))
3568 start_x = x + xx - 1;
3569 start_y = y + yy - 1;
3572 found_element = element;
3578 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3579 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3582 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3583 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3588 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3589 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3590 local_player->jx - MIDPOSX);
3592 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3593 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3594 local_player->jy - MIDPOSY);
3597 /* do not use PLAYING mask for fading out from main screen */
3598 game_status = GAME_MODE_MAIN;
3602 if (!game.restart_level)
3603 CloseDoor(DOOR_CLOSE_1);
3606 if (level_editor_test_game)
3607 FadeSkipNextFadeIn();
3609 FadeSetEnterScreen();
3611 if (level_editor_test_game)
3612 fading = fading_none;
3614 fading = menu.destination;
3618 FadeOut(REDRAW_FIELD);
3621 FadeOut(REDRAW_FIELD);
3624 game_status = GAME_MODE_PLAYING;
3626 /* !!! FIX THIS (START) !!! */
3627 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3629 InitGameEngine_EM();
3631 /* blit playfield from scroll buffer to normal back buffer for fading in */
3632 BlitScreenToBitmap_EM(backbuffer);
3639 /* after drawing the level, correct some elements */
3640 if (game.timegate_time_left == 0)
3641 CloseAllOpenTimegates();
3643 /* blit playfield from scroll buffer to normal back buffer for fading in */
3644 if (setup.soft_scrolling)
3645 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3647 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3649 /* !!! FIX THIS (END) !!! */
3652 FadeIn(REDRAW_FIELD);
3655 FadeIn(REDRAW_FIELD);
3660 if (!game.restart_level)
3662 /* copy default game door content to main double buffer */
3663 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3664 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3667 SetPanelBackground();
3668 SetDrawBackgroundMask(REDRAW_DOOR_1);
3670 DrawGameDoorValues();
3672 if (!game.restart_level)
3676 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3677 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3678 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3682 /* copy actual game door content to door double buffer for OpenDoor() */
3683 BlitBitmap(drawto, bitmap_db_door,
3684 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3686 OpenDoor(DOOR_OPEN_ALL);
3688 PlaySound(SND_GAME_STARTING);
3690 if (setup.sound_music)
3693 KeyboardAutoRepeatOffUnlessAutoplay();
3697 for (i = 0; i < MAX_PLAYERS; i++)
3698 printf("Player %d %sactive.\n",
3699 i + 1, (stored_player[i].active ? "" : "not "));
3710 game.restart_level = FALSE;
3713 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3715 /* this is used for non-R'n'D game engines to update certain engine values */
3717 /* needed to determine if sounds are played within the visible screen area */
3718 scroll_x = actual_scroll_x;
3719 scroll_y = actual_scroll_y;
3722 void InitMovDir(int x, int y)
3724 int i, element = Feld[x][y];
3725 static int xy[4][2] =
3732 static int direction[3][4] =
3734 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3735 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3736 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3745 Feld[x][y] = EL_BUG;
3746 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3749 case EL_SPACESHIP_RIGHT:
3750 case EL_SPACESHIP_UP:
3751 case EL_SPACESHIP_LEFT:
3752 case EL_SPACESHIP_DOWN:
3753 Feld[x][y] = EL_SPACESHIP;
3754 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3757 case EL_BD_BUTTERFLY_RIGHT:
3758 case EL_BD_BUTTERFLY_UP:
3759 case EL_BD_BUTTERFLY_LEFT:
3760 case EL_BD_BUTTERFLY_DOWN:
3761 Feld[x][y] = EL_BD_BUTTERFLY;
3762 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3765 case EL_BD_FIREFLY_RIGHT:
3766 case EL_BD_FIREFLY_UP:
3767 case EL_BD_FIREFLY_LEFT:
3768 case EL_BD_FIREFLY_DOWN:
3769 Feld[x][y] = EL_BD_FIREFLY;
3770 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3773 case EL_PACMAN_RIGHT:
3775 case EL_PACMAN_LEFT:
3776 case EL_PACMAN_DOWN:
3777 Feld[x][y] = EL_PACMAN;
3778 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3781 case EL_YAMYAM_LEFT:
3782 case EL_YAMYAM_RIGHT:
3784 case EL_YAMYAM_DOWN:
3785 Feld[x][y] = EL_YAMYAM;
3786 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3789 case EL_SP_SNIKSNAK:
3790 MovDir[x][y] = MV_UP;
3793 case EL_SP_ELECTRON:
3794 MovDir[x][y] = MV_LEFT;
3801 Feld[x][y] = EL_MOLE;
3802 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3806 if (IS_CUSTOM_ELEMENT(element))
3808 struct ElementInfo *ei = &element_info[element];
3809 int move_direction_initial = ei->move_direction_initial;
3810 int move_pattern = ei->move_pattern;
3812 if (move_direction_initial == MV_START_PREVIOUS)
3814 if (MovDir[x][y] != MV_NONE)
3817 move_direction_initial = MV_START_AUTOMATIC;
3820 if (move_direction_initial == MV_START_RANDOM)
3821 MovDir[x][y] = 1 << RND(4);
3822 else if (move_direction_initial & MV_ANY_DIRECTION)
3823 MovDir[x][y] = move_direction_initial;
3824 else if (move_pattern == MV_ALL_DIRECTIONS ||
3825 move_pattern == MV_TURNING_LEFT ||
3826 move_pattern == MV_TURNING_RIGHT ||
3827 move_pattern == MV_TURNING_LEFT_RIGHT ||
3828 move_pattern == MV_TURNING_RIGHT_LEFT ||
3829 move_pattern == MV_TURNING_RANDOM)
3830 MovDir[x][y] = 1 << RND(4);
3831 else if (move_pattern == MV_HORIZONTAL)
3832 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3833 else if (move_pattern == MV_VERTICAL)
3834 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3835 else if (move_pattern & MV_ANY_DIRECTION)
3836 MovDir[x][y] = element_info[element].move_pattern;
3837 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3838 move_pattern == MV_ALONG_RIGHT_SIDE)
3840 /* use random direction as default start direction */
3841 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3842 MovDir[x][y] = 1 << RND(4);
3844 for (i = 0; i < NUM_DIRECTIONS; i++)
3846 int x1 = x + xy[i][0];
3847 int y1 = y + xy[i][1];
3849 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3851 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3852 MovDir[x][y] = direction[0][i];
3854 MovDir[x][y] = direction[1][i];
3863 MovDir[x][y] = 1 << RND(4);
3865 if (element != EL_BUG &&
3866 element != EL_SPACESHIP &&
3867 element != EL_BD_BUTTERFLY &&
3868 element != EL_BD_FIREFLY)
3871 for (i = 0; i < NUM_DIRECTIONS; i++)
3873 int x1 = x + xy[i][0];
3874 int y1 = y + xy[i][1];
3876 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3878 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3880 MovDir[x][y] = direction[0][i];
3883 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3884 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3886 MovDir[x][y] = direction[1][i];
3895 GfxDir[x][y] = MovDir[x][y];
3898 void InitAmoebaNr(int x, int y)
3901 int group_nr = AmoebeNachbarNr(x, y);
3905 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3907 if (AmoebaCnt[i] == 0)
3915 AmoebaNr[x][y] = group_nr;
3916 AmoebaCnt[group_nr]++;
3917 AmoebaCnt2[group_nr]++;
3920 static void PlayerWins(struct PlayerInfo *player)
3922 player->LevelSolved = TRUE;
3923 player->GameOver = TRUE;
3925 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3926 level.native_em_level->lev->score : player->score);
3931 static int time, time_final;
3932 static int score, score_final;
3933 static int game_over_delay_1 = 0;
3934 static int game_over_delay_2 = 0;
3935 int game_over_delay_value_1 = 50;
3936 int game_over_delay_value_2 = 50;
3938 if (!local_player->LevelSolved_GameWon)
3942 /* do not start end game actions before the player stops moving (to exit) */
3943 if (local_player->MovPos)
3946 local_player->LevelSolved_GameWon = TRUE;
3947 local_player->LevelSolved_SaveTape = tape.recording;
3948 local_player->LevelSolved_SaveScore = !tape.playing;
3950 if (tape.auto_play) /* tape might already be stopped here */
3951 tape.auto_play_level_solved = TRUE;
3957 game_over_delay_1 = game_over_delay_value_1;
3958 game_over_delay_2 = game_over_delay_value_2;
3960 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3961 score = score_final = local_player->score_final;
3966 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3968 else if (level.time == 0 && TimePlayed < 999)
3971 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3974 local_player->score_final = score_final;
3976 if (level_editor_test_game)
3979 score = score_final;
3982 game_control_value[GAME_CONTROL_TIME] = time;
3983 game_control_value[GAME_CONTROL_SCORE] = score;
3985 DisplayGameControlValues();
3987 DrawGameValue_Time(time);
3988 DrawGameValue_Score(score);
3992 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3994 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3996 /* close exit door after last player */
3997 if ((AllPlayersGone &&
3998 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3999 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4000 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4001 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4002 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4004 int element = Feld[ExitX][ExitY];
4007 if (element == EL_EM_EXIT_OPEN ||
4008 element == EL_EM_STEEL_EXIT_OPEN)
4015 Feld[ExitX][ExitY] =
4016 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4017 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4018 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4019 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4020 EL_EM_STEEL_EXIT_CLOSING);
4022 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4026 /* player disappears */
4027 DrawLevelField(ExitX, ExitY);
4030 for (i = 0; i < MAX_PLAYERS; i++)
4032 struct PlayerInfo *player = &stored_player[i];
4034 if (player->present)
4036 RemovePlayer(player);
4038 /* player disappears */
4039 DrawLevelField(player->jx, player->jy);
4044 PlaySound(SND_GAME_WINNING);
4047 if (game_over_delay_1 > 0)
4049 game_over_delay_1--;
4054 if (time != time_final)
4056 int time_to_go = ABS(time_final - time);
4057 int time_count_dir = (time < time_final ? +1 : -1);
4058 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4060 time += time_count_steps * time_count_dir;
4061 score += time_count_steps * level.score[SC_TIME_BONUS];
4064 game_control_value[GAME_CONTROL_TIME] = time;
4065 game_control_value[GAME_CONTROL_SCORE] = score;
4067 DisplayGameControlValues();
4069 DrawGameValue_Time(time);
4070 DrawGameValue_Score(score);
4073 if (time == time_final)
4074 StopSound(SND_GAME_LEVELTIME_BONUS);
4075 else if (setup.sound_loops)
4076 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4078 PlaySound(SND_GAME_LEVELTIME_BONUS);
4083 local_player->LevelSolved_PanelOff = TRUE;
4085 if (game_over_delay_2 > 0)
4087 game_over_delay_2--;
4100 boolean raise_level = FALSE;
4102 local_player->LevelSolved_GameEnd = TRUE;
4104 CloseDoor(DOOR_CLOSE_1);
4106 if (local_player->LevelSolved_SaveTape)
4113 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4115 SaveTape(tape.level_nr); /* ask to save tape */
4119 if (level_editor_test_game)
4121 game_status = GAME_MODE_MAIN;
4124 DrawAndFadeInMainMenu(REDRAW_FIELD);
4132 if (!local_player->LevelSolved_SaveScore)
4135 FadeOut(REDRAW_FIELD);
4138 game_status = GAME_MODE_MAIN;
4140 DrawAndFadeInMainMenu(REDRAW_FIELD);
4145 if (level_nr == leveldir_current->handicap_level)
4147 leveldir_current->handicap_level++;
4148 SaveLevelSetup_SeriesInfo();
4151 if (level_nr < leveldir_current->last_level)
4152 raise_level = TRUE; /* advance to next level */
4154 if ((hi_pos = NewHiScore()) >= 0)
4156 game_status = GAME_MODE_SCORES;
4158 DrawHallOfFame(hi_pos);
4169 FadeOut(REDRAW_FIELD);
4172 game_status = GAME_MODE_MAIN;
4180 DrawAndFadeInMainMenu(REDRAW_FIELD);
4189 LoadScore(level_nr);
4191 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4192 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4195 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4197 if (local_player->score_final > highscore[k].Score)
4199 /* player has made it to the hall of fame */
4201 if (k < MAX_SCORE_ENTRIES - 1)
4203 int m = MAX_SCORE_ENTRIES - 1;
4206 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4207 if (strEqual(setup.player_name, highscore[l].Name))
4209 if (m == k) /* player's new highscore overwrites his old one */
4213 for (l = m; l > k; l--)
4215 strcpy(highscore[l].Name, highscore[l - 1].Name);
4216 highscore[l].Score = highscore[l - 1].Score;
4223 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4224 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4225 highscore[k].Score = local_player->score_final;
4231 else if (!strncmp(setup.player_name, highscore[k].Name,
4232 MAX_PLAYER_NAME_LEN))
4233 break; /* player already there with a higher score */
4239 SaveScore(level_nr);
4244 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4246 int element = Feld[x][y];
4247 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4248 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4249 int horiz_move = (dx != 0);
4250 int sign = (horiz_move ? dx : dy);
4251 int step = sign * element_info[element].move_stepsize;
4253 /* special values for move stepsize for spring and things on conveyor belt */
4256 if (CAN_FALL(element) &&
4257 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4258 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4259 else if (element == EL_SPRING)
4260 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4266 inline static int getElementMoveStepsize(int x, int y)
4268 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4271 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4273 if (player->GfxAction != action || player->GfxDir != dir)
4276 printf("Player frame reset! (%d => %d, %d => %d)\n",
4277 player->GfxAction, action, player->GfxDir, dir);
4280 player->GfxAction = action;
4281 player->GfxDir = dir;
4283 player->StepFrame = 0;
4287 #if USE_GFX_RESET_GFX_ANIMATION
4288 static void ResetGfxFrame(int x, int y, boolean redraw)
4290 int element = Feld[x][y];
4291 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4292 int last_gfx_frame = GfxFrame[x][y];
4294 if (graphic_info[graphic].anim_global_sync)
4295 GfxFrame[x][y] = FrameCounter;
4296 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4297 GfxFrame[x][y] = CustomValue[x][y];
4298 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4299 GfxFrame[x][y] = element_info[element].collect_score;
4300 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4301 GfxFrame[x][y] = ChangeDelay[x][y];
4303 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4304 DrawLevelGraphicAnimation(x, y, graphic);
4308 static void ResetGfxAnimation(int x, int y)
4310 GfxAction[x][y] = ACTION_DEFAULT;
4311 GfxDir[x][y] = MovDir[x][y];
4314 #if USE_GFX_RESET_GFX_ANIMATION
4315 ResetGfxFrame(x, y, FALSE);
4319 static void ResetRandomAnimationValue(int x, int y)
4321 GfxRandom[x][y] = INIT_GFX_RANDOM();
4324 void InitMovingField(int x, int y, int direction)
4326 int element = Feld[x][y];
4327 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4328 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4331 boolean is_moving_before, is_moving_after;
4333 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4336 /* check if element was/is moving or being moved before/after mode change */
4339 is_moving_before = (WasJustMoving[x][y] != 0);
4341 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4342 is_moving_before = WasJustMoving[x][y];
4345 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4347 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4349 /* reset animation only for moving elements which change direction of moving
4350 or which just started or stopped moving
4351 (else CEs with property "can move" / "not moving" are reset each frame) */
4352 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4354 if (is_moving_before != is_moving_after ||
4355 direction != MovDir[x][y])
4356 ResetGfxAnimation(x, y);
4358 if ((is_moving_before || is_moving_after) && !continues_moving)
4359 ResetGfxAnimation(x, y);
4362 if (!continues_moving)
4363 ResetGfxAnimation(x, y);
4366 MovDir[x][y] = direction;
4367 GfxDir[x][y] = direction;
4369 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4370 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4371 direction == MV_DOWN && CAN_FALL(element) ?
4372 ACTION_FALLING : ACTION_MOVING);
4374 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4375 ACTION_FALLING : ACTION_MOVING);
4378 /* this is needed for CEs with property "can move" / "not moving" */
4380 if (is_moving_after)
4382 if (Feld[newx][newy] == EL_EMPTY)
4383 Feld[newx][newy] = EL_BLOCKED;
4385 MovDir[newx][newy] = MovDir[x][y];
4387 #if USE_NEW_CUSTOM_VALUE
4388 CustomValue[newx][newy] = CustomValue[x][y];
4391 GfxFrame[newx][newy] = GfxFrame[x][y];
4392 GfxRandom[newx][newy] = GfxRandom[x][y];
4393 GfxAction[newx][newy] = GfxAction[x][y];
4394 GfxDir[newx][newy] = GfxDir[x][y];
4398 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4400 int direction = MovDir[x][y];
4401 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4402 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4408 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4410 int oldx = x, oldy = y;
4411 int direction = MovDir[x][y];
4413 if (direction == MV_LEFT)
4415 else if (direction == MV_RIGHT)
4417 else if (direction == MV_UP)
4419 else if (direction == MV_DOWN)
4422 *comes_from_x = oldx;
4423 *comes_from_y = oldy;
4426 int MovingOrBlocked2Element(int x, int y)
4428 int element = Feld[x][y];
4430 if (element == EL_BLOCKED)
4434 Blocked2Moving(x, y, &oldx, &oldy);
4435 return Feld[oldx][oldy];
4441 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4443 /* like MovingOrBlocked2Element(), but if element is moving
4444 and (x,y) is the field the moving element is just leaving,
4445 return EL_BLOCKED instead of the element value */
4446 int element = Feld[x][y];
4448 if (IS_MOVING(x, y))
4450 if (element == EL_BLOCKED)
4454 Blocked2Moving(x, y, &oldx, &oldy);
4455 return Feld[oldx][oldy];
4464 static void RemoveField(int x, int y)
4466 Feld[x][y] = EL_EMPTY;
4472 #if USE_NEW_CUSTOM_VALUE
4473 CustomValue[x][y] = 0;
4477 ChangeDelay[x][y] = 0;
4478 ChangePage[x][y] = -1;
4479 Pushed[x][y] = FALSE;
4482 ExplodeField[x][y] = EX_TYPE_NONE;
4485 GfxElement[x][y] = EL_UNDEFINED;
4486 GfxAction[x][y] = ACTION_DEFAULT;
4487 GfxDir[x][y] = MV_NONE;
4490 void RemoveMovingField(int x, int y)
4492 int oldx = x, oldy = y, newx = x, newy = y;
4493 int element = Feld[x][y];
4494 int next_element = EL_UNDEFINED;
4496 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4499 if (IS_MOVING(x, y))
4501 Moving2Blocked(x, y, &newx, &newy);
4503 if (Feld[newx][newy] != EL_BLOCKED)
4505 /* element is moving, but target field is not free (blocked), but
4506 already occupied by something different (example: acid pool);
4507 in this case, only remove the moving field, but not the target */
4509 RemoveField(oldx, oldy);
4511 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4513 DrawLevelField(oldx, oldy);
4518 else if (element == EL_BLOCKED)
4520 Blocked2Moving(x, y, &oldx, &oldy);
4521 if (!IS_MOVING(oldx, oldy))
4525 if (element == EL_BLOCKED &&
4526 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4527 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4528 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4529 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4530 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4531 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4532 next_element = get_next_element(Feld[oldx][oldy]);
4534 RemoveField(oldx, oldy);
4535 RemoveField(newx, newy);
4537 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4539 if (next_element != EL_UNDEFINED)
4540 Feld[oldx][oldy] = next_element;
4542 DrawLevelField(oldx, oldy);
4543 DrawLevelField(newx, newy);
4546 void DrawDynamite(int x, int y)
4548 int sx = SCREENX(x), sy = SCREENY(y);
4549 int graphic = el2img(Feld[x][y]);
4552 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4555 if (IS_WALKABLE_INSIDE(Back[x][y]))
4559 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4560 else if (Store[x][y])
4561 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4563 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4565 if (Back[x][y] || Store[x][y])
4566 DrawGraphicThruMask(sx, sy, graphic, frame);
4568 DrawGraphic(sx, sy, graphic, frame);
4571 void CheckDynamite(int x, int y)
4573 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4577 if (MovDelay[x][y] != 0)
4580 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4586 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4591 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4593 boolean num_checked_players = 0;
4596 for (i = 0; i < MAX_PLAYERS; i++)
4598 if (stored_player[i].active)
4600 int sx = stored_player[i].jx;
4601 int sy = stored_player[i].jy;
4603 if (num_checked_players == 0)
4610 *sx1 = MIN(*sx1, sx);
4611 *sy1 = MIN(*sy1, sy);
4612 *sx2 = MAX(*sx2, sx);
4613 *sy2 = MAX(*sy2, sy);
4616 num_checked_players++;
4621 static boolean checkIfAllPlayersFitToScreen_RND()
4623 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4625 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4627 return (sx2 - sx1 < SCR_FIELDX &&
4628 sy2 - sy1 < SCR_FIELDY);
4631 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4633 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4635 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4637 *sx = (sx1 + sx2) / 2;
4638 *sy = (sy1 + sy2) / 2;
4641 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4642 boolean center_screen, boolean quick_relocation)
4644 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4645 boolean no_delay = (tape.warp_forward);
4646 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4647 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4649 if (quick_relocation)
4651 int offset = game.scroll_delay_value;
4653 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4655 if (!level.shifted_relocation || center_screen)
4657 /* quick relocation (without scrolling), with centering of screen */
4659 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4660 x > SBX_Right + MIDPOSX ? SBX_Right :
4663 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4664 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4669 /* quick relocation (without scrolling), but do not center screen */
4671 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4672 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4675 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4676 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4679 int offset_x = x + (scroll_x - center_scroll_x);
4680 int offset_y = y + (scroll_y - center_scroll_y);
4682 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4683 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4684 offset_x - MIDPOSX);
4686 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4687 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4688 offset_y - MIDPOSY);
4693 /* quick relocation (without scrolling), inside visible screen area */
4695 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4696 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4697 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4699 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4700 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4701 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4703 /* don't scroll over playfield boundaries */
4704 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4705 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4707 /* don't scroll over playfield boundaries */
4708 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4709 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4712 RedrawPlayfield(TRUE, 0,0,0,0);
4717 int scroll_xx, scroll_yy;
4719 if (!level.shifted_relocation || center_screen)
4721 /* visible relocation (with scrolling), with centering of screen */
4723 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4724 x > SBX_Right + MIDPOSX ? SBX_Right :
4727 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4728 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4733 /* visible relocation (with scrolling), but do not center screen */
4735 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4736 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4739 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4740 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4743 int offset_x = x + (scroll_x - center_scroll_x);
4744 int offset_y = y + (scroll_y - center_scroll_y);
4746 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4747 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4748 offset_x - MIDPOSX);
4750 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4751 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4752 offset_y - MIDPOSY);
4757 /* visible relocation (with scrolling), with centering of screen */
4759 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4760 x > SBX_Right + MIDPOSX ? SBX_Right :
4763 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4764 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4768 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4770 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4773 int fx = FX, fy = FY;
4775 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4776 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4778 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4784 fx += dx * TILEX / 2;
4785 fy += dy * TILEY / 2;
4787 ScrollLevel(dx, dy);
4790 /* scroll in two steps of half tile size to make things smoother */
4791 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4793 Delay(wait_delay_value);
4795 /* scroll second step to align at full tile size */
4797 Delay(wait_delay_value);
4802 Delay(wait_delay_value);
4806 void RelocatePlayer(int jx, int jy, int el_player_raw)
4808 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4809 int player_nr = GET_PLAYER_NR(el_player);
4810 struct PlayerInfo *player = &stored_player[player_nr];
4811 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4812 boolean no_delay = (tape.warp_forward);
4813 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4814 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4815 int old_jx = player->jx;
4816 int old_jy = player->jy;
4817 int old_element = Feld[old_jx][old_jy];
4818 int element = Feld[jx][jy];
4819 boolean player_relocated = (old_jx != jx || old_jy != jy);
4821 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4822 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4823 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4824 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4825 int leave_side_horiz = move_dir_horiz;
4826 int leave_side_vert = move_dir_vert;
4827 int enter_side = enter_side_horiz | enter_side_vert;
4828 int leave_side = leave_side_horiz | leave_side_vert;
4830 if (player->GameOver) /* do not reanimate dead player */
4833 if (!player_relocated) /* no need to relocate the player */
4836 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4838 RemoveField(jx, jy); /* temporarily remove newly placed player */
4839 DrawLevelField(jx, jy);
4842 if (player->present)
4844 while (player->MovPos)
4846 ScrollPlayer(player, SCROLL_GO_ON);
4847 ScrollScreen(NULL, SCROLL_GO_ON);
4849 AdvanceFrameAndPlayerCounters(player->index_nr);
4854 Delay(wait_delay_value);
4857 DrawPlayer(player); /* needed here only to cleanup last field */
4858 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4860 player->is_moving = FALSE;
4863 if (IS_CUSTOM_ELEMENT(old_element))
4864 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4866 player->index_bit, leave_side);
4868 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4870 player->index_bit, leave_side);
4872 Feld[jx][jy] = el_player;
4873 InitPlayerField(jx, jy, el_player, TRUE);
4875 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4877 Feld[jx][jy] = element;
4878 InitField(jx, jy, FALSE);
4881 /* only visually relocate centered player */
4882 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4883 FALSE, level.instant_relocation);
4885 TestIfPlayerTouchesBadThing(jx, jy);
4886 TestIfPlayerTouchesCustomElement(jx, jy);
4888 if (IS_CUSTOM_ELEMENT(element))
4889 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4890 player->index_bit, enter_side);
4892 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4893 player->index_bit, enter_side);
4896 void Explode(int ex, int ey, int phase, int mode)
4902 /* !!! eliminate this variable !!! */
4903 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4905 if (game.explosions_delayed)
4907 ExplodeField[ex][ey] = mode;
4911 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4913 int center_element = Feld[ex][ey];
4914 int artwork_element, explosion_element; /* set these values later */
4917 /* --- This is only really needed (and now handled) in "Impact()". --- */
4918 /* do not explode moving elements that left the explode field in time */
4919 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4920 center_element == EL_EMPTY &&
4921 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4926 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4927 if (mode == EX_TYPE_NORMAL ||
4928 mode == EX_TYPE_CENTER ||
4929 mode == EX_TYPE_CROSS)
4930 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4933 /* remove things displayed in background while burning dynamite */
4934 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4937 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4939 /* put moving element to center field (and let it explode there) */
4940 center_element = MovingOrBlocked2Element(ex, ey);
4941 RemoveMovingField(ex, ey);
4942 Feld[ex][ey] = center_element;
4945 /* now "center_element" is finally determined -- set related values now */
4946 artwork_element = center_element; /* for custom player artwork */
4947 explosion_element = center_element; /* for custom player artwork */
4949 if (IS_PLAYER(ex, ey))
4951 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4953 artwork_element = stored_player[player_nr].artwork_element;
4955 if (level.use_explosion_element[player_nr])
4957 explosion_element = level.explosion_element[player_nr];
4958 artwork_element = explosion_element;
4963 if (mode == EX_TYPE_NORMAL ||
4964 mode == EX_TYPE_CENTER ||
4965 mode == EX_TYPE_CROSS)
4966 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4969 last_phase = element_info[explosion_element].explosion_delay + 1;
4971 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4973 int xx = x - ex + 1;
4974 int yy = y - ey + 1;
4977 if (!IN_LEV_FIELD(x, y) ||
4978 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4979 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4982 element = Feld[x][y];
4984 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4986 element = MovingOrBlocked2Element(x, y);
4988 if (!IS_EXPLOSION_PROOF(element))
4989 RemoveMovingField(x, y);
4992 /* indestructible elements can only explode in center (but not flames) */
4993 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4994 mode == EX_TYPE_BORDER)) ||
4995 element == EL_FLAMES)
4998 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4999 behaviour, for example when touching a yamyam that explodes to rocks
5000 with active deadly shield, a rock is created under the player !!! */
5001 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5003 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5004 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5005 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5007 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5010 if (IS_ACTIVE_BOMB(element))
5012 /* re-activate things under the bomb like gate or penguin */
5013 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5020 /* save walkable background elements while explosion on same tile */
5021 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5022 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5023 Back[x][y] = element;
5025 /* ignite explodable elements reached by other explosion */
5026 if (element == EL_EXPLOSION)
5027 element = Store2[x][y];
5029 if (AmoebaNr[x][y] &&
5030 (element == EL_AMOEBA_FULL ||
5031 element == EL_BD_AMOEBA ||
5032 element == EL_AMOEBA_GROWING))
5034 AmoebaCnt[AmoebaNr[x][y]]--;
5035 AmoebaCnt2[AmoebaNr[x][y]]--;
5040 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5042 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5044 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5046 if (PLAYERINFO(ex, ey)->use_murphy)
5047 Store[x][y] = EL_EMPTY;
5050 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5051 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5052 else if (ELEM_IS_PLAYER(center_element))
5053 Store[x][y] = EL_EMPTY;
5054 else if (center_element == EL_YAMYAM)
5055 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5056 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5057 Store[x][y] = element_info[center_element].content.e[xx][yy];
5059 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5060 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5061 otherwise) -- FIX THIS !!! */
5062 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5063 Store[x][y] = element_info[element].content.e[1][1];
5065 else if (!CAN_EXPLODE(element))
5066 Store[x][y] = element_info[element].content.e[1][1];
5069 Store[x][y] = EL_EMPTY;
5071 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5072 center_element == EL_AMOEBA_TO_DIAMOND)
5073 Store2[x][y] = element;
5075 Feld[x][y] = EL_EXPLOSION;
5076 GfxElement[x][y] = artwork_element;
5078 ExplodePhase[x][y] = 1;
5079 ExplodeDelay[x][y] = last_phase;
5084 if (center_element == EL_YAMYAM)
5085 game.yamyam_content_nr =
5086 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5098 GfxFrame[x][y] = 0; /* restart explosion animation */
5100 last_phase = ExplodeDelay[x][y];
5102 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5106 /* activate this even in non-DEBUG version until cause for crash in
5107 getGraphicAnimationFrame() (see below) is found and eliminated */
5113 /* this can happen if the player leaves an explosion just in time */
5114 if (GfxElement[x][y] == EL_UNDEFINED)
5115 GfxElement[x][y] = EL_EMPTY;
5117 if (GfxElement[x][y] == EL_UNDEFINED)
5120 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5121 printf("Explode(): This should never happen!\n");
5124 GfxElement[x][y] = EL_EMPTY;
5130 border_element = Store2[x][y];
5131 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5132 border_element = StorePlayer[x][y];
5134 if (phase == element_info[border_element].ignition_delay ||
5135 phase == last_phase)
5137 boolean border_explosion = FALSE;
5139 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5140 !PLAYER_EXPLOSION_PROTECTED(x, y))
5142 KillPlayerUnlessExplosionProtected(x, y);
5143 border_explosion = TRUE;
5145 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5147 Feld[x][y] = Store2[x][y];
5150 border_explosion = TRUE;
5152 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5154 AmoebeUmwandeln(x, y);
5156 border_explosion = TRUE;
5159 /* if an element just explodes due to another explosion (chain-reaction),
5160 do not immediately end the new explosion when it was the last frame of
5161 the explosion (as it would be done in the following "if"-statement!) */
5162 if (border_explosion && phase == last_phase)
5166 if (phase == last_phase)
5170 element = Feld[x][y] = Store[x][y];
5171 Store[x][y] = Store2[x][y] = 0;
5172 GfxElement[x][y] = EL_UNDEFINED;
5174 /* player can escape from explosions and might therefore be still alive */
5175 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5176 element <= EL_PLAYER_IS_EXPLODING_4)
5178 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5179 int explosion_element = EL_PLAYER_1 + player_nr;
5180 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5181 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5183 if (level.use_explosion_element[player_nr])
5184 explosion_element = level.explosion_element[player_nr];
5186 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5187 element_info[explosion_element].content.e[xx][yy]);
5190 /* restore probably existing indestructible background element */
5191 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5192 element = Feld[x][y] = Back[x][y];
5195 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5196 GfxDir[x][y] = MV_NONE;
5197 ChangeDelay[x][y] = 0;
5198 ChangePage[x][y] = -1;
5200 #if USE_NEW_CUSTOM_VALUE
5201 CustomValue[x][y] = 0;
5204 InitField_WithBug2(x, y, FALSE);
5206 DrawLevelField(x, y);
5208 TestIfElementTouchesCustomElement(x, y);
5210 if (GFX_CRUMBLED(element))
5211 DrawLevelFieldCrumbledSandNeighbours(x, y);
5213 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5214 StorePlayer[x][y] = 0;
5216 if (ELEM_IS_PLAYER(element))
5217 RelocatePlayer(x, y, element);
5219 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5221 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5222 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5225 DrawLevelFieldCrumbledSand(x, y);
5227 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5229 DrawLevelElement(x, y, Back[x][y]);
5230 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5232 else if (IS_WALKABLE_UNDER(Back[x][y]))
5234 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5235 DrawLevelElementThruMask(x, y, Back[x][y]);
5237 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5238 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5242 void DynaExplode(int ex, int ey)
5245 int dynabomb_element = Feld[ex][ey];
5246 int dynabomb_size = 1;
5247 boolean dynabomb_xl = FALSE;
5248 struct PlayerInfo *player;
5249 static int xy[4][2] =
5257 if (IS_ACTIVE_BOMB(dynabomb_element))
5259 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5260 dynabomb_size = player->dynabomb_size;
5261 dynabomb_xl = player->dynabomb_xl;
5262 player->dynabombs_left++;
5265 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5267 for (i = 0; i < NUM_DIRECTIONS; i++)
5269 for (j = 1; j <= dynabomb_size; j++)
5271 int x = ex + j * xy[i][0];
5272 int y = ey + j * xy[i][1];
5275 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5278 element = Feld[x][y];
5280 /* do not restart explosions of fields with active bombs */
5281 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5284 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5286 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5287 !IS_DIGGABLE(element) && !dynabomb_xl)
5293 void Bang(int x, int y)
5295 int element = MovingOrBlocked2Element(x, y);
5296 int explosion_type = EX_TYPE_NORMAL;
5298 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5300 struct PlayerInfo *player = PLAYERINFO(x, y);
5302 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5303 player->element_nr);
5305 if (level.use_explosion_element[player->index_nr])
5307 int explosion_element = level.explosion_element[player->index_nr];
5309 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5310 explosion_type = EX_TYPE_CROSS;
5311 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5312 explosion_type = EX_TYPE_CENTER;
5320 case EL_BD_BUTTERFLY:
5323 case EL_DARK_YAMYAM:
5327 RaiseScoreElement(element);
5330 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5331 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5332 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5333 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5334 case EL_DYNABOMB_INCREASE_NUMBER:
5335 case EL_DYNABOMB_INCREASE_SIZE:
5336 case EL_DYNABOMB_INCREASE_POWER:
5337 explosion_type = EX_TYPE_DYNA;
5340 case EL_DC_LANDMINE:
5342 case EL_EM_EXIT_OPEN:
5343 case EL_EM_STEEL_EXIT_OPEN:
5345 explosion_type = EX_TYPE_CENTER;
5350 case EL_LAMP_ACTIVE:
5351 case EL_AMOEBA_TO_DIAMOND:
5352 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5353 explosion_type = EX_TYPE_CENTER;
5357 if (element_info[element].explosion_type == EXPLODES_CROSS)
5358 explosion_type = EX_TYPE_CROSS;
5359 else if (element_info[element].explosion_type == EXPLODES_1X1)
5360 explosion_type = EX_TYPE_CENTER;
5364 if (explosion_type == EX_TYPE_DYNA)
5367 Explode(x, y, EX_PHASE_START, explosion_type);
5369 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5372 void SplashAcid(int x, int y)
5374 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5375 (!IN_LEV_FIELD(x - 1, y - 2) ||
5376 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5377 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5379 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5380 (!IN_LEV_FIELD(x + 1, y - 2) ||
5381 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5382 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5384 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5387 static void InitBeltMovement()
5389 static int belt_base_element[4] =
5391 EL_CONVEYOR_BELT_1_LEFT,
5392 EL_CONVEYOR_BELT_2_LEFT,
5393 EL_CONVEYOR_BELT_3_LEFT,
5394 EL_CONVEYOR_BELT_4_LEFT
5396 static int belt_base_active_element[4] =
5398 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5399 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5400 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5401 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5406 /* set frame order for belt animation graphic according to belt direction */
5407 for (i = 0; i < NUM_BELTS; i++)
5411 for (j = 0; j < NUM_BELT_PARTS; j++)
5413 int element = belt_base_active_element[belt_nr] + j;
5414 int graphic = el2img(element);
5416 if (game.belt_dir[i] == MV_LEFT)
5417 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5419 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5423 SCAN_PLAYFIELD(x, y)
5425 int element = Feld[x][y];
5427 for (i = 0; i < NUM_BELTS; i++)
5429 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5431 int e_belt_nr = getBeltNrFromBeltElement(element);
5434 if (e_belt_nr == belt_nr)
5436 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5438 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5445 static void ToggleBeltSwitch(int x, int y)
5447 static int belt_base_element[4] =
5449 EL_CONVEYOR_BELT_1_LEFT,
5450 EL_CONVEYOR_BELT_2_LEFT,
5451 EL_CONVEYOR_BELT_3_LEFT,
5452 EL_CONVEYOR_BELT_4_LEFT
5454 static int belt_base_active_element[4] =
5456 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5457 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5458 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5459 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5461 static int belt_base_switch_element[4] =
5463 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5464 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5465 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5466 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5468 static int belt_move_dir[4] =
5476 int element = Feld[x][y];
5477 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5478 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5479 int belt_dir = belt_move_dir[belt_dir_nr];
5482 if (!IS_BELT_SWITCH(element))
5485 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5486 game.belt_dir[belt_nr] = belt_dir;
5488 if (belt_dir_nr == 3)
5491 /* set frame order for belt animation graphic according to belt direction */
5492 for (i = 0; i < NUM_BELT_PARTS; i++)
5494 int element = belt_base_active_element[belt_nr] + i;
5495 int graphic = el2img(element);
5497 if (belt_dir == MV_LEFT)
5498 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5500 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5503 SCAN_PLAYFIELD(xx, yy)
5505 int element = Feld[xx][yy];
5507 if (IS_BELT_SWITCH(element))
5509 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5511 if (e_belt_nr == belt_nr)
5513 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5514 DrawLevelField(xx, yy);
5517 else if (IS_BELT(element) && belt_dir != MV_NONE)
5519 int e_belt_nr = getBeltNrFromBeltElement(element);
5521 if (e_belt_nr == belt_nr)
5523 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5525 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5526 DrawLevelField(xx, yy);
5529 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5531 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5533 if (e_belt_nr == belt_nr)
5535 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5537 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5538 DrawLevelField(xx, yy);
5544 static void ToggleSwitchgateSwitch(int x, int y)
5548 game.switchgate_pos = !game.switchgate_pos;
5550 SCAN_PLAYFIELD(xx, yy)
5552 int element = Feld[xx][yy];
5554 #if !USE_BOTH_SWITCHGATE_SWITCHES
5555 if (element == EL_SWITCHGATE_SWITCH_UP ||
5556 element == EL_SWITCHGATE_SWITCH_DOWN)
5558 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5559 DrawLevelField(xx, yy);
5561 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5562 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5564 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5565 DrawLevelField(xx, yy);
5568 if (element == EL_SWITCHGATE_SWITCH_UP)
5570 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5571 DrawLevelField(xx, yy);
5573 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5575 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5576 DrawLevelField(xx, yy);
5578 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5580 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5581 DrawLevelField(xx, yy);
5583 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5585 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5586 DrawLevelField(xx, yy);
5589 else if (element == EL_SWITCHGATE_OPEN ||
5590 element == EL_SWITCHGATE_OPENING)
5592 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5594 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5596 else if (element == EL_SWITCHGATE_CLOSED ||
5597 element == EL_SWITCHGATE_CLOSING)
5599 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5601 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5606 static int getInvisibleActiveFromInvisibleElement(int element)
5608 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5609 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5610 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5614 static int getInvisibleFromInvisibleActiveElement(int element)
5616 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5617 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5618 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5622 static void RedrawAllLightSwitchesAndInvisibleElements()
5626 SCAN_PLAYFIELD(x, y)
5628 int element = Feld[x][y];
5630 if (element == EL_LIGHT_SWITCH &&
5631 game.light_time_left > 0)
5633 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5634 DrawLevelField(x, y);
5636 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5637 game.light_time_left == 0)
5639 Feld[x][y] = EL_LIGHT_SWITCH;
5640 DrawLevelField(x, y);
5642 else if (element == EL_EMC_DRIPPER &&
5643 game.light_time_left > 0)
5645 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5646 DrawLevelField(x, y);
5648 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5649 game.light_time_left == 0)
5651 Feld[x][y] = EL_EMC_DRIPPER;
5652 DrawLevelField(x, y);
5654 else if (element == EL_INVISIBLE_STEELWALL ||
5655 element == EL_INVISIBLE_WALL ||
5656 element == EL_INVISIBLE_SAND)
5658 if (game.light_time_left > 0)
5659 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5661 DrawLevelField(x, y);
5663 /* uncrumble neighbour fields, if needed */
5664 if (element == EL_INVISIBLE_SAND)
5665 DrawLevelFieldCrumbledSandNeighbours(x, y);
5667 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5668 element == EL_INVISIBLE_WALL_ACTIVE ||
5669 element == EL_INVISIBLE_SAND_ACTIVE)
5671 if (game.light_time_left == 0)
5672 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5674 DrawLevelField(x, y);
5676 /* re-crumble neighbour fields, if needed */
5677 if (element == EL_INVISIBLE_SAND)
5678 DrawLevelFieldCrumbledSandNeighbours(x, y);
5683 static void RedrawAllInvisibleElementsForLenses()
5687 SCAN_PLAYFIELD(x, y)
5689 int element = Feld[x][y];
5691 if (element == EL_EMC_DRIPPER &&
5692 game.lenses_time_left > 0)
5694 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5695 DrawLevelField(x, y);
5697 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5698 game.lenses_time_left == 0)
5700 Feld[x][y] = EL_EMC_DRIPPER;
5701 DrawLevelField(x, y);
5703 else if (element == EL_INVISIBLE_STEELWALL ||
5704 element == EL_INVISIBLE_WALL ||
5705 element == EL_INVISIBLE_SAND)
5707 if (game.lenses_time_left > 0)
5708 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5710 DrawLevelField(x, y);
5712 /* uncrumble neighbour fields, if needed */
5713 if (element == EL_INVISIBLE_SAND)
5714 DrawLevelFieldCrumbledSandNeighbours(x, y);
5716 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5717 element == EL_INVISIBLE_WALL_ACTIVE ||
5718 element == EL_INVISIBLE_SAND_ACTIVE)
5720 if (game.lenses_time_left == 0)
5721 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5723 DrawLevelField(x, y);
5725 /* re-crumble neighbour fields, if needed */
5726 if (element == EL_INVISIBLE_SAND)
5727 DrawLevelFieldCrumbledSandNeighbours(x, y);
5732 static void RedrawAllInvisibleElementsForMagnifier()
5736 SCAN_PLAYFIELD(x, y)
5738 int element = Feld[x][y];
5740 if (element == EL_EMC_FAKE_GRASS &&
5741 game.magnify_time_left > 0)
5743 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5744 DrawLevelField(x, y);
5746 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5747 game.magnify_time_left == 0)
5749 Feld[x][y] = EL_EMC_FAKE_GRASS;
5750 DrawLevelField(x, y);
5752 else if (IS_GATE_GRAY(element) &&
5753 game.magnify_time_left > 0)
5755 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5756 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5757 IS_EM_GATE_GRAY(element) ?
5758 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5759 IS_EMC_GATE_GRAY(element) ?
5760 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5762 DrawLevelField(x, y);
5764 else if (IS_GATE_GRAY_ACTIVE(element) &&
5765 game.magnify_time_left == 0)
5767 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5768 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5769 IS_EM_GATE_GRAY_ACTIVE(element) ?
5770 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5771 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5772 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5774 DrawLevelField(x, y);
5779 static void ToggleLightSwitch(int x, int y)
5781 int element = Feld[x][y];
5783 game.light_time_left =
5784 (element == EL_LIGHT_SWITCH ?
5785 level.time_light * FRAMES_PER_SECOND : 0);
5787 RedrawAllLightSwitchesAndInvisibleElements();
5790 static void ActivateTimegateSwitch(int x, int y)
5794 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5796 SCAN_PLAYFIELD(xx, yy)
5798 int element = Feld[xx][yy];
5800 if (element == EL_TIMEGATE_CLOSED ||
5801 element == EL_TIMEGATE_CLOSING)
5803 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5804 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5808 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5810 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5811 DrawLevelField(xx, yy);
5818 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5819 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5821 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5825 void Impact(int x, int y)
5827 boolean last_line = (y == lev_fieldy - 1);
5828 boolean object_hit = FALSE;
5829 boolean impact = (last_line || object_hit);
5830 int element = Feld[x][y];
5831 int smashed = EL_STEELWALL;
5833 if (!last_line) /* check if element below was hit */
5835 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5838 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5839 MovDir[x][y + 1] != MV_DOWN ||
5840 MovPos[x][y + 1] <= TILEY / 2));
5842 /* do not smash moving elements that left the smashed field in time */
5843 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5844 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5847 #if USE_QUICKSAND_IMPACT_BUGFIX
5848 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5850 RemoveMovingField(x, y + 1);
5851 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5852 Feld[x][y + 2] = EL_ROCK;
5853 DrawLevelField(x, y + 2);
5858 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5860 RemoveMovingField(x, y + 1);
5861 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5862 Feld[x][y + 2] = EL_ROCK;
5863 DrawLevelField(x, y + 2);
5870 smashed = MovingOrBlocked2Element(x, y + 1);
5872 impact = (last_line || object_hit);
5875 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5877 SplashAcid(x, y + 1);
5881 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5882 /* only reset graphic animation if graphic really changes after impact */
5884 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5886 ResetGfxAnimation(x, y);
5887 DrawLevelField(x, y);
5890 if (impact && CAN_EXPLODE_IMPACT(element))
5895 else if (impact && element == EL_PEARL &&
5896 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5898 ResetGfxAnimation(x, y);
5900 Feld[x][y] = EL_PEARL_BREAKING;
5901 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5904 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5906 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5911 if (impact && element == EL_AMOEBA_DROP)
5913 if (object_hit && IS_PLAYER(x, y + 1))
5914 KillPlayerUnlessEnemyProtected(x, y + 1);
5915 else if (object_hit && smashed == EL_PENGUIN)
5919 Feld[x][y] = EL_AMOEBA_GROWING;
5920 Store[x][y] = EL_AMOEBA_WET;
5922 ResetRandomAnimationValue(x, y);
5927 if (object_hit) /* check which object was hit */
5929 if ((CAN_PASS_MAGIC_WALL(element) &&
5930 (smashed == EL_MAGIC_WALL ||
5931 smashed == EL_BD_MAGIC_WALL)) ||
5932 (CAN_PASS_DC_MAGIC_WALL(element) &&
5933 smashed == EL_DC_MAGIC_WALL))
5936 int activated_magic_wall =
5937 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5938 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5939 EL_DC_MAGIC_WALL_ACTIVE);
5941 /* activate magic wall / mill */
5942 SCAN_PLAYFIELD(xx, yy)
5944 if (Feld[xx][yy] == smashed)
5945 Feld[xx][yy] = activated_magic_wall;
5948 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5949 game.magic_wall_active = TRUE;
5951 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5952 SND_MAGIC_WALL_ACTIVATING :
5953 smashed == EL_BD_MAGIC_WALL ?
5954 SND_BD_MAGIC_WALL_ACTIVATING :
5955 SND_DC_MAGIC_WALL_ACTIVATING));
5958 if (IS_PLAYER(x, y + 1))
5960 if (CAN_SMASH_PLAYER(element))
5962 KillPlayerUnlessEnemyProtected(x, y + 1);
5966 else if (smashed == EL_PENGUIN)
5968 if (CAN_SMASH_PLAYER(element))
5974 else if (element == EL_BD_DIAMOND)
5976 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5982 else if (((element == EL_SP_INFOTRON ||
5983 element == EL_SP_ZONK) &&
5984 (smashed == EL_SP_SNIKSNAK ||
5985 smashed == EL_SP_ELECTRON ||
5986 smashed == EL_SP_DISK_ORANGE)) ||
5987 (element == EL_SP_INFOTRON &&
5988 smashed == EL_SP_DISK_YELLOW))
5993 else if (CAN_SMASH_EVERYTHING(element))
5995 if (IS_CLASSIC_ENEMY(smashed) ||
5996 CAN_EXPLODE_SMASHED(smashed))
6001 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6003 if (smashed == EL_LAMP ||
6004 smashed == EL_LAMP_ACTIVE)
6009 else if (smashed == EL_NUT)
6011 Feld[x][y + 1] = EL_NUT_BREAKING;
6012 PlayLevelSound(x, y, SND_NUT_BREAKING);
6013 RaiseScoreElement(EL_NUT);
6016 else if (smashed == EL_PEARL)
6018 ResetGfxAnimation(x, y);
6020 Feld[x][y + 1] = EL_PEARL_BREAKING;
6021 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6024 else if (smashed == EL_DIAMOND)
6026 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6027 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6030 else if (IS_BELT_SWITCH(smashed))
6032 ToggleBeltSwitch(x, y + 1);
6034 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6035 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6036 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6037 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6039 ToggleSwitchgateSwitch(x, y + 1);
6041 else if (smashed == EL_LIGHT_SWITCH ||
6042 smashed == EL_LIGHT_SWITCH_ACTIVE)
6044 ToggleLightSwitch(x, y + 1);
6049 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6052 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6054 CheckElementChangeBySide(x, y + 1, smashed, element,
6055 CE_SWITCHED, CH_SIDE_TOP);
6056 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6062 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6067 /* play sound of magic wall / mill */
6069 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6070 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6071 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6073 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6074 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6075 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6076 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6077 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6078 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6083 /* play sound of object that hits the ground */
6084 if (last_line || object_hit)
6085 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6088 inline static void TurnRoundExt(int x, int y)
6100 { 0, 0 }, { 0, 0 }, { 0, 0 },
6105 int left, right, back;
6109 { MV_DOWN, MV_UP, MV_RIGHT },
6110 { MV_UP, MV_DOWN, MV_LEFT },
6112 { MV_LEFT, MV_RIGHT, MV_DOWN },
6116 { MV_RIGHT, MV_LEFT, MV_UP }
6119 int element = Feld[x][y];
6120 int move_pattern = element_info[element].move_pattern;
6122 int old_move_dir = MovDir[x][y];
6123 int left_dir = turn[old_move_dir].left;
6124 int right_dir = turn[old_move_dir].right;
6125 int back_dir = turn[old_move_dir].back;
6127 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6128 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6129 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6130 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6132 int left_x = x + left_dx, left_y = y + left_dy;
6133 int right_x = x + right_dx, right_y = y + right_dy;
6134 int move_x = x + move_dx, move_y = y + move_dy;
6138 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6140 TestIfBadThingTouchesOtherBadThing(x, y);
6142 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6143 MovDir[x][y] = right_dir;
6144 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6145 MovDir[x][y] = left_dir;
6147 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6149 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6152 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6154 TestIfBadThingTouchesOtherBadThing(x, y);
6156 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6157 MovDir[x][y] = left_dir;
6158 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6159 MovDir[x][y] = right_dir;
6161 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6163 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6166 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6168 TestIfBadThingTouchesOtherBadThing(x, y);
6170 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6171 MovDir[x][y] = left_dir;
6172 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6173 MovDir[x][y] = right_dir;
6175 if (MovDir[x][y] != old_move_dir)
6178 else if (element == EL_YAMYAM)
6180 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6181 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6183 if (can_turn_left && can_turn_right)
6184 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6185 else if (can_turn_left)
6186 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6187 else if (can_turn_right)
6188 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6190 MovDir[x][y] = back_dir;
6192 MovDelay[x][y] = 16 + 16 * RND(3);
6194 else if (element == EL_DARK_YAMYAM)
6196 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6198 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6201 if (can_turn_left && can_turn_right)
6202 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6203 else if (can_turn_left)
6204 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6205 else if (can_turn_right)
6206 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6208 MovDir[x][y] = back_dir;
6210 MovDelay[x][y] = 16 + 16 * RND(3);
6212 else if (element == EL_PACMAN)
6214 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6215 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6217 if (can_turn_left && can_turn_right)
6218 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6219 else if (can_turn_left)
6220 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6221 else if (can_turn_right)
6222 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6224 MovDir[x][y] = back_dir;
6226 MovDelay[x][y] = 6 + RND(40);
6228 else if (element == EL_PIG)
6230 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6231 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6232 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6233 boolean should_turn_left, should_turn_right, should_move_on;
6235 int rnd = RND(rnd_value);
6237 should_turn_left = (can_turn_left &&
6239 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6240 y + back_dy + left_dy)));
6241 should_turn_right = (can_turn_right &&
6243 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6244 y + back_dy + right_dy)));
6245 should_move_on = (can_move_on &&
6248 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6249 y + move_dy + left_dy) ||
6250 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6251 y + move_dy + right_dy)));
6253 if (should_turn_left || should_turn_right || should_move_on)
6255 if (should_turn_left && should_turn_right && should_move_on)
6256 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6257 rnd < 2 * rnd_value / 3 ? right_dir :
6259 else if (should_turn_left && should_turn_right)
6260 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6261 else if (should_turn_left && should_move_on)
6262 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6263 else if (should_turn_right && should_move_on)
6264 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6265 else if (should_turn_left)
6266 MovDir[x][y] = left_dir;
6267 else if (should_turn_right)
6268 MovDir[x][y] = right_dir;
6269 else if (should_move_on)
6270 MovDir[x][y] = old_move_dir;
6272 else if (can_move_on && rnd > rnd_value / 8)
6273 MovDir[x][y] = old_move_dir;
6274 else if (can_turn_left && can_turn_right)
6275 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6276 else if (can_turn_left && rnd > rnd_value / 8)
6277 MovDir[x][y] = left_dir;
6278 else if (can_turn_right && rnd > rnd_value/8)
6279 MovDir[x][y] = right_dir;
6281 MovDir[x][y] = back_dir;
6283 xx = x + move_xy[MovDir[x][y]].dx;
6284 yy = y + move_xy[MovDir[x][y]].dy;
6286 if (!IN_LEV_FIELD(xx, yy) ||
6287 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6288 MovDir[x][y] = old_move_dir;
6292 else if (element == EL_DRAGON)
6294 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6295 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6296 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6298 int rnd = RND(rnd_value);
6300 if (can_move_on && rnd > rnd_value / 8)
6301 MovDir[x][y] = old_move_dir;
6302 else if (can_turn_left && can_turn_right)
6303 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6304 else if (can_turn_left && rnd > rnd_value / 8)
6305 MovDir[x][y] = left_dir;
6306 else if (can_turn_right && rnd > rnd_value / 8)
6307 MovDir[x][y] = right_dir;
6309 MovDir[x][y] = back_dir;
6311 xx = x + move_xy[MovDir[x][y]].dx;
6312 yy = y + move_xy[MovDir[x][y]].dy;
6314 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6315 MovDir[x][y] = old_move_dir;
6319 else if (element == EL_MOLE)
6321 boolean can_move_on =
6322 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6323 IS_AMOEBOID(Feld[move_x][move_y]) ||
6324 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6327 boolean can_turn_left =
6328 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6329 IS_AMOEBOID(Feld[left_x][left_y])));
6331 boolean can_turn_right =
6332 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6333 IS_AMOEBOID(Feld[right_x][right_y])));
6335 if (can_turn_left && can_turn_right)
6336 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6337 else if (can_turn_left)
6338 MovDir[x][y] = left_dir;
6340 MovDir[x][y] = right_dir;
6343 if (MovDir[x][y] != old_move_dir)
6346 else if (element == EL_BALLOON)
6348 MovDir[x][y] = game.wind_direction;
6351 else if (element == EL_SPRING)
6353 #if USE_NEW_SPRING_BUMPER
6354 if (MovDir[x][y] & MV_HORIZONTAL)
6356 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6357 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6359 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6360 ResetGfxAnimation(move_x, move_y);
6361 DrawLevelField(move_x, move_y);
6363 MovDir[x][y] = back_dir;
6365 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6366 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6367 MovDir[x][y] = MV_NONE;
6370 if (MovDir[x][y] & MV_HORIZONTAL &&
6371 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6372 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6373 MovDir[x][y] = MV_NONE;
6378 else if (element == EL_ROBOT ||
6379 element == EL_SATELLITE ||
6380 element == EL_PENGUIN ||
6381 element == EL_EMC_ANDROID)
6383 int attr_x = -1, attr_y = -1;
6394 for (i = 0; i < MAX_PLAYERS; i++)
6396 struct PlayerInfo *player = &stored_player[i];
6397 int jx = player->jx, jy = player->jy;
6399 if (!player->active)
6403 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6411 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6412 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6413 game.engine_version < VERSION_IDENT(3,1,0,0)))
6419 if (element == EL_PENGUIN)
6422 static int xy[4][2] =
6430 for (i = 0; i < NUM_DIRECTIONS; i++)
6432 int ex = x + xy[i][0];
6433 int ey = y + xy[i][1];
6435 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6436 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6437 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6438 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6447 MovDir[x][y] = MV_NONE;
6449 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6450 else if (attr_x > x)
6451 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6453 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6454 else if (attr_y > y)
6455 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6457 if (element == EL_ROBOT)
6461 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6462 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6463 Moving2Blocked(x, y, &newx, &newy);
6465 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6466 MovDelay[x][y] = 8 + 8 * !RND(3);
6468 MovDelay[x][y] = 16;
6470 else if (element == EL_PENGUIN)
6476 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6478 boolean first_horiz = RND(2);
6479 int new_move_dir = MovDir[x][y];
6482 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6483 Moving2Blocked(x, y, &newx, &newy);
6485 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6489 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6490 Moving2Blocked(x, y, &newx, &newy);
6492 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6495 MovDir[x][y] = old_move_dir;
6499 else if (element == EL_SATELLITE)
6505 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6507 boolean first_horiz = RND(2);
6508 int new_move_dir = MovDir[x][y];
6511 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6512 Moving2Blocked(x, y, &newx, &newy);
6514 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6518 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6519 Moving2Blocked(x, y, &newx, &newy);
6521 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6524 MovDir[x][y] = old_move_dir;
6528 else if (element == EL_EMC_ANDROID)
6530 static int check_pos[16] =
6532 -1, /* 0 => (invalid) */
6533 7, /* 1 => MV_LEFT */
6534 3, /* 2 => MV_RIGHT */
6535 -1, /* 3 => (invalid) */
6537 0, /* 5 => MV_LEFT | MV_UP */
6538 2, /* 6 => MV_RIGHT | MV_UP */
6539 -1, /* 7 => (invalid) */
6540 5, /* 8 => MV_DOWN */
6541 6, /* 9 => MV_LEFT | MV_DOWN */
6542 4, /* 10 => MV_RIGHT | MV_DOWN */
6543 -1, /* 11 => (invalid) */
6544 -1, /* 12 => (invalid) */
6545 -1, /* 13 => (invalid) */
6546 -1, /* 14 => (invalid) */
6547 -1, /* 15 => (invalid) */
6555 { -1, -1, MV_LEFT | MV_UP },
6557 { +1, -1, MV_RIGHT | MV_UP },
6558 { +1, 0, MV_RIGHT },
6559 { +1, +1, MV_RIGHT | MV_DOWN },
6561 { -1, +1, MV_LEFT | MV_DOWN },
6564 int start_pos, check_order;
6565 boolean can_clone = FALSE;
6568 /* check if there is any free field around current position */
6569 for (i = 0; i < 8; i++)
6571 int newx = x + check_xy[i].dx;
6572 int newy = y + check_xy[i].dy;
6574 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6582 if (can_clone) /* randomly find an element to clone */
6586 start_pos = check_pos[RND(8)];
6587 check_order = (RND(2) ? -1 : +1);
6589 for (i = 0; i < 8; i++)
6591 int pos_raw = start_pos + i * check_order;
6592 int pos = (pos_raw + 8) % 8;
6593 int newx = x + check_xy[pos].dx;
6594 int newy = y + check_xy[pos].dy;
6596 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6598 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6599 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6601 Store[x][y] = Feld[newx][newy];
6610 if (can_clone) /* randomly find a direction to move */
6614 start_pos = check_pos[RND(8)];
6615 check_order = (RND(2) ? -1 : +1);
6617 for (i = 0; i < 8; i++)
6619 int pos_raw = start_pos + i * check_order;
6620 int pos = (pos_raw + 8) % 8;
6621 int newx = x + check_xy[pos].dx;
6622 int newy = y + check_xy[pos].dy;
6623 int new_move_dir = check_xy[pos].dir;
6625 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6627 MovDir[x][y] = new_move_dir;
6628 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6637 if (can_clone) /* cloning and moving successful */
6640 /* cannot clone -- try to move towards player */
6642 start_pos = check_pos[MovDir[x][y] & 0x0f];
6643 check_order = (RND(2) ? -1 : +1);
6645 for (i = 0; i < 3; i++)
6647 /* first check start_pos, then previous/next or (next/previous) pos */
6648 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6649 int pos = (pos_raw + 8) % 8;
6650 int newx = x + check_xy[pos].dx;
6651 int newy = y + check_xy[pos].dy;
6652 int new_move_dir = check_xy[pos].dir;
6654 if (IS_PLAYER(newx, newy))
6657 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6659 MovDir[x][y] = new_move_dir;
6660 MovDelay[x][y] = level.android_move_time * 8 + 1;
6667 else if (move_pattern == MV_TURNING_LEFT ||
6668 move_pattern == MV_TURNING_RIGHT ||
6669 move_pattern == MV_TURNING_LEFT_RIGHT ||
6670 move_pattern == MV_TURNING_RIGHT_LEFT ||
6671 move_pattern == MV_TURNING_RANDOM ||
6672 move_pattern == MV_ALL_DIRECTIONS)
6674 boolean can_turn_left =
6675 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6676 boolean can_turn_right =
6677 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6679 if (element_info[element].move_stepsize == 0) /* "not moving" */
6682 if (move_pattern == MV_TURNING_LEFT)
6683 MovDir[x][y] = left_dir;
6684 else if (move_pattern == MV_TURNING_RIGHT)
6685 MovDir[x][y] = right_dir;
6686 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6687 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6688 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6689 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6690 else if (move_pattern == MV_TURNING_RANDOM)
6691 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6692 can_turn_right && !can_turn_left ? right_dir :
6693 RND(2) ? left_dir : right_dir);
6694 else if (can_turn_left && can_turn_right)
6695 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6696 else if (can_turn_left)
6697 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6698 else if (can_turn_right)
6699 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6701 MovDir[x][y] = back_dir;
6703 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6705 else if (move_pattern == MV_HORIZONTAL ||
6706 move_pattern == MV_VERTICAL)
6708 if (move_pattern & old_move_dir)
6709 MovDir[x][y] = back_dir;
6710 else if (move_pattern == MV_HORIZONTAL)
6711 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6712 else if (move_pattern == MV_VERTICAL)
6713 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6715 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6717 else if (move_pattern & MV_ANY_DIRECTION)
6719 MovDir[x][y] = move_pattern;
6720 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6722 else if (move_pattern & MV_WIND_DIRECTION)
6724 MovDir[x][y] = game.wind_direction;
6725 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6727 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6729 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6730 MovDir[x][y] = left_dir;
6731 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6732 MovDir[x][y] = right_dir;
6734 if (MovDir[x][y] != old_move_dir)
6735 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6737 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6739 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6740 MovDir[x][y] = right_dir;
6741 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6742 MovDir[x][y] = left_dir;
6744 if (MovDir[x][y] != old_move_dir)
6745 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6747 else if (move_pattern == MV_TOWARDS_PLAYER ||
6748 move_pattern == MV_AWAY_FROM_PLAYER)
6750 int attr_x = -1, attr_y = -1;
6752 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6763 for (i = 0; i < MAX_PLAYERS; i++)
6765 struct PlayerInfo *player = &stored_player[i];
6766 int jx = player->jx, jy = player->jy;
6768 if (!player->active)
6772 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6780 MovDir[x][y] = MV_NONE;
6782 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6783 else if (attr_x > x)
6784 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6786 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6787 else if (attr_y > y)
6788 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6790 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6792 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6794 boolean first_horiz = RND(2);
6795 int new_move_dir = MovDir[x][y];
6797 if (element_info[element].move_stepsize == 0) /* "not moving" */
6799 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6800 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6806 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6807 Moving2Blocked(x, y, &newx, &newy);
6809 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6813 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6814 Moving2Blocked(x, y, &newx, &newy);
6816 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6819 MovDir[x][y] = old_move_dir;
6822 else if (move_pattern == MV_WHEN_PUSHED ||
6823 move_pattern == MV_WHEN_DROPPED)
6825 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6826 MovDir[x][y] = MV_NONE;
6830 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6832 static int test_xy[7][2] =
6842 static int test_dir[7] =
6852 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6853 int move_preference = -1000000; /* start with very low preference */
6854 int new_move_dir = MV_NONE;
6855 int start_test = RND(4);
6858 for (i = 0; i < NUM_DIRECTIONS; i++)
6860 int move_dir = test_dir[start_test + i];
6861 int move_dir_preference;
6863 xx = x + test_xy[start_test + i][0];
6864 yy = y + test_xy[start_test + i][1];
6866 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6867 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6869 new_move_dir = move_dir;
6874 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6877 move_dir_preference = -1 * RunnerVisit[xx][yy];
6878 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6879 move_dir_preference = PlayerVisit[xx][yy];
6881 if (move_dir_preference > move_preference)
6883 /* prefer field that has not been visited for the longest time */
6884 move_preference = move_dir_preference;
6885 new_move_dir = move_dir;
6887 else if (move_dir_preference == move_preference &&
6888 move_dir == old_move_dir)
6890 /* prefer last direction when all directions are preferred equally */
6891 move_preference = move_dir_preference;
6892 new_move_dir = move_dir;
6896 MovDir[x][y] = new_move_dir;
6897 if (old_move_dir != new_move_dir)
6898 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6902 static void TurnRound(int x, int y)
6904 int direction = MovDir[x][y];
6908 GfxDir[x][y] = MovDir[x][y];
6910 if (direction != MovDir[x][y])
6914 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6916 ResetGfxFrame(x, y, FALSE);
6919 static boolean JustBeingPushed(int x, int y)
6923 for (i = 0; i < MAX_PLAYERS; i++)
6925 struct PlayerInfo *player = &stored_player[i];
6927 if (player->active && player->is_pushing && player->MovPos)
6929 int next_jx = player->jx + (player->jx - player->last_jx);
6930 int next_jy = player->jy + (player->jy - player->last_jy);
6932 if (x == next_jx && y == next_jy)
6940 void StartMoving(int x, int y)
6942 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6943 int element = Feld[x][y];
6948 if (MovDelay[x][y] == 0)
6949 GfxAction[x][y] = ACTION_DEFAULT;
6951 if (CAN_FALL(element) && y < lev_fieldy - 1)
6953 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6954 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6955 if (JustBeingPushed(x, y))
6958 if (element == EL_QUICKSAND_FULL)
6960 if (IS_FREE(x, y + 1))
6962 InitMovingField(x, y, MV_DOWN);
6963 started_moving = TRUE;
6965 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6966 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6967 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6968 Store[x][y] = EL_ROCK;
6970 Store[x][y] = EL_ROCK;
6973 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6975 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6977 if (!MovDelay[x][y])
6978 MovDelay[x][y] = TILEY + 1;
6987 Feld[x][y] = EL_QUICKSAND_EMPTY;
6988 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6989 Store[x][y + 1] = Store[x][y];
6992 PlayLevelSoundAction(x, y, ACTION_FILLING);
6995 else if (element == EL_QUICKSAND_FAST_FULL)
6997 if (IS_FREE(x, y + 1))
6999 InitMovingField(x, y, MV_DOWN);
7000 started_moving = TRUE;
7002 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7003 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7004 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7005 Store[x][y] = EL_ROCK;
7007 Store[x][y] = EL_ROCK;
7010 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7012 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7014 if (!MovDelay[x][y])
7015 MovDelay[x][y] = TILEY + 1;
7024 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7025 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7026 Store[x][y + 1] = Store[x][y];
7029 PlayLevelSoundAction(x, y, ACTION_FILLING);
7032 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7033 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7035 InitMovingField(x, y, MV_DOWN);
7036 started_moving = TRUE;
7038 Feld[x][y] = EL_QUICKSAND_FILLING;
7039 Store[x][y] = element;
7041 PlayLevelSoundAction(x, y, ACTION_FILLING);
7043 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7044 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7046 InitMovingField(x, y, MV_DOWN);
7047 started_moving = TRUE;
7049 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7050 Store[x][y] = element;
7052 PlayLevelSoundAction(x, y, ACTION_FILLING);
7054 else if (element == EL_MAGIC_WALL_FULL)
7056 if (IS_FREE(x, y + 1))
7058 InitMovingField(x, y, MV_DOWN);
7059 started_moving = TRUE;
7061 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7062 Store[x][y] = EL_CHANGED(Store[x][y]);
7064 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7066 if (!MovDelay[x][y])
7067 MovDelay[x][y] = TILEY/4 + 1;
7076 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7077 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7078 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7082 else if (element == EL_BD_MAGIC_WALL_FULL)
7084 if (IS_FREE(x, y + 1))
7086 InitMovingField(x, y, MV_DOWN);
7087 started_moving = TRUE;
7089 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7090 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7092 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7094 if (!MovDelay[x][y])
7095 MovDelay[x][y] = TILEY/4 + 1;
7104 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7105 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7106 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7110 else if (element == EL_DC_MAGIC_WALL_FULL)
7112 if (IS_FREE(x, y + 1))
7114 InitMovingField(x, y, MV_DOWN);
7115 started_moving = TRUE;
7117 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7118 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7120 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7122 if (!MovDelay[x][y])
7123 MovDelay[x][y] = TILEY/4 + 1;
7132 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7133 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7134 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7138 else if ((CAN_PASS_MAGIC_WALL(element) &&
7139 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7140 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7141 (CAN_PASS_DC_MAGIC_WALL(element) &&
7142 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7145 InitMovingField(x, y, MV_DOWN);
7146 started_moving = TRUE;
7149 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7150 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7151 EL_DC_MAGIC_WALL_FILLING);
7152 Store[x][y] = element;
7154 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7156 SplashAcid(x, y + 1);
7158 InitMovingField(x, y, MV_DOWN);
7159 started_moving = TRUE;
7161 Store[x][y] = EL_ACID;
7164 #if USE_FIX_IMPACT_COLLISION
7165 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7166 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7168 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7169 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7171 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7172 CAN_FALL(element) && WasJustFalling[x][y] &&
7173 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7175 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7176 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7177 (Feld[x][y + 1] == EL_BLOCKED)))
7179 /* this is needed for a special case not covered by calling "Impact()"
7180 from "ContinueMoving()": if an element moves to a tile directly below
7181 another element which was just falling on that tile (which was empty
7182 in the previous frame), the falling element above would just stop
7183 instead of smashing the element below (in previous version, the above
7184 element was just checked for "moving" instead of "falling", resulting
7185 in incorrect smashes caused by horizontal movement of the above
7186 element; also, the case of the player being the element to smash was
7187 simply not covered here... :-/ ) */
7189 CheckCollision[x][y] = 0;
7190 CheckImpact[x][y] = 0;
7194 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7196 if (MovDir[x][y] == MV_NONE)
7198 InitMovingField(x, y, MV_DOWN);
7199 started_moving = TRUE;
7202 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7204 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7205 MovDir[x][y] = MV_DOWN;
7207 InitMovingField(x, y, MV_DOWN);
7208 started_moving = TRUE;
7210 else if (element == EL_AMOEBA_DROP)
7212 Feld[x][y] = EL_AMOEBA_GROWING;
7213 Store[x][y] = EL_AMOEBA_WET;
7215 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7216 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7217 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7218 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7220 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7221 (IS_FREE(x - 1, y + 1) ||
7222 Feld[x - 1][y + 1] == EL_ACID));
7223 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7224 (IS_FREE(x + 1, y + 1) ||
7225 Feld[x + 1][y + 1] == EL_ACID));
7226 boolean can_fall_any = (can_fall_left || can_fall_right);
7227 boolean can_fall_both = (can_fall_left && can_fall_right);
7228 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7230 #if USE_NEW_ALL_SLIPPERY
7231 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7233 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7234 can_fall_right = FALSE;
7235 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7236 can_fall_left = FALSE;
7237 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7238 can_fall_right = FALSE;
7239 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7240 can_fall_left = FALSE;
7242 can_fall_any = (can_fall_left || can_fall_right);
7243 can_fall_both = FALSE;
7246 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7248 if (slippery_type == SLIPPERY_ONLY_LEFT)
7249 can_fall_right = FALSE;
7250 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7251 can_fall_left = FALSE;
7252 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7253 can_fall_right = FALSE;
7254 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7255 can_fall_left = FALSE;
7257 can_fall_any = (can_fall_left || can_fall_right);
7258 can_fall_both = (can_fall_left && can_fall_right);
7262 #if USE_NEW_ALL_SLIPPERY
7264 #if USE_NEW_SP_SLIPPERY
7265 /* !!! better use the same properties as for custom elements here !!! */
7266 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7267 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7269 can_fall_right = FALSE; /* slip down on left side */
7270 can_fall_both = FALSE;
7275 #if USE_NEW_ALL_SLIPPERY
7278 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7279 can_fall_right = FALSE; /* slip down on left side */
7281 can_fall_left = !(can_fall_right = RND(2));
7283 can_fall_both = FALSE;
7288 if (game.emulation == EMU_BOULDERDASH ||
7289 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7290 can_fall_right = FALSE; /* slip down on left side */
7292 can_fall_left = !(can_fall_right = RND(2));
7294 can_fall_both = FALSE;
7300 /* if not determined otherwise, prefer left side for slipping down */
7301 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7302 started_moving = TRUE;
7306 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7308 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7311 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7312 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7313 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7314 int belt_dir = game.belt_dir[belt_nr];
7316 if ((belt_dir == MV_LEFT && left_is_free) ||
7317 (belt_dir == MV_RIGHT && right_is_free))
7319 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7321 InitMovingField(x, y, belt_dir);
7322 started_moving = TRUE;
7324 Pushed[x][y] = TRUE;
7325 Pushed[nextx][y] = TRUE;
7327 GfxAction[x][y] = ACTION_DEFAULT;
7331 MovDir[x][y] = 0; /* if element was moving, stop it */
7336 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7338 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7340 if (CAN_MOVE(element) && !started_moving)
7343 int move_pattern = element_info[element].move_pattern;
7348 if (MovDir[x][y] == MV_NONE)
7350 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7351 x, y, element, element_info[element].token_name);
7352 printf("StartMoving(): This should never happen!\n");
7357 Moving2Blocked(x, y, &newx, &newy);
7359 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7362 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7363 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7365 WasJustMoving[x][y] = 0;
7366 CheckCollision[x][y] = 0;
7368 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7370 if (Feld[x][y] != element) /* element has changed */
7374 if (!MovDelay[x][y]) /* start new movement phase */
7376 /* all objects that can change their move direction after each step
7377 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7379 if (element != EL_YAMYAM &&
7380 element != EL_DARK_YAMYAM &&
7381 element != EL_PACMAN &&
7382 !(move_pattern & MV_ANY_DIRECTION) &&
7383 move_pattern != MV_TURNING_LEFT &&
7384 move_pattern != MV_TURNING_RIGHT &&
7385 move_pattern != MV_TURNING_LEFT_RIGHT &&
7386 move_pattern != MV_TURNING_RIGHT_LEFT &&
7387 move_pattern != MV_TURNING_RANDOM)
7391 if (MovDelay[x][y] && (element == EL_BUG ||
7392 element == EL_SPACESHIP ||
7393 element == EL_SP_SNIKSNAK ||
7394 element == EL_SP_ELECTRON ||
7395 element == EL_MOLE))
7396 DrawLevelField(x, y);
7400 if (MovDelay[x][y]) /* wait some time before next movement */
7404 if (element == EL_ROBOT ||
7405 element == EL_YAMYAM ||
7406 element == EL_DARK_YAMYAM)
7408 DrawLevelElementAnimationIfNeeded(x, y, element);
7409 PlayLevelSoundAction(x, y, ACTION_WAITING);
7411 else if (element == EL_SP_ELECTRON)
7412 DrawLevelElementAnimationIfNeeded(x, y, element);
7413 else if (element == EL_DRAGON)
7416 int dir = MovDir[x][y];
7417 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7418 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7419 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7420 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7421 dir == MV_UP ? IMG_FLAMES_1_UP :
7422 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7423 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7425 GfxAction[x][y] = ACTION_ATTACKING;
7427 if (IS_PLAYER(x, y))
7428 DrawPlayerField(x, y);
7430 DrawLevelField(x, y);
7432 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7434 for (i = 1; i <= 3; i++)
7436 int xx = x + i * dx;
7437 int yy = y + i * dy;
7438 int sx = SCREENX(xx);
7439 int sy = SCREENY(yy);
7440 int flame_graphic = graphic + (i - 1);
7442 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7447 int flamed = MovingOrBlocked2Element(xx, yy);
7451 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7453 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7454 RemoveMovingField(xx, yy);
7456 RemoveField(xx, yy);
7458 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7461 RemoveMovingField(xx, yy);
7464 ChangeDelay[xx][yy] = 0;
7466 Feld[xx][yy] = EL_FLAMES;
7468 if (IN_SCR_FIELD(sx, sy))
7470 DrawLevelFieldCrumbledSand(xx, yy);
7471 DrawGraphic(sx, sy, flame_graphic, frame);
7476 if (Feld[xx][yy] == EL_FLAMES)
7477 Feld[xx][yy] = EL_EMPTY;
7478 DrawLevelField(xx, yy);
7483 if (MovDelay[x][y]) /* element still has to wait some time */
7485 PlayLevelSoundAction(x, y, ACTION_WAITING);
7491 /* now make next step */
7493 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7495 if (DONT_COLLIDE_WITH(element) &&
7496 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7497 !PLAYER_ENEMY_PROTECTED(newx, newy))
7499 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7504 else if (CAN_MOVE_INTO_ACID(element) &&
7505 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7506 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7507 (MovDir[x][y] == MV_DOWN ||
7508 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7510 SplashAcid(newx, newy);
7511 Store[x][y] = EL_ACID;
7513 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7515 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7516 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7517 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7518 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7521 DrawLevelField(x, y);
7523 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7524 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7525 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7527 local_player->friends_still_needed--;
7528 if (!local_player->friends_still_needed &&
7529 !local_player->GameOver && AllPlayersGone)
7530 PlayerWins(local_player);
7534 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7536 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7537 DrawLevelField(newx, newy);
7539 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7541 else if (!IS_FREE(newx, newy))
7543 GfxAction[x][y] = ACTION_WAITING;
7545 if (IS_PLAYER(x, y))
7546 DrawPlayerField(x, y);
7548 DrawLevelField(x, y);
7553 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7555 if (IS_FOOD_PIG(Feld[newx][newy]))
7557 if (IS_MOVING(newx, newy))
7558 RemoveMovingField(newx, newy);
7561 Feld[newx][newy] = EL_EMPTY;
7562 DrawLevelField(newx, newy);
7565 PlayLevelSound(x, y, SND_PIG_DIGGING);
7567 else if (!IS_FREE(newx, newy))
7569 if (IS_PLAYER(x, y))
7570 DrawPlayerField(x, y);
7572 DrawLevelField(x, y);
7577 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7579 if (Store[x][y] != EL_EMPTY)
7581 boolean can_clone = FALSE;
7584 /* check if element to clone is still there */
7585 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7587 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7595 /* cannot clone or target field not free anymore -- do not clone */
7596 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7597 Store[x][y] = EL_EMPTY;
7600 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7602 if (IS_MV_DIAGONAL(MovDir[x][y]))
7604 int diagonal_move_dir = MovDir[x][y];
7605 int stored = Store[x][y];
7606 int change_delay = 8;
7609 /* android is moving diagonally */
7611 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7613 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7614 GfxElement[x][y] = EL_EMC_ANDROID;
7615 GfxAction[x][y] = ACTION_SHRINKING;
7616 GfxDir[x][y] = diagonal_move_dir;
7617 ChangeDelay[x][y] = change_delay;
7619 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7622 DrawLevelGraphicAnimation(x, y, graphic);
7623 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7625 if (Feld[newx][newy] == EL_ACID)
7627 SplashAcid(newx, newy);
7632 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7634 Store[newx][newy] = EL_EMC_ANDROID;
7635 GfxElement[newx][newy] = EL_EMC_ANDROID;
7636 GfxAction[newx][newy] = ACTION_GROWING;
7637 GfxDir[newx][newy] = diagonal_move_dir;
7638 ChangeDelay[newx][newy] = change_delay;
7640 graphic = el_act_dir2img(GfxElement[newx][newy],
7641 GfxAction[newx][newy], GfxDir[newx][newy]);
7643 DrawLevelGraphicAnimation(newx, newy, graphic);
7644 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7650 Feld[newx][newy] = EL_EMPTY;
7651 DrawLevelField(newx, newy);
7653 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7656 else if (!IS_FREE(newx, newy))
7659 if (IS_PLAYER(x, y))
7660 DrawPlayerField(x, y);
7662 DrawLevelField(x, y);
7668 else if (IS_CUSTOM_ELEMENT(element) &&
7669 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7671 int new_element = Feld[newx][newy];
7673 if (!IS_FREE(newx, newy))
7675 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7676 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7679 /* no element can dig solid indestructible elements */
7680 if (IS_INDESTRUCTIBLE(new_element) &&
7681 !IS_DIGGABLE(new_element) &&
7682 !IS_COLLECTIBLE(new_element))
7685 if (AmoebaNr[newx][newy] &&
7686 (new_element == EL_AMOEBA_FULL ||
7687 new_element == EL_BD_AMOEBA ||
7688 new_element == EL_AMOEBA_GROWING))
7690 AmoebaCnt[AmoebaNr[newx][newy]]--;
7691 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7694 if (IS_MOVING(newx, newy))
7695 RemoveMovingField(newx, newy);
7698 RemoveField(newx, newy);
7699 DrawLevelField(newx, newy);
7702 /* if digged element was about to explode, prevent the explosion */
7703 ExplodeField[newx][newy] = EX_TYPE_NONE;
7705 PlayLevelSoundAction(x, y, action);
7708 Store[newx][newy] = EL_EMPTY;
7710 /* this makes it possible to leave the removed element again */
7711 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7712 Store[newx][newy] = new_element;
7714 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7716 int move_leave_element = element_info[element].move_leave_element;
7718 /* this makes it possible to leave the removed element again */
7719 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7720 new_element : move_leave_element);
7724 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7726 RunnerVisit[x][y] = FrameCounter;
7727 PlayerVisit[x][y] /= 8; /* expire player visit path */
7730 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7732 if (!IS_FREE(newx, newy))
7734 if (IS_PLAYER(x, y))
7735 DrawPlayerField(x, y);
7737 DrawLevelField(x, y);
7743 boolean wanna_flame = !RND(10);
7744 int dx = newx - x, dy = newy - y;
7745 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7746 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7747 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7748 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7749 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7750 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7753 IS_CLASSIC_ENEMY(element1) ||
7754 IS_CLASSIC_ENEMY(element2)) &&
7755 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7756 element1 != EL_FLAMES && element2 != EL_FLAMES)
7758 ResetGfxAnimation(x, y);
7759 GfxAction[x][y] = ACTION_ATTACKING;
7761 if (IS_PLAYER(x, y))
7762 DrawPlayerField(x, y);
7764 DrawLevelField(x, y);
7766 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7768 MovDelay[x][y] = 50;
7772 RemoveField(newx, newy);
7774 Feld[newx][newy] = EL_FLAMES;
7775 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7778 RemoveField(newx1, newy1);
7780 Feld[newx1][newy1] = EL_FLAMES;
7782 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7785 RemoveField(newx2, newy2);
7787 Feld[newx2][newy2] = EL_FLAMES;
7794 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7795 Feld[newx][newy] == EL_DIAMOND)
7797 if (IS_MOVING(newx, newy))
7798 RemoveMovingField(newx, newy);
7801 Feld[newx][newy] = EL_EMPTY;
7802 DrawLevelField(newx, newy);
7805 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7807 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7808 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7810 if (AmoebaNr[newx][newy])
7812 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7813 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7814 Feld[newx][newy] == EL_BD_AMOEBA)
7815 AmoebaCnt[AmoebaNr[newx][newy]]--;
7820 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7822 RemoveMovingField(newx, newy);
7825 if (IS_MOVING(newx, newy))
7827 RemoveMovingField(newx, newy);
7832 Feld[newx][newy] = EL_EMPTY;
7833 DrawLevelField(newx, newy);
7836 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7838 else if ((element == EL_PACMAN || element == EL_MOLE)
7839 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7841 if (AmoebaNr[newx][newy])
7843 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7844 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7845 Feld[newx][newy] == EL_BD_AMOEBA)
7846 AmoebaCnt[AmoebaNr[newx][newy]]--;
7849 if (element == EL_MOLE)
7851 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7852 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7854 ResetGfxAnimation(x, y);
7855 GfxAction[x][y] = ACTION_DIGGING;
7856 DrawLevelField(x, y);
7858 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7860 return; /* wait for shrinking amoeba */
7862 else /* element == EL_PACMAN */
7864 Feld[newx][newy] = EL_EMPTY;
7865 DrawLevelField(newx, newy);
7866 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7869 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7870 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7871 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7873 /* wait for shrinking amoeba to completely disappear */
7876 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7878 /* object was running against a wall */
7883 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7884 if (move_pattern & MV_ANY_DIRECTION &&
7885 move_pattern == MovDir[x][y])
7887 int blocking_element =
7888 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7890 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7893 element = Feld[x][y]; /* element might have changed */
7897 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7898 DrawLevelElementAnimation(x, y, element);
7900 if (DONT_TOUCH(element))
7901 TestIfBadThingTouchesPlayer(x, y);
7906 InitMovingField(x, y, MovDir[x][y]);
7908 PlayLevelSoundAction(x, y, ACTION_MOVING);
7912 ContinueMoving(x, y);
7915 void ContinueMoving(int x, int y)
7917 int element = Feld[x][y];
7918 struct ElementInfo *ei = &element_info[element];
7919 int direction = MovDir[x][y];
7920 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7921 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7922 int newx = x + dx, newy = y + dy;
7923 int stored = Store[x][y];
7924 int stored_new = Store[newx][newy];
7925 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7926 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7927 boolean last_line = (newy == lev_fieldy - 1);
7929 MovPos[x][y] += getElementMoveStepsize(x, y);
7931 if (pushed_by_player) /* special case: moving object pushed by player */
7932 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7934 if (ABS(MovPos[x][y]) < TILEX)
7937 int ee = Feld[x][y];
7938 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7939 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7941 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7942 x, y, ABS(MovPos[x][y]),
7944 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7947 DrawLevelField(x, y);
7949 return; /* element is still moving */
7952 /* element reached destination field */
7954 Feld[x][y] = EL_EMPTY;
7955 Feld[newx][newy] = element;
7956 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7958 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7960 element = Feld[newx][newy] = EL_ACID;
7962 else if (element == EL_MOLE)
7964 Feld[x][y] = EL_SAND;
7966 DrawLevelFieldCrumbledSandNeighbours(x, y);
7968 else if (element == EL_QUICKSAND_FILLING)
7970 element = Feld[newx][newy] = get_next_element(element);
7971 Store[newx][newy] = Store[x][y];
7973 else if (element == EL_QUICKSAND_EMPTYING)
7975 Feld[x][y] = get_next_element(element);
7976 element = Feld[newx][newy] = Store[x][y];
7978 else if (element == EL_QUICKSAND_FAST_FILLING)
7980 element = Feld[newx][newy] = get_next_element(element);
7981 Store[newx][newy] = Store[x][y];
7983 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7985 Feld[x][y] = get_next_element(element);
7986 element = Feld[newx][newy] = Store[x][y];
7988 else if (element == EL_MAGIC_WALL_FILLING)
7990 element = Feld[newx][newy] = get_next_element(element);
7991 if (!game.magic_wall_active)
7992 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7993 Store[newx][newy] = Store[x][y];
7995 else if (element == EL_MAGIC_WALL_EMPTYING)
7997 Feld[x][y] = get_next_element(element);
7998 if (!game.magic_wall_active)
7999 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8000 element = Feld[newx][newy] = Store[x][y];
8002 #if USE_NEW_CUSTOM_VALUE
8003 InitField(newx, newy, FALSE);
8006 else if (element == EL_BD_MAGIC_WALL_FILLING)
8008 element = Feld[newx][newy] = get_next_element(element);
8009 if (!game.magic_wall_active)
8010 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8011 Store[newx][newy] = Store[x][y];
8013 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8015 Feld[x][y] = get_next_element(element);
8016 if (!game.magic_wall_active)
8017 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8018 element = Feld[newx][newy] = Store[x][y];
8020 #if USE_NEW_CUSTOM_VALUE
8021 InitField(newx, newy, FALSE);
8024 else if (element == EL_DC_MAGIC_WALL_FILLING)
8026 element = Feld[newx][newy] = get_next_element(element);
8027 if (!game.magic_wall_active)
8028 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8029 Store[newx][newy] = Store[x][y];
8031 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8033 Feld[x][y] = get_next_element(element);
8034 if (!game.magic_wall_active)
8035 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8036 element = Feld[newx][newy] = Store[x][y];
8038 #if USE_NEW_CUSTOM_VALUE
8039 InitField(newx, newy, FALSE);
8042 else if (element == EL_AMOEBA_DROPPING)
8044 Feld[x][y] = get_next_element(element);
8045 element = Feld[newx][newy] = Store[x][y];
8047 else if (element == EL_SOKOBAN_OBJECT)
8050 Feld[x][y] = Back[x][y];
8052 if (Back[newx][newy])
8053 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8055 Back[x][y] = Back[newx][newy] = 0;
8058 Store[x][y] = EL_EMPTY;
8063 MovDelay[newx][newy] = 0;
8065 if (CAN_CHANGE_OR_HAS_ACTION(element))
8067 /* copy element change control values to new field */
8068 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8069 ChangePage[newx][newy] = ChangePage[x][y];
8070 ChangeCount[newx][newy] = ChangeCount[x][y];
8071 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8074 #if USE_NEW_CUSTOM_VALUE
8075 CustomValue[newx][newy] = CustomValue[x][y];
8078 ChangeDelay[x][y] = 0;
8079 ChangePage[x][y] = -1;
8080 ChangeCount[x][y] = 0;
8081 ChangeEvent[x][y] = -1;
8083 #if USE_NEW_CUSTOM_VALUE
8084 CustomValue[x][y] = 0;
8087 /* copy animation control values to new field */
8088 GfxFrame[newx][newy] = GfxFrame[x][y];
8089 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8090 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8091 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8093 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8095 /* some elements can leave other elements behind after moving */
8097 if (ei->move_leave_element != EL_EMPTY &&
8098 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8099 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8101 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8102 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8103 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8106 int move_leave_element = ei->move_leave_element;
8110 /* this makes it possible to leave the removed element again */
8111 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8112 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8114 /* this makes it possible to leave the removed element again */
8115 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8116 move_leave_element = stored;
8119 /* this makes it possible to leave the removed element again */
8120 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8121 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8122 move_leave_element = stored;
8125 Feld[x][y] = move_leave_element;
8127 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8128 MovDir[x][y] = direction;
8130 InitField(x, y, FALSE);
8132 if (GFX_CRUMBLED(Feld[x][y]))
8133 DrawLevelFieldCrumbledSandNeighbours(x, y);
8135 if (ELEM_IS_PLAYER(move_leave_element))
8136 RelocatePlayer(x, y, move_leave_element);
8139 /* do this after checking for left-behind element */
8140 ResetGfxAnimation(x, y); /* reset animation values for old field */
8142 if (!CAN_MOVE(element) ||
8143 (CAN_FALL(element) && direction == MV_DOWN &&
8144 (element == EL_SPRING ||
8145 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8146 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8147 GfxDir[x][y] = MovDir[newx][newy] = 0;
8149 DrawLevelField(x, y);
8150 DrawLevelField(newx, newy);
8152 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8154 /* prevent pushed element from moving on in pushed direction */
8155 if (pushed_by_player && CAN_MOVE(element) &&
8156 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8157 !(element_info[element].move_pattern & direction))
8158 TurnRound(newx, newy);
8160 /* prevent elements on conveyor belt from moving on in last direction */
8161 if (pushed_by_conveyor && CAN_FALL(element) &&
8162 direction & MV_HORIZONTAL)
8163 MovDir[newx][newy] = 0;
8165 if (!pushed_by_player)
8167 int nextx = newx + dx, nexty = newy + dy;
8168 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8170 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8172 if (CAN_FALL(element) && direction == MV_DOWN)
8173 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8175 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8176 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8178 #if USE_FIX_IMPACT_COLLISION
8179 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8180 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8184 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8186 TestIfBadThingTouchesPlayer(newx, newy);
8187 TestIfBadThingTouchesFriend(newx, newy);
8189 if (!IS_CUSTOM_ELEMENT(element))
8190 TestIfBadThingTouchesOtherBadThing(newx, newy);
8192 else if (element == EL_PENGUIN)
8193 TestIfFriendTouchesBadThing(newx, newy);
8195 /* give the player one last chance (one more frame) to move away */
8196 if (CAN_FALL(element) && direction == MV_DOWN &&
8197 (last_line || (!IS_FREE(x, newy + 1) &&
8198 (!IS_PLAYER(x, newy + 1) ||
8199 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8202 if (pushed_by_player && !game.use_change_when_pushing_bug)
8204 int push_side = MV_DIR_OPPOSITE(direction);
8205 struct PlayerInfo *player = PLAYERINFO(x, y);
8207 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8208 player->index_bit, push_side);
8209 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8210 player->index_bit, push_side);
8213 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8214 MovDelay[newx][newy] = 1;
8216 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8218 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8221 if (ChangePage[newx][newy] != -1) /* delayed change */
8223 int page = ChangePage[newx][newy];
8224 struct ElementChangeInfo *change = &ei->change_page[page];
8226 ChangePage[newx][newy] = -1;
8228 if (change->can_change)
8230 if (ChangeElement(newx, newy, element, page))
8232 if (change->post_change_function)
8233 change->post_change_function(newx, newy);
8237 if (change->has_action)
8238 ExecuteCustomElementAction(newx, newy, element, page);
8242 TestIfElementHitsCustomElement(newx, newy, direction);
8243 TestIfPlayerTouchesCustomElement(newx, newy);
8244 TestIfElementTouchesCustomElement(newx, newy);
8246 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8247 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8248 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8249 MV_DIR_OPPOSITE(direction));
8252 int AmoebeNachbarNr(int ax, int ay)
8255 int element = Feld[ax][ay];
8257 static int xy[4][2] =
8265 for (i = 0; i < NUM_DIRECTIONS; i++)
8267 int x = ax + xy[i][0];
8268 int y = ay + xy[i][1];
8270 if (!IN_LEV_FIELD(x, y))
8273 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8274 group_nr = AmoebaNr[x][y];
8280 void AmoebenVereinigen(int ax, int ay)
8282 int i, x, y, xx, yy;
8283 int new_group_nr = AmoebaNr[ax][ay];
8284 static int xy[4][2] =
8292 if (new_group_nr == 0)
8295 for (i = 0; i < NUM_DIRECTIONS; i++)
8300 if (!IN_LEV_FIELD(x, y))
8303 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8304 Feld[x][y] == EL_BD_AMOEBA ||
8305 Feld[x][y] == EL_AMOEBA_DEAD) &&
8306 AmoebaNr[x][y] != new_group_nr)
8308 int old_group_nr = AmoebaNr[x][y];
8310 if (old_group_nr == 0)
8313 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8314 AmoebaCnt[old_group_nr] = 0;
8315 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8316 AmoebaCnt2[old_group_nr] = 0;
8318 SCAN_PLAYFIELD(xx, yy)
8320 if (AmoebaNr[xx][yy] == old_group_nr)
8321 AmoebaNr[xx][yy] = new_group_nr;
8327 void AmoebeUmwandeln(int ax, int ay)
8331 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8333 int group_nr = AmoebaNr[ax][ay];
8338 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8339 printf("AmoebeUmwandeln(): This should never happen!\n");
8344 SCAN_PLAYFIELD(x, y)
8346 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8349 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8353 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8354 SND_AMOEBA_TURNING_TO_GEM :
8355 SND_AMOEBA_TURNING_TO_ROCK));
8360 static int xy[4][2] =
8368 for (i = 0; i < NUM_DIRECTIONS; i++)
8373 if (!IN_LEV_FIELD(x, y))
8376 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8378 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8379 SND_AMOEBA_TURNING_TO_GEM :
8380 SND_AMOEBA_TURNING_TO_ROCK));
8387 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8390 int group_nr = AmoebaNr[ax][ay];
8391 boolean done = FALSE;
8396 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8397 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8402 SCAN_PLAYFIELD(x, y)
8404 if (AmoebaNr[x][y] == group_nr &&
8405 (Feld[x][y] == EL_AMOEBA_DEAD ||
8406 Feld[x][y] == EL_BD_AMOEBA ||
8407 Feld[x][y] == EL_AMOEBA_GROWING))
8410 Feld[x][y] = new_element;
8411 InitField(x, y, FALSE);
8412 DrawLevelField(x, y);
8418 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8419 SND_BD_AMOEBA_TURNING_TO_ROCK :
8420 SND_BD_AMOEBA_TURNING_TO_GEM));
8423 void AmoebeWaechst(int x, int y)
8425 static unsigned long sound_delay = 0;
8426 static unsigned long sound_delay_value = 0;
8428 if (!MovDelay[x][y]) /* start new growing cycle */
8432 if (DelayReached(&sound_delay, sound_delay_value))
8434 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8435 sound_delay_value = 30;
8439 if (MovDelay[x][y]) /* wait some time before growing bigger */
8442 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8444 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8445 6 - MovDelay[x][y]);
8447 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8450 if (!MovDelay[x][y])
8452 Feld[x][y] = Store[x][y];
8454 DrawLevelField(x, y);
8459 void AmoebaDisappearing(int x, int y)
8461 static unsigned long sound_delay = 0;
8462 static unsigned long sound_delay_value = 0;
8464 if (!MovDelay[x][y]) /* start new shrinking cycle */
8468 if (DelayReached(&sound_delay, sound_delay_value))
8469 sound_delay_value = 30;
8472 if (MovDelay[x][y]) /* wait some time before shrinking */
8475 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8477 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8478 6 - MovDelay[x][y]);
8480 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8483 if (!MovDelay[x][y])
8485 Feld[x][y] = EL_EMPTY;
8486 DrawLevelField(x, y);
8488 /* don't let mole enter this field in this cycle;
8489 (give priority to objects falling to this field from above) */
8495 void AmoebeAbleger(int ax, int ay)
8498 int element = Feld[ax][ay];
8499 int graphic = el2img(element);
8500 int newax = ax, neway = ay;
8501 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8502 static int xy[4][2] =
8510 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8512 Feld[ax][ay] = EL_AMOEBA_DEAD;
8513 DrawLevelField(ax, ay);
8517 if (IS_ANIMATED(graphic))
8518 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8520 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8521 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8523 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8526 if (MovDelay[ax][ay])
8530 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8533 int x = ax + xy[start][0];
8534 int y = ay + xy[start][1];
8536 if (!IN_LEV_FIELD(x, y))
8539 if (IS_FREE(x, y) ||
8540 CAN_GROW_INTO(Feld[x][y]) ||
8541 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8542 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8548 if (newax == ax && neway == ay)
8551 else /* normal or "filled" (BD style) amoeba */
8554 boolean waiting_for_player = FALSE;
8556 for (i = 0; i < NUM_DIRECTIONS; i++)
8558 int j = (start + i) % 4;
8559 int x = ax + xy[j][0];
8560 int y = ay + xy[j][1];
8562 if (!IN_LEV_FIELD(x, y))
8565 if (IS_FREE(x, y) ||
8566 CAN_GROW_INTO(Feld[x][y]) ||
8567 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8568 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8574 else if (IS_PLAYER(x, y))
8575 waiting_for_player = TRUE;
8578 if (newax == ax && neway == ay) /* amoeba cannot grow */
8580 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8582 Feld[ax][ay] = EL_AMOEBA_DEAD;
8583 DrawLevelField(ax, ay);
8584 AmoebaCnt[AmoebaNr[ax][ay]]--;
8586 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8588 if (element == EL_AMOEBA_FULL)
8589 AmoebeUmwandeln(ax, ay);
8590 else if (element == EL_BD_AMOEBA)
8591 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8596 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8598 /* amoeba gets larger by growing in some direction */
8600 int new_group_nr = AmoebaNr[ax][ay];
8603 if (new_group_nr == 0)
8605 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8606 printf("AmoebeAbleger(): This should never happen!\n");
8611 AmoebaNr[newax][neway] = new_group_nr;
8612 AmoebaCnt[new_group_nr]++;
8613 AmoebaCnt2[new_group_nr]++;
8615 /* if amoeba touches other amoeba(s) after growing, unify them */
8616 AmoebenVereinigen(newax, neway);
8618 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8620 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8626 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8627 (neway == lev_fieldy - 1 && newax != ax))
8629 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8630 Store[newax][neway] = element;
8632 else if (neway == ay || element == EL_EMC_DRIPPER)
8634 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8636 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8640 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8641 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8642 Store[ax][ay] = EL_AMOEBA_DROP;
8643 ContinueMoving(ax, ay);
8647 DrawLevelField(newax, neway);
8650 void Life(int ax, int ay)
8654 int element = Feld[ax][ay];
8655 int graphic = el2img(element);
8656 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8658 boolean changed = FALSE;
8660 if (IS_ANIMATED(graphic))
8661 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8666 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8667 MovDelay[ax][ay] = life_time;
8669 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8672 if (MovDelay[ax][ay])
8676 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8678 int xx = ax+x1, yy = ay+y1;
8681 if (!IN_LEV_FIELD(xx, yy))
8684 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8686 int x = xx+x2, y = yy+y2;
8688 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8691 if (((Feld[x][y] == element ||
8692 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8694 (IS_FREE(x, y) && Stop[x][y]))
8698 if (xx == ax && yy == ay) /* field in the middle */
8700 if (nachbarn < life_parameter[0] ||
8701 nachbarn > life_parameter[1])
8703 Feld[xx][yy] = EL_EMPTY;
8705 DrawLevelField(xx, yy);
8706 Stop[xx][yy] = TRUE;
8710 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8711 { /* free border field */
8712 if (nachbarn >= life_parameter[2] &&
8713 nachbarn <= life_parameter[3])
8715 Feld[xx][yy] = element;
8716 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8718 DrawLevelField(xx, yy);
8719 Stop[xx][yy] = TRUE;
8726 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8727 SND_GAME_OF_LIFE_GROWING);
8730 static void InitRobotWheel(int x, int y)
8732 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8735 static void RunRobotWheel(int x, int y)
8737 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8740 static void StopRobotWheel(int x, int y)
8742 if (ZX == x && ZY == y)
8746 static void InitTimegateWheel(int x, int y)
8748 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8751 static void RunTimegateWheel(int x, int y)
8753 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8756 static void InitMagicBallDelay(int x, int y)
8759 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8761 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8765 static void ActivateMagicBall(int bx, int by)
8769 if (level.ball_random)
8771 int pos_border = RND(8); /* select one of the eight border elements */
8772 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8773 int xx = pos_content % 3;
8774 int yy = pos_content / 3;
8779 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8780 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8784 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8786 int xx = x - bx + 1;
8787 int yy = y - by + 1;
8789 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8790 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8794 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8797 void CheckExit(int x, int y)
8799 if (local_player->gems_still_needed > 0 ||
8800 local_player->sokobanfields_still_needed > 0 ||
8801 local_player->lights_still_needed > 0)
8803 int element = Feld[x][y];
8804 int graphic = el2img(element);
8806 if (IS_ANIMATED(graphic))
8807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8812 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8815 Feld[x][y] = EL_EXIT_OPENING;
8817 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8820 void CheckExitEM(int x, int y)
8822 if (local_player->gems_still_needed > 0 ||
8823 local_player->sokobanfields_still_needed > 0 ||
8824 local_player->lights_still_needed > 0)
8826 int element = Feld[x][y];
8827 int graphic = el2img(element);
8829 if (IS_ANIMATED(graphic))
8830 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8835 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8838 Feld[x][y] = EL_EM_EXIT_OPENING;
8840 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8843 void CheckExitSteel(int x, int y)
8845 if (local_player->gems_still_needed > 0 ||
8846 local_player->sokobanfields_still_needed > 0 ||
8847 local_player->lights_still_needed > 0)
8849 int element = Feld[x][y];
8850 int graphic = el2img(element);
8852 if (IS_ANIMATED(graphic))
8853 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8858 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8861 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8863 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8866 void CheckExitSteelEM(int x, int y)
8868 if (local_player->gems_still_needed > 0 ||
8869 local_player->sokobanfields_still_needed > 0 ||
8870 local_player->lights_still_needed > 0)
8872 int element = Feld[x][y];
8873 int graphic = el2img(element);
8875 if (IS_ANIMATED(graphic))
8876 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8881 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8884 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8886 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8889 void CheckExitSP(int x, int y)
8891 if (local_player->gems_still_needed > 0)
8893 int element = Feld[x][y];
8894 int graphic = el2img(element);
8896 if (IS_ANIMATED(graphic))
8897 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8902 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8905 Feld[x][y] = EL_SP_EXIT_OPENING;
8907 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8910 static void CloseAllOpenTimegates()
8914 SCAN_PLAYFIELD(x, y)
8916 int element = Feld[x][y];
8918 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8920 Feld[x][y] = EL_TIMEGATE_CLOSING;
8922 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8927 void DrawTwinkleOnField(int x, int y)
8929 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8932 if (Feld[x][y] == EL_BD_DIAMOND)
8935 if (MovDelay[x][y] == 0) /* next animation frame */
8936 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8938 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8942 if (setup.direct_draw && MovDelay[x][y])
8943 SetDrawtoField(DRAW_BUFFERED);
8945 DrawLevelElementAnimation(x, y, Feld[x][y]);
8947 if (MovDelay[x][y] != 0)
8949 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8950 10 - MovDelay[x][y]);
8952 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8954 if (setup.direct_draw)
8958 dest_x = FX + SCREENX(x) * TILEX;
8959 dest_y = FY + SCREENY(y) * TILEY;
8961 BlitBitmap(drawto_field, window,
8962 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8963 SetDrawtoField(DRAW_DIRECT);
8969 void MauerWaechst(int x, int y)
8973 if (!MovDelay[x][y]) /* next animation frame */
8974 MovDelay[x][y] = 3 * delay;
8976 if (MovDelay[x][y]) /* wait some time before next frame */
8980 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8982 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8983 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8985 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8988 if (!MovDelay[x][y])
8990 if (MovDir[x][y] == MV_LEFT)
8992 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8993 DrawLevelField(x - 1, y);
8995 else if (MovDir[x][y] == MV_RIGHT)
8997 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8998 DrawLevelField(x + 1, y);
9000 else if (MovDir[x][y] == MV_UP)
9002 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9003 DrawLevelField(x, y - 1);
9007 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9008 DrawLevelField(x, y + 1);
9011 Feld[x][y] = Store[x][y];
9013 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9014 DrawLevelField(x, y);
9019 void MauerAbleger(int ax, int ay)
9021 int element = Feld[ax][ay];
9022 int graphic = el2img(element);
9023 boolean oben_frei = FALSE, unten_frei = FALSE;
9024 boolean links_frei = FALSE, rechts_frei = FALSE;
9025 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9026 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9027 boolean new_wall = FALSE;
9029 if (IS_ANIMATED(graphic))
9030 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9032 if (!MovDelay[ax][ay]) /* start building new wall */
9033 MovDelay[ax][ay] = 6;
9035 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9038 if (MovDelay[ax][ay])
9042 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9044 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9046 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9048 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9051 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9052 element == EL_EXPANDABLE_WALL_ANY)
9056 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9057 Store[ax][ay-1] = element;
9058 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9059 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9060 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9061 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9066 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9067 Store[ax][ay+1] = element;
9068 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9069 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9070 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9071 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9076 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9077 element == EL_EXPANDABLE_WALL_ANY ||
9078 element == EL_EXPANDABLE_WALL ||
9079 element == EL_BD_EXPANDABLE_WALL)
9083 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9084 Store[ax-1][ay] = element;
9085 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9086 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9087 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9088 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9094 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9095 Store[ax+1][ay] = element;
9096 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9097 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9098 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9099 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9104 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9105 DrawLevelField(ax, ay);
9107 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9109 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9110 unten_massiv = TRUE;
9111 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9112 links_massiv = TRUE;
9113 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9114 rechts_massiv = TRUE;
9116 if (((oben_massiv && unten_massiv) ||
9117 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9118 element == EL_EXPANDABLE_WALL) &&
9119 ((links_massiv && rechts_massiv) ||
9120 element == EL_EXPANDABLE_WALL_VERTICAL))
9121 Feld[ax][ay] = EL_WALL;
9124 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9127 void MauerAblegerStahl(int ax, int ay)
9129 int element = Feld[ax][ay];
9130 int graphic = el2img(element);
9131 boolean oben_frei = FALSE, unten_frei = FALSE;
9132 boolean links_frei = FALSE, rechts_frei = FALSE;
9133 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9134 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9135 boolean new_wall = FALSE;
9137 if (IS_ANIMATED(graphic))
9138 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9140 if (!MovDelay[ax][ay]) /* start building new wall */
9141 MovDelay[ax][ay] = 6;
9143 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9146 if (MovDelay[ax][ay])
9150 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9152 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9154 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9156 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9159 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9160 element == EL_EXPANDABLE_STEELWALL_ANY)
9164 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9165 Store[ax][ay-1] = element;
9166 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9167 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9168 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9169 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9174 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9175 Store[ax][ay+1] = element;
9176 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9177 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9178 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9179 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9184 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9185 element == EL_EXPANDABLE_STEELWALL_ANY)
9189 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9190 Store[ax-1][ay] = element;
9191 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9192 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9193 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9194 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9200 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9201 Store[ax+1][ay] = element;
9202 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9203 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9204 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9205 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9210 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9212 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9213 unten_massiv = TRUE;
9214 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9215 links_massiv = TRUE;
9216 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9217 rechts_massiv = TRUE;
9219 if (((oben_massiv && unten_massiv) ||
9220 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9221 ((links_massiv && rechts_massiv) ||
9222 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9223 Feld[ax][ay] = EL_WALL;
9226 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9229 void CheckForDragon(int x, int y)
9232 boolean dragon_found = FALSE;
9233 static int xy[4][2] =
9241 for (i = 0; i < NUM_DIRECTIONS; i++)
9243 for (j = 0; j < 4; j++)
9245 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9247 if (IN_LEV_FIELD(xx, yy) &&
9248 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9250 if (Feld[xx][yy] == EL_DRAGON)
9251 dragon_found = TRUE;
9260 for (i = 0; i < NUM_DIRECTIONS; i++)
9262 for (j = 0; j < 3; j++)
9264 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9266 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9268 Feld[xx][yy] = EL_EMPTY;
9269 DrawLevelField(xx, yy);
9278 static void InitBuggyBase(int x, int y)
9280 int element = Feld[x][y];
9281 int activating_delay = FRAMES_PER_SECOND / 4;
9284 (element == EL_SP_BUGGY_BASE ?
9285 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9286 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9288 element == EL_SP_BUGGY_BASE_ACTIVE ?
9289 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9292 static void WarnBuggyBase(int x, int y)
9295 static int xy[4][2] =
9303 for (i = 0; i < NUM_DIRECTIONS; i++)
9305 int xx = x + xy[i][0];
9306 int yy = y + xy[i][1];
9308 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9310 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9317 static void InitTrap(int x, int y)
9319 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9322 static void ActivateTrap(int x, int y)
9324 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9327 static void ChangeActiveTrap(int x, int y)
9329 int graphic = IMG_TRAP_ACTIVE;
9331 /* if new animation frame was drawn, correct crumbled sand border */
9332 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9333 DrawLevelFieldCrumbledSand(x, y);
9336 static int getSpecialActionElement(int element, int number, int base_element)
9338 return (element != EL_EMPTY ? element :
9339 number != -1 ? base_element + number - 1 :
9343 static int getModifiedActionNumber(int value_old, int operator, int operand,
9344 int value_min, int value_max)
9346 int value_new = (operator == CA_MODE_SET ? operand :
9347 operator == CA_MODE_ADD ? value_old + operand :
9348 operator == CA_MODE_SUBTRACT ? value_old - operand :
9349 operator == CA_MODE_MULTIPLY ? value_old * operand :
9350 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9351 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9354 return (value_new < value_min ? value_min :
9355 value_new > value_max ? value_max :
9359 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9361 struct ElementInfo *ei = &element_info[element];
9362 struct ElementChangeInfo *change = &ei->change_page[page];
9363 int target_element = change->target_element;
9364 int action_type = change->action_type;
9365 int action_mode = change->action_mode;
9366 int action_arg = change->action_arg;
9369 if (!change->has_action)
9372 /* ---------- determine action paramater values -------------------------- */
9374 int level_time_value =
9375 (level.time > 0 ? TimeLeft :
9378 int action_arg_element =
9379 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9380 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9381 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9384 int action_arg_direction =
9385 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9386 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9387 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9388 change->actual_trigger_side :
9389 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9390 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9393 int action_arg_number_min =
9394 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9397 int action_arg_number_max =
9398 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9399 action_type == CA_SET_LEVEL_GEMS ? 999 :
9400 action_type == CA_SET_LEVEL_TIME ? 9999 :
9401 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9402 action_type == CA_SET_CE_VALUE ? 9999 :
9403 action_type == CA_SET_CE_SCORE ? 9999 :
9406 int action_arg_number_reset =
9407 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9408 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9409 action_type == CA_SET_LEVEL_TIME ? level.time :
9410 action_type == CA_SET_LEVEL_SCORE ? 0 :
9411 #if USE_NEW_CUSTOM_VALUE
9412 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9414 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9416 action_type == CA_SET_CE_SCORE ? 0 :
9419 int action_arg_number =
9420 (action_arg <= CA_ARG_MAX ? action_arg :
9421 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9422 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9423 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9424 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9425 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9426 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9427 #if USE_NEW_CUSTOM_VALUE
9428 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9430 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9432 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9433 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9434 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9435 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9436 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9437 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9438 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9439 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9440 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9441 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9442 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9445 int action_arg_number_old =
9446 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9447 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9448 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9449 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9450 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9453 int action_arg_number_new =
9454 getModifiedActionNumber(action_arg_number_old,
9455 action_mode, action_arg_number,
9456 action_arg_number_min, action_arg_number_max);
9458 int trigger_player_bits =
9459 (change->actual_trigger_player >= EL_PLAYER_1 &&
9460 change->actual_trigger_player <= EL_PLAYER_4 ?
9461 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9464 int action_arg_player_bits =
9465 (action_arg >= CA_ARG_PLAYER_1 &&
9466 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9467 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9470 /* ---------- execute action -------------------------------------------- */
9472 switch (action_type)
9479 /* ---------- level actions ------------------------------------------- */
9481 case CA_RESTART_LEVEL:
9483 game.restart_level = TRUE;
9488 case CA_SHOW_ENVELOPE:
9490 int element = getSpecialActionElement(action_arg_element,
9491 action_arg_number, EL_ENVELOPE_1);
9493 if (IS_ENVELOPE(element))
9494 local_player->show_envelope = element;
9499 case CA_SET_LEVEL_TIME:
9501 if (level.time > 0) /* only modify limited time value */
9503 TimeLeft = action_arg_number_new;
9506 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9508 DisplayGameControlValues();
9510 DrawGameValue_Time(TimeLeft);
9513 if (!TimeLeft && setup.time_limit)
9514 for (i = 0; i < MAX_PLAYERS; i++)
9515 KillPlayer(&stored_player[i]);
9521 case CA_SET_LEVEL_SCORE:
9523 local_player->score = action_arg_number_new;
9526 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9528 DisplayGameControlValues();
9530 DrawGameValue_Score(local_player->score);
9536 case CA_SET_LEVEL_GEMS:
9538 local_player->gems_still_needed = action_arg_number_new;
9541 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9543 DisplayGameControlValues();
9545 DrawGameValue_Emeralds(local_player->gems_still_needed);
9551 #if !USE_PLAYER_GRAVITY
9552 case CA_SET_LEVEL_GRAVITY:
9554 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9555 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9556 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9562 case CA_SET_LEVEL_WIND:
9564 game.wind_direction = action_arg_direction;
9569 /* ---------- player actions ------------------------------------------ */
9571 case CA_MOVE_PLAYER:
9573 /* automatically move to the next field in specified direction */
9574 for (i = 0; i < MAX_PLAYERS; i++)
9575 if (trigger_player_bits & (1 << i))
9576 stored_player[i].programmed_action = action_arg_direction;
9581 case CA_EXIT_PLAYER:
9583 for (i = 0; i < MAX_PLAYERS; i++)
9584 if (action_arg_player_bits & (1 << i))
9585 PlayerWins(&stored_player[i]);
9590 case CA_KILL_PLAYER:
9592 for (i = 0; i < MAX_PLAYERS; i++)
9593 if (action_arg_player_bits & (1 << i))
9594 KillPlayer(&stored_player[i]);
9599 case CA_SET_PLAYER_KEYS:
9601 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9602 int element = getSpecialActionElement(action_arg_element,
9603 action_arg_number, EL_KEY_1);
9605 if (IS_KEY(element))
9607 for (i = 0; i < MAX_PLAYERS; i++)
9609 if (trigger_player_bits & (1 << i))
9611 stored_player[i].key[KEY_NR(element)] = key_state;
9613 DrawGameDoorValues();
9621 case CA_SET_PLAYER_SPEED:
9623 for (i = 0; i < MAX_PLAYERS; i++)
9625 if (trigger_player_bits & (1 << i))
9627 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9629 if (action_arg == CA_ARG_SPEED_FASTER &&
9630 stored_player[i].cannot_move)
9632 action_arg_number = STEPSIZE_VERY_SLOW;
9634 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9635 action_arg == CA_ARG_SPEED_FASTER)
9637 action_arg_number = 2;
9638 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9641 else if (action_arg == CA_ARG_NUMBER_RESET)
9643 action_arg_number = level.initial_player_stepsize[i];
9647 getModifiedActionNumber(move_stepsize,
9650 action_arg_number_min,
9651 action_arg_number_max);
9653 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9660 case CA_SET_PLAYER_SHIELD:
9662 for (i = 0; i < MAX_PLAYERS; i++)
9664 if (trigger_player_bits & (1 << i))
9666 if (action_arg == CA_ARG_SHIELD_OFF)
9668 stored_player[i].shield_normal_time_left = 0;
9669 stored_player[i].shield_deadly_time_left = 0;
9671 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9673 stored_player[i].shield_normal_time_left = 999999;
9675 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9677 stored_player[i].shield_normal_time_left = 999999;
9678 stored_player[i].shield_deadly_time_left = 999999;
9686 #if USE_PLAYER_GRAVITY
9687 case CA_SET_PLAYER_GRAVITY:
9689 for (i = 0; i < MAX_PLAYERS; i++)
9691 if (trigger_player_bits & (1 << i))
9693 stored_player[i].gravity =
9694 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9695 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9696 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9697 stored_player[i].gravity);
9705 case CA_SET_PLAYER_ARTWORK:
9707 for (i = 0; i < MAX_PLAYERS; i++)
9709 if (trigger_player_bits & (1 << i))
9711 int artwork_element = action_arg_element;
9713 if (action_arg == CA_ARG_ELEMENT_RESET)
9715 (level.use_artwork_element[i] ? level.artwork_element[i] :
9716 stored_player[i].element_nr);
9718 #if USE_GFX_RESET_PLAYER_ARTWORK
9719 if (stored_player[i].artwork_element != artwork_element)
9720 stored_player[i].Frame = 0;
9723 stored_player[i].artwork_element = artwork_element;
9725 SetPlayerWaiting(&stored_player[i], FALSE);
9727 /* set number of special actions for bored and sleeping animation */
9728 stored_player[i].num_special_action_bored =
9729 get_num_special_action(artwork_element,
9730 ACTION_BORING_1, ACTION_BORING_LAST);
9731 stored_player[i].num_special_action_sleeping =
9732 get_num_special_action(artwork_element,
9733 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9740 /* ---------- CE actions ---------------------------------------------- */
9742 case CA_SET_CE_VALUE:
9744 #if USE_NEW_CUSTOM_VALUE
9745 int last_ce_value = CustomValue[x][y];
9747 CustomValue[x][y] = action_arg_number_new;
9749 if (CustomValue[x][y] != last_ce_value)
9751 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9752 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9754 if (CustomValue[x][y] == 0)
9756 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9757 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9765 case CA_SET_CE_SCORE:
9767 #if USE_NEW_CUSTOM_VALUE
9768 int last_ce_score = ei->collect_score;
9770 ei->collect_score = action_arg_number_new;
9772 if (ei->collect_score != last_ce_score)
9774 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9775 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9777 if (ei->collect_score == 0)
9781 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9782 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9785 This is a very special case that seems to be a mixture between
9786 CheckElementChange() and CheckTriggeredElementChange(): while
9787 the first one only affects single elements that are triggered
9788 directly, the second one affects multiple elements in the playfield
9789 that are triggered indirectly by another element. This is a third
9790 case: Changing the CE score always affects multiple identical CEs,
9791 so every affected CE must be checked, not only the single CE for
9792 which the CE score was changed in the first place (as every instance
9793 of that CE shares the same CE score, and therefore also can change)!
9795 SCAN_PLAYFIELD(xx, yy)
9797 if (Feld[xx][yy] == element)
9798 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9799 CE_SCORE_GETS_ZERO);
9808 /* ---------- engine actions ------------------------------------------ */
9810 case CA_SET_ENGINE_SCAN_MODE:
9812 InitPlayfieldScanMode(action_arg);
9822 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9824 int old_element = Feld[x][y];
9825 int new_element = GetElementFromGroupElement(element);
9826 int previous_move_direction = MovDir[x][y];
9827 #if USE_NEW_CUSTOM_VALUE
9828 int last_ce_value = CustomValue[x][y];
9830 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9831 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9832 boolean add_player_onto_element = (new_element_is_player &&
9833 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9834 /* this breaks SnakeBite when a snake is
9835 halfway through a door that closes */
9836 /* NOW FIXED AT LEVEL INIT IN files.c */
9837 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9839 IS_WALKABLE(old_element));
9842 /* check if element under the player changes from accessible to unaccessible
9843 (needed for special case of dropping element which then changes) */
9844 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9845 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9853 if (!add_player_onto_element)
9855 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9856 RemoveMovingField(x, y);
9860 Feld[x][y] = new_element;
9862 #if !USE_GFX_RESET_GFX_ANIMATION
9863 ResetGfxAnimation(x, y);
9864 ResetRandomAnimationValue(x, y);
9867 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9868 MovDir[x][y] = previous_move_direction;
9870 #if USE_NEW_CUSTOM_VALUE
9871 if (element_info[new_element].use_last_ce_value)
9872 CustomValue[x][y] = last_ce_value;
9875 InitField_WithBug1(x, y, FALSE);
9877 new_element = Feld[x][y]; /* element may have changed */
9879 #if USE_GFX_RESET_GFX_ANIMATION
9880 ResetGfxAnimation(x, y);
9881 ResetRandomAnimationValue(x, y);
9884 DrawLevelField(x, y);
9886 if (GFX_CRUMBLED(new_element))
9887 DrawLevelFieldCrumbledSandNeighbours(x, y);
9891 /* check if element under the player changes from accessible to unaccessible
9892 (needed for special case of dropping element which then changes) */
9893 /* (must be checked after creating new element for walkable group elements) */
9894 #if USE_FIX_KILLED_BY_NON_WALKABLE
9895 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9896 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9903 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9904 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9913 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9914 if (new_element_is_player)
9915 RelocatePlayer(x, y, new_element);
9918 ChangeCount[x][y]++; /* count number of changes in the same frame */
9920 TestIfBadThingTouchesPlayer(x, y);
9921 TestIfPlayerTouchesCustomElement(x, y);
9922 TestIfElementTouchesCustomElement(x, y);
9925 static void CreateField(int x, int y, int element)
9927 CreateFieldExt(x, y, element, FALSE);
9930 static void CreateElementFromChange(int x, int y, int element)
9932 element = GET_VALID_RUNTIME_ELEMENT(element);
9934 #if USE_STOP_CHANGED_ELEMENTS
9935 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9937 int old_element = Feld[x][y];
9939 /* prevent changed element from moving in same engine frame
9940 unless both old and new element can either fall or move */
9941 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9942 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9947 CreateFieldExt(x, y, element, TRUE);
9950 static boolean ChangeElement(int x, int y, int element, int page)
9952 struct ElementInfo *ei = &element_info[element];
9953 struct ElementChangeInfo *change = &ei->change_page[page];
9954 int ce_value = CustomValue[x][y];
9955 int ce_score = ei->collect_score;
9957 int old_element = Feld[x][y];
9959 /* always use default change event to prevent running into a loop */
9960 if (ChangeEvent[x][y] == -1)
9961 ChangeEvent[x][y] = CE_DELAY;
9963 if (ChangeEvent[x][y] == CE_DELAY)
9965 /* reset actual trigger element, trigger player and action element */
9966 change->actual_trigger_element = EL_EMPTY;
9967 change->actual_trigger_player = EL_PLAYER_1;
9968 change->actual_trigger_side = CH_SIDE_NONE;
9969 change->actual_trigger_ce_value = 0;
9970 change->actual_trigger_ce_score = 0;
9973 /* do not change elements more than a specified maximum number of changes */
9974 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9977 ChangeCount[x][y]++; /* count number of changes in the same frame */
9979 if (change->explode)
9986 if (change->use_target_content)
9988 boolean complete_replace = TRUE;
9989 boolean can_replace[3][3];
9992 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9995 boolean is_walkable;
9996 boolean is_diggable;
9997 boolean is_collectible;
9998 boolean is_removable;
9999 boolean is_destructible;
10000 int ex = x + xx - 1;
10001 int ey = y + yy - 1;
10002 int content_element = change->target_content.e[xx][yy];
10005 can_replace[xx][yy] = TRUE;
10007 if (ex == x && ey == y) /* do not check changing element itself */
10010 if (content_element == EL_EMPTY_SPACE)
10012 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10017 if (!IN_LEV_FIELD(ex, ey))
10019 can_replace[xx][yy] = FALSE;
10020 complete_replace = FALSE;
10027 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10028 e = MovingOrBlocked2Element(ex, ey);
10030 is_empty = (IS_FREE(ex, ey) ||
10031 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10033 is_walkable = (is_empty || IS_WALKABLE(e));
10034 is_diggable = (is_empty || IS_DIGGABLE(e));
10035 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10036 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10037 is_removable = (is_diggable || is_collectible);
10039 can_replace[xx][yy] =
10040 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10041 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10042 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10043 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10044 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10045 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10046 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10048 if (!can_replace[xx][yy])
10049 complete_replace = FALSE;
10052 if (!change->only_if_complete || complete_replace)
10054 boolean something_has_changed = FALSE;
10056 if (change->only_if_complete && change->use_random_replace &&
10057 RND(100) < change->random_percentage)
10060 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10062 int ex = x + xx - 1;
10063 int ey = y + yy - 1;
10064 int content_element;
10066 if (can_replace[xx][yy] && (!change->use_random_replace ||
10067 RND(100) < change->random_percentage))
10069 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10070 RemoveMovingField(ex, ey);
10072 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10074 content_element = change->target_content.e[xx][yy];
10075 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10076 ce_value, ce_score);
10078 CreateElementFromChange(ex, ey, target_element);
10080 something_has_changed = TRUE;
10082 /* for symmetry reasons, freeze newly created border elements */
10083 if (ex != x || ey != y)
10084 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10088 if (something_has_changed)
10090 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10091 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10097 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10098 ce_value, ce_score);
10100 if (element == EL_DIAGONAL_GROWING ||
10101 element == EL_DIAGONAL_SHRINKING)
10103 target_element = Store[x][y];
10105 Store[x][y] = EL_EMPTY;
10108 CreateElementFromChange(x, y, target_element);
10110 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10111 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10114 /* this uses direct change before indirect change */
10115 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10120 #if USE_NEW_DELAYED_ACTION
10122 static void HandleElementChange(int x, int y, int page)
10124 int element = MovingOrBlocked2Element(x, y);
10125 struct ElementInfo *ei = &element_info[element];
10126 struct ElementChangeInfo *change = &ei->change_page[page];
10129 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10130 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10133 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10134 x, y, element, element_info[element].token_name);
10135 printf("HandleElementChange(): This should never happen!\n");
10140 /* this can happen with classic bombs on walkable, changing elements */
10141 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10144 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10145 ChangeDelay[x][y] = 0;
10151 if (ChangeDelay[x][y] == 0) /* initialize element change */
10153 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10155 if (change->can_change)
10158 /* !!! not clear why graphic animation should be reset at all here !!! */
10159 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10160 #if USE_GFX_RESET_WHEN_NOT_MOVING
10161 /* when a custom element is about to change (for example by change delay),
10162 do not reset graphic animation when the custom element is moving */
10163 if (!IS_MOVING(x, y))
10166 ResetGfxAnimation(x, y);
10167 ResetRandomAnimationValue(x, y);
10171 if (change->pre_change_function)
10172 change->pre_change_function(x, y);
10176 ChangeDelay[x][y]--;
10178 if (ChangeDelay[x][y] != 0) /* continue element change */
10180 if (change->can_change)
10182 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10184 if (IS_ANIMATED(graphic))
10185 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10187 if (change->change_function)
10188 change->change_function(x, y);
10191 else /* finish element change */
10193 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10195 page = ChangePage[x][y];
10196 ChangePage[x][y] = -1;
10198 change = &ei->change_page[page];
10201 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10203 ChangeDelay[x][y] = 1; /* try change after next move step */
10204 ChangePage[x][y] = page; /* remember page to use for change */
10209 if (change->can_change)
10211 if (ChangeElement(x, y, element, page))
10213 if (change->post_change_function)
10214 change->post_change_function(x, y);
10218 if (change->has_action)
10219 ExecuteCustomElementAction(x, y, element, page);
10225 static void HandleElementChange(int x, int y, int page)
10227 int element = MovingOrBlocked2Element(x, y);
10228 struct ElementInfo *ei = &element_info[element];
10229 struct ElementChangeInfo *change = &ei->change_page[page];
10232 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10235 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10236 x, y, element, element_info[element].token_name);
10237 printf("HandleElementChange(): This should never happen!\n");
10242 /* this can happen with classic bombs on walkable, changing elements */
10243 if (!CAN_CHANGE(element))
10246 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10247 ChangeDelay[x][y] = 0;
10253 if (ChangeDelay[x][y] == 0) /* initialize element change */
10255 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10257 ResetGfxAnimation(x, y);
10258 ResetRandomAnimationValue(x, y);
10260 if (change->pre_change_function)
10261 change->pre_change_function(x, y);
10264 ChangeDelay[x][y]--;
10266 if (ChangeDelay[x][y] != 0) /* continue element change */
10268 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10270 if (IS_ANIMATED(graphic))
10271 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10273 if (change->change_function)
10274 change->change_function(x, y);
10276 else /* finish element change */
10278 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10280 page = ChangePage[x][y];
10281 ChangePage[x][y] = -1;
10283 change = &ei->change_page[page];
10286 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10288 ChangeDelay[x][y] = 1; /* try change after next move step */
10289 ChangePage[x][y] = page; /* remember page to use for change */
10294 if (ChangeElement(x, y, element, page))
10296 if (change->post_change_function)
10297 change->post_change_function(x, y);
10304 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10305 int trigger_element,
10307 int trigger_player,
10311 boolean change_done_any = FALSE;
10312 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10315 if (!(trigger_events[trigger_element][trigger_event]))
10319 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10320 trigger_event, recursion_loop_depth, recursion_loop_detected,
10321 recursion_loop_element, EL_NAME(recursion_loop_element));
10324 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10326 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10328 int element = EL_CUSTOM_START + i;
10329 boolean change_done = FALSE;
10332 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10333 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10336 for (p = 0; p < element_info[element].num_change_pages; p++)
10338 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10340 if (change->can_change_or_has_action &&
10341 change->has_event[trigger_event] &&
10342 change->trigger_side & trigger_side &&
10343 change->trigger_player & trigger_player &&
10344 change->trigger_page & trigger_page_bits &&
10345 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10347 change->actual_trigger_element = trigger_element;
10348 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10349 change->actual_trigger_side = trigger_side;
10350 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10351 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10353 if ((change->can_change && !change_done) || change->has_action)
10357 SCAN_PLAYFIELD(x, y)
10359 if (Feld[x][y] == element)
10361 if (change->can_change && !change_done)
10363 ChangeDelay[x][y] = 1;
10364 ChangeEvent[x][y] = trigger_event;
10366 HandleElementChange(x, y, p);
10368 #if USE_NEW_DELAYED_ACTION
10369 else if (change->has_action)
10371 ExecuteCustomElementAction(x, y, element, p);
10372 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10375 if (change->has_action)
10377 ExecuteCustomElementAction(x, y, element, p);
10378 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10384 if (change->can_change)
10386 change_done = TRUE;
10387 change_done_any = TRUE;
10394 RECURSION_LOOP_DETECTION_END();
10396 return change_done_any;
10399 static boolean CheckElementChangeExt(int x, int y,
10401 int trigger_element,
10403 int trigger_player,
10406 boolean change_done = FALSE;
10409 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10410 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10413 if (Feld[x][y] == EL_BLOCKED)
10415 Blocked2Moving(x, y, &x, &y);
10416 element = Feld[x][y];
10420 /* check if element has already changed */
10421 if (Feld[x][y] != element)
10424 /* check if element has already changed or is about to change after moving */
10425 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10426 Feld[x][y] != element) ||
10428 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10429 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10430 ChangePage[x][y] != -1)))
10435 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10436 trigger_event, recursion_loop_depth, recursion_loop_detected,
10437 recursion_loop_element, EL_NAME(recursion_loop_element));
10440 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10442 for (p = 0; p < element_info[element].num_change_pages; p++)
10444 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10446 /* check trigger element for all events where the element that is checked
10447 for changing interacts with a directly adjacent element -- this is
10448 different to element changes that affect other elements to change on the
10449 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10450 boolean check_trigger_element =
10451 (trigger_event == CE_TOUCHING_X ||
10452 trigger_event == CE_HITTING_X ||
10453 trigger_event == CE_HIT_BY_X ||
10455 /* this one was forgotten until 3.2.3 */
10456 trigger_event == CE_DIGGING_X);
10459 if (change->can_change_or_has_action &&
10460 change->has_event[trigger_event] &&
10461 change->trigger_side & trigger_side &&
10462 change->trigger_player & trigger_player &&
10463 (!check_trigger_element ||
10464 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10466 change->actual_trigger_element = trigger_element;
10467 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10468 change->actual_trigger_side = trigger_side;
10469 change->actual_trigger_ce_value = CustomValue[x][y];
10470 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10472 /* special case: trigger element not at (x,y) position for some events */
10473 if (check_trigger_element)
10485 { 0, 0 }, { 0, 0 }, { 0, 0 },
10489 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10490 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10492 change->actual_trigger_ce_value = CustomValue[xx][yy];
10493 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10496 if (change->can_change && !change_done)
10498 ChangeDelay[x][y] = 1;
10499 ChangeEvent[x][y] = trigger_event;
10501 HandleElementChange(x, y, p);
10503 change_done = TRUE;
10505 #if USE_NEW_DELAYED_ACTION
10506 else if (change->has_action)
10508 ExecuteCustomElementAction(x, y, element, p);
10509 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10512 if (change->has_action)
10514 ExecuteCustomElementAction(x, y, element, p);
10515 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10521 RECURSION_LOOP_DETECTION_END();
10523 return change_done;
10526 static void PlayPlayerSound(struct PlayerInfo *player)
10528 int jx = player->jx, jy = player->jy;
10529 int sound_element = player->artwork_element;
10530 int last_action = player->last_action_waiting;
10531 int action = player->action_waiting;
10533 if (player->is_waiting)
10535 if (action != last_action)
10536 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10538 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10542 if (action != last_action)
10543 StopSound(element_info[sound_element].sound[last_action]);
10545 if (last_action == ACTION_SLEEPING)
10546 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10550 static void PlayAllPlayersSound()
10554 for (i = 0; i < MAX_PLAYERS; i++)
10555 if (stored_player[i].active)
10556 PlayPlayerSound(&stored_player[i]);
10559 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10561 boolean last_waiting = player->is_waiting;
10562 int move_dir = player->MovDir;
10564 player->dir_waiting = move_dir;
10565 player->last_action_waiting = player->action_waiting;
10569 if (!last_waiting) /* not waiting -> waiting */
10571 player->is_waiting = TRUE;
10573 player->frame_counter_bored =
10575 game.player_boring_delay_fixed +
10576 GetSimpleRandom(game.player_boring_delay_random);
10577 player->frame_counter_sleeping =
10579 game.player_sleeping_delay_fixed +
10580 GetSimpleRandom(game.player_sleeping_delay_random);
10582 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10585 if (game.player_sleeping_delay_fixed +
10586 game.player_sleeping_delay_random > 0 &&
10587 player->anim_delay_counter == 0 &&
10588 player->post_delay_counter == 0 &&
10589 FrameCounter >= player->frame_counter_sleeping)
10590 player->is_sleeping = TRUE;
10591 else if (game.player_boring_delay_fixed +
10592 game.player_boring_delay_random > 0 &&
10593 FrameCounter >= player->frame_counter_bored)
10594 player->is_bored = TRUE;
10596 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10597 player->is_bored ? ACTION_BORING :
10600 if (player->is_sleeping && player->use_murphy)
10602 /* special case for sleeping Murphy when leaning against non-free tile */
10604 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10605 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10606 !IS_MOVING(player->jx - 1, player->jy)))
10607 move_dir = MV_LEFT;
10608 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10609 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10610 !IS_MOVING(player->jx + 1, player->jy)))
10611 move_dir = MV_RIGHT;
10613 player->is_sleeping = FALSE;
10615 player->dir_waiting = move_dir;
10618 if (player->is_sleeping)
10620 if (player->num_special_action_sleeping > 0)
10622 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10624 int last_special_action = player->special_action_sleeping;
10625 int num_special_action = player->num_special_action_sleeping;
10626 int special_action =
10627 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10628 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10629 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10630 last_special_action + 1 : ACTION_SLEEPING);
10631 int special_graphic =
10632 el_act_dir2img(player->artwork_element, special_action, move_dir);
10634 player->anim_delay_counter =
10635 graphic_info[special_graphic].anim_delay_fixed +
10636 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10637 player->post_delay_counter =
10638 graphic_info[special_graphic].post_delay_fixed +
10639 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10641 player->special_action_sleeping = special_action;
10644 if (player->anim_delay_counter > 0)
10646 player->action_waiting = player->special_action_sleeping;
10647 player->anim_delay_counter--;
10649 else if (player->post_delay_counter > 0)
10651 player->post_delay_counter--;
10655 else if (player->is_bored)
10657 if (player->num_special_action_bored > 0)
10659 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10661 int special_action =
10662 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10663 int special_graphic =
10664 el_act_dir2img(player->artwork_element, special_action, move_dir);
10666 player->anim_delay_counter =
10667 graphic_info[special_graphic].anim_delay_fixed +
10668 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10669 player->post_delay_counter =
10670 graphic_info[special_graphic].post_delay_fixed +
10671 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10673 player->special_action_bored = special_action;
10676 if (player->anim_delay_counter > 0)
10678 player->action_waiting = player->special_action_bored;
10679 player->anim_delay_counter--;
10681 else if (player->post_delay_counter > 0)
10683 player->post_delay_counter--;
10688 else if (last_waiting) /* waiting -> not waiting */
10690 player->is_waiting = FALSE;
10691 player->is_bored = FALSE;
10692 player->is_sleeping = FALSE;
10694 player->frame_counter_bored = -1;
10695 player->frame_counter_sleeping = -1;
10697 player->anim_delay_counter = 0;
10698 player->post_delay_counter = 0;
10700 player->dir_waiting = player->MovDir;
10701 player->action_waiting = ACTION_DEFAULT;
10703 player->special_action_bored = ACTION_DEFAULT;
10704 player->special_action_sleeping = ACTION_DEFAULT;
10708 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10710 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10711 int left = player_action & JOY_LEFT;
10712 int right = player_action & JOY_RIGHT;
10713 int up = player_action & JOY_UP;
10714 int down = player_action & JOY_DOWN;
10715 int button1 = player_action & JOY_BUTTON_1;
10716 int button2 = player_action & JOY_BUTTON_2;
10717 int dx = (left ? -1 : right ? 1 : 0);
10718 int dy = (up ? -1 : down ? 1 : 0);
10720 if (!player->active || tape.pausing)
10726 snapped = SnapField(player, dx, dy);
10730 dropped = DropElement(player);
10732 moved = MovePlayer(player, dx, dy);
10735 if (tape.single_step && tape.recording && !tape.pausing)
10737 if (button1 || (dropped && !moved))
10739 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10740 SnapField(player, 0, 0); /* stop snapping */
10744 SetPlayerWaiting(player, FALSE);
10746 return player_action;
10750 /* no actions for this player (no input at player's configured device) */
10752 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10753 SnapField(player, 0, 0);
10754 CheckGravityMovementWhenNotMoving(player);
10756 if (player->MovPos == 0)
10757 SetPlayerWaiting(player, TRUE);
10759 if (player->MovPos == 0) /* needed for tape.playing */
10760 player->is_moving = FALSE;
10762 player->is_dropping = FALSE;
10763 player->is_dropping_pressed = FALSE;
10764 player->drop_pressed_delay = 0;
10770 static void CheckLevelTime()
10774 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10776 if (level.native_em_level->lev->home == 0) /* all players at home */
10778 PlayerWins(local_player);
10780 AllPlayersGone = TRUE;
10782 level.native_em_level->lev->home = -1;
10785 if (level.native_em_level->ply[0]->alive == 0 &&
10786 level.native_em_level->ply[1]->alive == 0 &&
10787 level.native_em_level->ply[2]->alive == 0 &&
10788 level.native_em_level->ply[3]->alive == 0) /* all dead */
10789 AllPlayersGone = TRUE;
10792 if (TimeFrames >= FRAMES_PER_SECOND)
10797 for (i = 0; i < MAX_PLAYERS; i++)
10799 struct PlayerInfo *player = &stored_player[i];
10801 if (SHIELD_ON(player))
10803 player->shield_normal_time_left--;
10805 if (player->shield_deadly_time_left > 0)
10806 player->shield_deadly_time_left--;
10810 if (!local_player->LevelSolved && !level.use_step_counter)
10818 if (TimeLeft <= 10 && setup.time_limit)
10819 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10822 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10824 DisplayGameControlValues();
10826 DrawGameValue_Time(TimeLeft);
10829 if (!TimeLeft && setup.time_limit)
10831 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10832 level.native_em_level->lev->killed_out_of_time = TRUE;
10834 for (i = 0; i < MAX_PLAYERS; i++)
10835 KillPlayer(&stored_player[i]);
10839 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10841 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10843 DisplayGameControlValues();
10846 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10847 DrawGameValue_Time(TimePlayed);
10850 level.native_em_level->lev->time =
10851 (level.time == 0 ? TimePlayed : TimeLeft);
10854 if (tape.recording || tape.playing)
10855 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10858 DrawGameDoorValues();
10861 void AdvanceFrameAndPlayerCounters(int player_nr)
10865 /* advance frame counters (global frame counter and time frame counter) */
10869 /* advance player counters (counters for move delay, move animation etc.) */
10870 for (i = 0; i < MAX_PLAYERS; i++)
10872 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10873 int move_delay_value = stored_player[i].move_delay_value;
10874 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10876 if (!advance_player_counters) /* not all players may be affected */
10879 #if USE_NEW_PLAYER_ANIM
10880 if (move_frames == 0) /* less than one move per game frame */
10882 int stepsize = TILEX / move_delay_value;
10883 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10884 int count = (stored_player[i].is_moving ?
10885 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10887 if (count % delay == 0)
10892 stored_player[i].Frame += move_frames;
10894 if (stored_player[i].MovPos != 0)
10895 stored_player[i].StepFrame += move_frames;
10897 if (stored_player[i].move_delay > 0)
10898 stored_player[i].move_delay--;
10900 /* due to bugs in previous versions, counter must count up, not down */
10901 if (stored_player[i].push_delay != -1)
10902 stored_player[i].push_delay++;
10904 if (stored_player[i].drop_delay > 0)
10905 stored_player[i].drop_delay--;
10907 if (stored_player[i].is_dropping_pressed)
10908 stored_player[i].drop_pressed_delay++;
10912 void StartGameActions(boolean init_network_game, boolean record_tape,
10915 unsigned long new_random_seed = InitRND(random_seed);
10918 TapeStartRecording(new_random_seed);
10920 #if defined(NETWORK_AVALIABLE)
10921 if (init_network_game)
10923 SendToServer_StartPlaying();
10934 static unsigned long game_frame_delay = 0;
10935 unsigned long game_frame_delay_value;
10936 byte *recorded_player_action;
10937 byte summarized_player_action = 0;
10938 byte tape_action[MAX_PLAYERS];
10941 /* detect endless loops, caused by custom element programming */
10942 if (recursion_loop_detected && recursion_loop_depth == 0)
10944 char *message = getStringCat3("Internal Error ! Element ",
10945 EL_NAME(recursion_loop_element),
10946 " caused endless loop ! Quit the game ?");
10948 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10949 EL_NAME(recursion_loop_element));
10951 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10953 recursion_loop_detected = FALSE; /* if game should be continued */
10960 if (game.restart_level)
10961 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10963 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10965 if (level.native_em_level->lev->home == 0) /* all players at home */
10967 PlayerWins(local_player);
10969 AllPlayersGone = TRUE;
10971 level.native_em_level->lev->home = -1;
10974 if (level.native_em_level->ply[0]->alive == 0 &&
10975 level.native_em_level->ply[1]->alive == 0 &&
10976 level.native_em_level->ply[2]->alive == 0 &&
10977 level.native_em_level->ply[3]->alive == 0) /* all dead */
10978 AllPlayersGone = TRUE;
10981 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10984 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10987 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10990 game_frame_delay_value =
10991 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10993 if (tape.playing && tape.warp_forward && !tape.pausing)
10994 game_frame_delay_value = 0;
10996 /* ---------- main game synchronization point ---------- */
10998 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11000 if (network_playing && !network_player_action_received)
11002 /* try to get network player actions in time */
11004 #if defined(NETWORK_AVALIABLE)
11005 /* last chance to get network player actions without main loop delay */
11006 HandleNetworking();
11009 /* game was quit by network peer */
11010 if (game_status != GAME_MODE_PLAYING)
11013 if (!network_player_action_received)
11014 return; /* failed to get network player actions in time */
11016 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11022 /* at this point we know that we really continue executing the game */
11024 network_player_action_received = FALSE;
11026 /* when playing tape, read previously recorded player input from tape data */
11027 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11030 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11035 if (tape.set_centered_player)
11037 game.centered_player_nr_next = tape.centered_player_nr_next;
11038 game.set_centered_player = TRUE;
11041 for (i = 0; i < MAX_PLAYERS; i++)
11043 summarized_player_action |= stored_player[i].action;
11045 if (!network_playing)
11046 stored_player[i].effective_action = stored_player[i].action;
11049 #if defined(NETWORK_AVALIABLE)
11050 if (network_playing)
11051 SendToServer_MovePlayer(summarized_player_action);
11054 if (!options.network && !setup.team_mode)
11055 local_player->effective_action = summarized_player_action;
11057 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11059 for (i = 0; i < MAX_PLAYERS; i++)
11060 stored_player[i].effective_action =
11061 (i == game.centered_player_nr ? summarized_player_action : 0);
11064 if (recorded_player_action != NULL)
11065 for (i = 0; i < MAX_PLAYERS; i++)
11066 stored_player[i].effective_action = recorded_player_action[i];
11068 for (i = 0; i < MAX_PLAYERS; i++)
11070 tape_action[i] = stored_player[i].effective_action;
11072 /* (this can only happen in the R'n'D game engine) */
11073 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11074 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11077 /* only record actions from input devices, but not programmed actions */
11078 if (tape.recording)
11079 TapeRecordAction(tape_action);
11081 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11083 GameActions_EM_Main();
11091 void GameActions_EM_Main()
11093 byte effective_action[MAX_PLAYERS];
11094 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11097 for (i = 0; i < MAX_PLAYERS; i++)
11098 effective_action[i] = stored_player[i].effective_action;
11100 GameActions_EM(effective_action, warp_mode);
11104 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11107 void GameActions_RND()
11109 int magic_wall_x = 0, magic_wall_y = 0;
11110 int i, x, y, element, graphic;
11112 InitPlayfieldScanModeVars();
11114 #if USE_ONE_MORE_CHANGE_PER_FRAME
11115 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11117 SCAN_PLAYFIELD(x, y)
11119 ChangeCount[x][y] = 0;
11120 ChangeEvent[x][y] = -1;
11125 if (game.set_centered_player)
11127 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11129 /* switching to "all players" only possible if all players fit to screen */
11130 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11132 game.centered_player_nr_next = game.centered_player_nr;
11133 game.set_centered_player = FALSE;
11136 /* do not switch focus to non-existing (or non-active) player */
11137 if (game.centered_player_nr_next >= 0 &&
11138 !stored_player[game.centered_player_nr_next].active)
11140 game.centered_player_nr_next = game.centered_player_nr;
11141 game.set_centered_player = FALSE;
11145 if (game.set_centered_player &&
11146 ScreenMovPos == 0) /* screen currently aligned at tile position */
11150 if (game.centered_player_nr_next == -1)
11152 setScreenCenteredToAllPlayers(&sx, &sy);
11156 sx = stored_player[game.centered_player_nr_next].jx;
11157 sy = stored_player[game.centered_player_nr_next].jy;
11160 game.centered_player_nr = game.centered_player_nr_next;
11161 game.set_centered_player = FALSE;
11163 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11164 DrawGameDoorValues();
11167 for (i = 0; i < MAX_PLAYERS; i++)
11169 int actual_player_action = stored_player[i].effective_action;
11172 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11173 - rnd_equinox_tetrachloride 048
11174 - rnd_equinox_tetrachloride_ii 096
11175 - rnd_emanuel_schmieg 002
11176 - doctor_sloan_ww 001, 020
11178 if (stored_player[i].MovPos == 0)
11179 CheckGravityMovement(&stored_player[i]);
11182 /* overwrite programmed action with tape action */
11183 if (stored_player[i].programmed_action)
11184 actual_player_action = stored_player[i].programmed_action;
11186 PlayerActions(&stored_player[i], actual_player_action);
11188 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11191 ScrollScreen(NULL, SCROLL_GO_ON);
11193 /* for backwards compatibility, the following code emulates a fixed bug that
11194 occured when pushing elements (causing elements that just made their last
11195 pushing step to already (if possible) make their first falling step in the
11196 same game frame, which is bad); this code is also needed to use the famous
11197 "spring push bug" which is used in older levels and might be wanted to be
11198 used also in newer levels, but in this case the buggy pushing code is only
11199 affecting the "spring" element and no other elements */
11201 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11203 for (i = 0; i < MAX_PLAYERS; i++)
11205 struct PlayerInfo *player = &stored_player[i];
11206 int x = player->jx;
11207 int y = player->jy;
11209 if (player->active && player->is_pushing && player->is_moving &&
11211 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11212 Feld[x][y] == EL_SPRING))
11214 ContinueMoving(x, y);
11216 /* continue moving after pushing (this is actually a bug) */
11217 if (!IS_MOVING(x, y))
11218 Stop[x][y] = FALSE;
11224 debug_print_timestamp(0, "start main loop profiling");
11227 SCAN_PLAYFIELD(x, y)
11229 ChangeCount[x][y] = 0;
11230 ChangeEvent[x][y] = -1;
11232 /* this must be handled before main playfield loop */
11233 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11236 if (MovDelay[x][y] <= 0)
11240 #if USE_NEW_SNAP_DELAY
11241 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11244 if (MovDelay[x][y] <= 0)
11247 DrawLevelField(x, y);
11249 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11255 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11257 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11258 printf("GameActions(): This should never happen!\n");
11260 ChangePage[x][y] = -1;
11264 Stop[x][y] = FALSE;
11265 if (WasJustMoving[x][y] > 0)
11266 WasJustMoving[x][y]--;
11267 if (WasJustFalling[x][y] > 0)
11268 WasJustFalling[x][y]--;
11269 if (CheckCollision[x][y] > 0)
11270 CheckCollision[x][y]--;
11271 if (CheckImpact[x][y] > 0)
11272 CheckImpact[x][y]--;
11276 /* reset finished pushing action (not done in ContinueMoving() to allow
11277 continuous pushing animation for elements with zero push delay) */
11278 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11280 ResetGfxAnimation(x, y);
11281 DrawLevelField(x, y);
11285 if (IS_BLOCKED(x, y))
11289 Blocked2Moving(x, y, &oldx, &oldy);
11290 if (!IS_MOVING(oldx, oldy))
11292 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11293 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11294 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11295 printf("GameActions(): This should never happen!\n");
11302 debug_print_timestamp(0, "- time for pre-main loop:");
11305 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11306 SCAN_PLAYFIELD(x, y)
11308 element = Feld[x][y];
11309 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11314 int element2 = element;
11315 int graphic2 = graphic;
11317 int element2 = Feld[x][y];
11318 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11320 int last_gfx_frame = GfxFrame[x][y];
11322 if (graphic_info[graphic2].anim_global_sync)
11323 GfxFrame[x][y] = FrameCounter;
11324 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11325 GfxFrame[x][y] = CustomValue[x][y];
11326 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11327 GfxFrame[x][y] = element_info[element2].collect_score;
11328 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11329 GfxFrame[x][y] = ChangeDelay[x][y];
11331 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11332 DrawLevelGraphicAnimation(x, y, graphic2);
11335 ResetGfxFrame(x, y, TRUE);
11339 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11340 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11341 ResetRandomAnimationValue(x, y);
11345 SetRandomAnimationValue(x, y);
11349 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11352 #endif // -------------------- !!! TEST ONLY !!! --------------------
11355 debug_print_timestamp(0, "- time for TEST loop: -->");
11358 SCAN_PLAYFIELD(x, y)
11360 element = Feld[x][y];
11361 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11363 ResetGfxFrame(x, y, TRUE);
11365 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11366 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11367 ResetRandomAnimationValue(x, y);
11369 SetRandomAnimationValue(x, y);
11371 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11373 if (IS_INACTIVE(element))
11375 if (IS_ANIMATED(graphic))
11376 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11381 /* this may take place after moving, so 'element' may have changed */
11382 if (IS_CHANGING(x, y) &&
11383 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11385 int page = element_info[element].event_page_nr[CE_DELAY];
11388 HandleElementChange(x, y, page);
11390 if (CAN_CHANGE(element))
11391 HandleElementChange(x, y, page);
11393 if (HAS_ACTION(element))
11394 ExecuteCustomElementAction(x, y, element, page);
11397 element = Feld[x][y];
11398 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11401 #if 0 // ---------------------------------------------------------------------
11403 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11407 element = Feld[x][y];
11408 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11410 if (IS_ANIMATED(graphic) &&
11411 !IS_MOVING(x, y) &&
11413 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11415 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11416 DrawTwinkleOnField(x, y);
11418 else if (IS_MOVING(x, y))
11419 ContinueMoving(x, y);
11426 case EL_EM_EXIT_OPEN:
11427 case EL_SP_EXIT_OPEN:
11428 case EL_STEEL_EXIT_OPEN:
11429 case EL_EM_STEEL_EXIT_OPEN:
11430 case EL_SP_TERMINAL:
11431 case EL_SP_TERMINAL_ACTIVE:
11432 case EL_EXTRA_TIME:
11433 case EL_SHIELD_NORMAL:
11434 case EL_SHIELD_DEADLY:
11435 if (IS_ANIMATED(graphic))
11436 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11439 case EL_DYNAMITE_ACTIVE:
11440 case EL_EM_DYNAMITE_ACTIVE:
11441 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11442 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11443 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11444 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11445 case EL_SP_DISK_RED_ACTIVE:
11446 CheckDynamite(x, y);
11449 case EL_AMOEBA_GROWING:
11450 AmoebeWaechst(x, y);
11453 case EL_AMOEBA_SHRINKING:
11454 AmoebaDisappearing(x, y);
11457 #if !USE_NEW_AMOEBA_CODE
11458 case EL_AMOEBA_WET:
11459 case EL_AMOEBA_DRY:
11460 case EL_AMOEBA_FULL:
11462 case EL_EMC_DRIPPER:
11463 AmoebeAbleger(x, y);
11467 case EL_GAME_OF_LIFE:
11472 case EL_EXIT_CLOSED:
11476 case EL_EM_EXIT_CLOSED:
11480 case EL_STEEL_EXIT_CLOSED:
11481 CheckExitSteel(x, y);
11484 case EL_EM_STEEL_EXIT_CLOSED:
11485 CheckExitSteelEM(x, y);
11488 case EL_SP_EXIT_CLOSED:
11492 case EL_EXPANDABLE_WALL_GROWING:
11493 case EL_EXPANDABLE_STEELWALL_GROWING:
11494 MauerWaechst(x, y);
11497 case EL_EXPANDABLE_WALL:
11498 case EL_EXPANDABLE_WALL_HORIZONTAL:
11499 case EL_EXPANDABLE_WALL_VERTICAL:
11500 case EL_EXPANDABLE_WALL_ANY:
11501 case EL_BD_EXPANDABLE_WALL:
11502 MauerAbleger(x, y);
11505 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11506 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11507 case EL_EXPANDABLE_STEELWALL_ANY:
11508 MauerAblegerStahl(x, y);
11512 CheckForDragon(x, y);
11518 case EL_ELEMENT_SNAPPING:
11519 case EL_DIAGONAL_SHRINKING:
11520 case EL_DIAGONAL_GROWING:
11523 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11525 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11530 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11531 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11536 #else // ---------------------------------------------------------------------
11538 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11542 element = Feld[x][y];
11543 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11545 if (IS_ANIMATED(graphic) &&
11546 !IS_MOVING(x, y) &&
11548 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11550 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11551 DrawTwinkleOnField(x, y);
11553 else if ((element == EL_ACID ||
11554 element == EL_EXIT_OPEN ||
11555 element == EL_EM_EXIT_OPEN ||
11556 element == EL_SP_EXIT_OPEN ||
11557 element == EL_STEEL_EXIT_OPEN ||
11558 element == EL_EM_STEEL_EXIT_OPEN ||
11559 element == EL_SP_TERMINAL ||
11560 element == EL_SP_TERMINAL_ACTIVE ||
11561 element == EL_EXTRA_TIME ||
11562 element == EL_SHIELD_NORMAL ||
11563 element == EL_SHIELD_DEADLY) &&
11564 IS_ANIMATED(graphic))
11565 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11566 else if (IS_MOVING(x, y))
11567 ContinueMoving(x, y);
11568 else if (IS_ACTIVE_BOMB(element))
11569 CheckDynamite(x, y);
11570 else if (element == EL_AMOEBA_GROWING)
11571 AmoebeWaechst(x, y);
11572 else if (element == EL_AMOEBA_SHRINKING)
11573 AmoebaDisappearing(x, y);
11575 #if !USE_NEW_AMOEBA_CODE
11576 else if (IS_AMOEBALIVE(element))
11577 AmoebeAbleger(x, y);
11580 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11582 else if (element == EL_EXIT_CLOSED)
11584 else if (element == EL_EM_EXIT_CLOSED)
11586 else if (element == EL_STEEL_EXIT_CLOSED)
11587 CheckExitSteel(x, y);
11588 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11589 CheckExitSteelEM(x, y);
11590 else if (element == EL_SP_EXIT_CLOSED)
11592 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11593 element == EL_EXPANDABLE_STEELWALL_GROWING)
11594 MauerWaechst(x, y);
11595 else if (element == EL_EXPANDABLE_WALL ||
11596 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11597 element == EL_EXPANDABLE_WALL_VERTICAL ||
11598 element == EL_EXPANDABLE_WALL_ANY ||
11599 element == EL_BD_EXPANDABLE_WALL)
11600 MauerAbleger(x, y);
11601 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11602 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11603 element == EL_EXPANDABLE_STEELWALL_ANY)
11604 MauerAblegerStahl(x, y);
11605 else if (element == EL_FLAMES)
11606 CheckForDragon(x, y);
11607 else if (element == EL_EXPLOSION)
11608 ; /* drawing of correct explosion animation is handled separately */
11609 else if (element == EL_ELEMENT_SNAPPING ||
11610 element == EL_DIAGONAL_SHRINKING ||
11611 element == EL_DIAGONAL_GROWING)
11613 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11615 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11617 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11618 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11620 #endif // ---------------------------------------------------------------------
11622 if (IS_BELT_ACTIVE(element))
11623 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11625 if (game.magic_wall_active)
11627 int jx = local_player->jx, jy = local_player->jy;
11629 /* play the element sound at the position nearest to the player */
11630 if ((element == EL_MAGIC_WALL_FULL ||
11631 element == EL_MAGIC_WALL_ACTIVE ||
11632 element == EL_MAGIC_WALL_EMPTYING ||
11633 element == EL_BD_MAGIC_WALL_FULL ||
11634 element == EL_BD_MAGIC_WALL_ACTIVE ||
11635 element == EL_BD_MAGIC_WALL_EMPTYING ||
11636 element == EL_DC_MAGIC_WALL_FULL ||
11637 element == EL_DC_MAGIC_WALL_ACTIVE ||
11638 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11639 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11648 debug_print_timestamp(0, "- time for MAIN loop: -->");
11651 #if USE_NEW_AMOEBA_CODE
11652 /* new experimental amoeba growth stuff */
11653 if (!(FrameCounter % 8))
11655 static unsigned long random = 1684108901;
11657 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11659 x = RND(lev_fieldx);
11660 y = RND(lev_fieldy);
11661 element = Feld[x][y];
11663 if (!IS_PLAYER(x,y) &&
11664 (element == EL_EMPTY ||
11665 CAN_GROW_INTO(element) ||
11666 element == EL_QUICKSAND_EMPTY ||
11667 element == EL_QUICKSAND_FAST_EMPTY ||
11668 element == EL_ACID_SPLASH_LEFT ||
11669 element == EL_ACID_SPLASH_RIGHT))
11671 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11672 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11673 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11674 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11675 Feld[x][y] = EL_AMOEBA_DROP;
11678 random = random * 129 + 1;
11684 if (game.explosions_delayed)
11687 game.explosions_delayed = FALSE;
11689 SCAN_PLAYFIELD(x, y)
11691 element = Feld[x][y];
11693 if (ExplodeField[x][y])
11694 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11695 else if (element == EL_EXPLOSION)
11696 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11698 ExplodeField[x][y] = EX_TYPE_NONE;
11701 game.explosions_delayed = TRUE;
11704 if (game.magic_wall_active)
11706 if (!(game.magic_wall_time_left % 4))
11708 int element = Feld[magic_wall_x][magic_wall_y];
11710 if (element == EL_BD_MAGIC_WALL_FULL ||
11711 element == EL_BD_MAGIC_WALL_ACTIVE ||
11712 element == EL_BD_MAGIC_WALL_EMPTYING)
11713 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11714 else if (element == EL_DC_MAGIC_WALL_FULL ||
11715 element == EL_DC_MAGIC_WALL_ACTIVE ||
11716 element == EL_DC_MAGIC_WALL_EMPTYING)
11717 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11719 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11722 if (game.magic_wall_time_left > 0)
11724 game.magic_wall_time_left--;
11725 if (!game.magic_wall_time_left)
11727 SCAN_PLAYFIELD(x, y)
11729 element = Feld[x][y];
11731 if (element == EL_MAGIC_WALL_ACTIVE ||
11732 element == EL_MAGIC_WALL_FULL)
11734 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11735 DrawLevelField(x, y);
11737 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11738 element == EL_BD_MAGIC_WALL_FULL)
11740 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11741 DrawLevelField(x, y);
11743 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11744 element == EL_DC_MAGIC_WALL_FULL)
11746 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11747 DrawLevelField(x, y);
11751 game.magic_wall_active = FALSE;
11756 if (game.light_time_left > 0)
11758 game.light_time_left--;
11760 if (game.light_time_left == 0)
11761 RedrawAllLightSwitchesAndInvisibleElements();
11764 if (game.timegate_time_left > 0)
11766 game.timegate_time_left--;
11768 if (game.timegate_time_left == 0)
11769 CloseAllOpenTimegates();
11772 if (game.lenses_time_left > 0)
11774 game.lenses_time_left--;
11776 if (game.lenses_time_left == 0)
11777 RedrawAllInvisibleElementsForLenses();
11780 if (game.magnify_time_left > 0)
11782 game.magnify_time_left--;
11784 if (game.magnify_time_left == 0)
11785 RedrawAllInvisibleElementsForMagnifier();
11788 for (i = 0; i < MAX_PLAYERS; i++)
11790 struct PlayerInfo *player = &stored_player[i];
11792 if (SHIELD_ON(player))
11794 if (player->shield_deadly_time_left)
11795 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11796 else if (player->shield_normal_time_left)
11797 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11804 PlayAllPlayersSound();
11806 if (options.debug) /* calculate frames per second */
11808 static unsigned long fps_counter = 0;
11809 static int fps_frames = 0;
11810 unsigned long fps_delay_ms = Counter() - fps_counter;
11814 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11816 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11819 fps_counter = Counter();
11822 redraw_mask |= REDRAW_FPS;
11825 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11827 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11829 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11831 local_player->show_envelope = 0;
11835 debug_print_timestamp(0, "stop main loop profiling ");
11836 printf("----------------------------------------------------------\n");
11839 /* use random number generator in every frame to make it less predictable */
11840 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11844 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11846 int min_x = x, min_y = y, max_x = x, max_y = y;
11849 for (i = 0; i < MAX_PLAYERS; i++)
11851 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11853 if (!stored_player[i].active || &stored_player[i] == player)
11856 min_x = MIN(min_x, jx);
11857 min_y = MIN(min_y, jy);
11858 max_x = MAX(max_x, jx);
11859 max_y = MAX(max_y, jy);
11862 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11865 static boolean AllPlayersInVisibleScreen()
11869 for (i = 0; i < MAX_PLAYERS; i++)
11871 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11873 if (!stored_player[i].active)
11876 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11883 void ScrollLevel(int dx, int dy)
11886 static Bitmap *bitmap_db_field2 = NULL;
11887 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11894 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11895 /* only horizontal XOR vertical scroll direction allowed */
11896 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11901 if (bitmap_db_field2 == NULL)
11902 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11904 /* needed when blitting directly to same bitmap -- should not be needed with
11905 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11906 BlitBitmap(drawto_field, bitmap_db_field2,
11907 FX + TILEX * (dx == -1) - softscroll_offset,
11908 FY + TILEY * (dy == -1) - softscroll_offset,
11909 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11910 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11911 FX + TILEX * (dx == 1) - softscroll_offset,
11912 FY + TILEY * (dy == 1) - softscroll_offset);
11913 BlitBitmap(bitmap_db_field2, drawto_field,
11914 FX + TILEX * (dx == 1) - softscroll_offset,
11915 FY + TILEY * (dy == 1) - softscroll_offset,
11916 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11917 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11918 FX + TILEX * (dx == 1) - softscroll_offset,
11919 FY + TILEY * (dy == 1) - softscroll_offset);
11924 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11925 int xsize = (BX2 - BX1 + 1);
11926 int ysize = (BY2 - BY1 + 1);
11927 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11928 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11929 int step = (start < end ? +1 : -1);
11931 for (i = start; i != end; i += step)
11933 BlitBitmap(drawto_field, drawto_field,
11934 FX + TILEX * (dx != 0 ? i + step : 0),
11935 FY + TILEY * (dy != 0 ? i + step : 0),
11936 TILEX * (dx != 0 ? 1 : xsize),
11937 TILEY * (dy != 0 ? 1 : ysize),
11938 FX + TILEX * (dx != 0 ? i : 0),
11939 FY + TILEY * (dy != 0 ? i : 0));
11944 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11946 BlitBitmap(drawto_field, drawto_field,
11947 FX + TILEX * (dx == -1) - softscroll_offset,
11948 FY + TILEY * (dy == -1) - softscroll_offset,
11949 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11950 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11951 FX + TILEX * (dx == 1) - softscroll_offset,
11952 FY + TILEY * (dy == 1) - softscroll_offset);
11958 x = (dx == 1 ? BX1 : BX2);
11959 for (y = BY1; y <= BY2; y++)
11960 DrawScreenField(x, y);
11965 y = (dy == 1 ? BY1 : BY2);
11966 for (x = BX1; x <= BX2; x++)
11967 DrawScreenField(x, y);
11970 redraw_mask |= REDRAW_FIELD;
11973 static boolean canFallDown(struct PlayerInfo *player)
11975 int jx = player->jx, jy = player->jy;
11977 return (IN_LEV_FIELD(jx, jy + 1) &&
11978 (IS_FREE(jx, jy + 1) ||
11979 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11980 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11981 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11984 static boolean canPassField(int x, int y, int move_dir)
11986 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11987 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11988 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11989 int nextx = x + dx;
11990 int nexty = y + dy;
11991 int element = Feld[x][y];
11993 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11994 !CAN_MOVE(element) &&
11995 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11996 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11997 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12000 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12002 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12003 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12004 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12008 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12009 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12010 (IS_DIGGABLE(Feld[newx][newy]) ||
12011 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12012 canPassField(newx, newy, move_dir)));
12015 static void CheckGravityMovement(struct PlayerInfo *player)
12017 #if USE_PLAYER_GRAVITY
12018 if (player->gravity && !player->programmed_action)
12020 if (game.gravity && !player->programmed_action)
12023 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12024 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12025 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12026 int jx = player->jx, jy = player->jy;
12027 boolean player_is_moving_to_valid_field =
12028 (!player_is_snapping &&
12029 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12030 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12031 boolean player_can_fall_down = canFallDown(player);
12033 if (player_can_fall_down &&
12034 !player_is_moving_to_valid_field)
12035 player->programmed_action = MV_DOWN;
12039 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12041 return CheckGravityMovement(player);
12043 #if USE_PLAYER_GRAVITY
12044 if (player->gravity && !player->programmed_action)
12046 if (game.gravity && !player->programmed_action)
12049 int jx = player->jx, jy = player->jy;
12050 boolean field_under_player_is_free =
12051 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12052 boolean player_is_standing_on_valid_field =
12053 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12054 (IS_WALKABLE(Feld[jx][jy]) &&
12055 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12057 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12058 player->programmed_action = MV_DOWN;
12063 MovePlayerOneStep()
12064 -----------------------------------------------------------------------------
12065 dx, dy: direction (non-diagonal) to try to move the player to
12066 real_dx, real_dy: direction as read from input device (can be diagonal)
12069 boolean MovePlayerOneStep(struct PlayerInfo *player,
12070 int dx, int dy, int real_dx, int real_dy)
12072 int jx = player->jx, jy = player->jy;
12073 int new_jx = jx + dx, new_jy = jy + dy;
12074 #if !USE_FIXED_DONT_RUN_INTO
12078 boolean player_can_move = !player->cannot_move;
12080 if (!player->active || (!dx && !dy))
12081 return MP_NO_ACTION;
12083 player->MovDir = (dx < 0 ? MV_LEFT :
12084 dx > 0 ? MV_RIGHT :
12086 dy > 0 ? MV_DOWN : MV_NONE);
12088 if (!IN_LEV_FIELD(new_jx, new_jy))
12089 return MP_NO_ACTION;
12091 if (!player_can_move)
12093 if (player->MovPos == 0)
12095 player->is_moving = FALSE;
12096 player->is_digging = FALSE;
12097 player->is_collecting = FALSE;
12098 player->is_snapping = FALSE;
12099 player->is_pushing = FALSE;
12104 if (!options.network && game.centered_player_nr == -1 &&
12105 !AllPlayersInSight(player, new_jx, new_jy))
12106 return MP_NO_ACTION;
12108 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12109 return MP_NO_ACTION;
12112 #if !USE_FIXED_DONT_RUN_INTO
12113 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12115 /* (moved to DigField()) */
12116 if (player_can_move && DONT_RUN_INTO(element))
12118 if (element == EL_ACID && dx == 0 && dy == 1)
12120 SplashAcid(new_jx, new_jy);
12121 Feld[jx][jy] = EL_PLAYER_1;
12122 InitMovingField(jx, jy, MV_DOWN);
12123 Store[jx][jy] = EL_ACID;
12124 ContinueMoving(jx, jy);
12125 BuryPlayer(player);
12128 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12134 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12135 if (can_move != MP_MOVING)
12138 /* check if DigField() has caused relocation of the player */
12139 if (player->jx != jx || player->jy != jy)
12140 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12142 StorePlayer[jx][jy] = 0;
12143 player->last_jx = jx;
12144 player->last_jy = jy;
12145 player->jx = new_jx;
12146 player->jy = new_jy;
12147 StorePlayer[new_jx][new_jy] = player->element_nr;
12149 if (player->move_delay_value_next != -1)
12151 player->move_delay_value = player->move_delay_value_next;
12152 player->move_delay_value_next = -1;
12156 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12158 player->step_counter++;
12160 PlayerVisit[jx][jy] = FrameCounter;
12162 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12163 player->is_moving = TRUE;
12167 /* should better be called in MovePlayer(), but this breaks some tapes */
12168 ScrollPlayer(player, SCROLL_INIT);
12174 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12176 int jx = player->jx, jy = player->jy;
12177 int old_jx = jx, old_jy = jy;
12178 int moved = MP_NO_ACTION;
12180 if (!player->active)
12185 if (player->MovPos == 0)
12187 player->is_moving = FALSE;
12188 player->is_digging = FALSE;
12189 player->is_collecting = FALSE;
12190 player->is_snapping = FALSE;
12191 player->is_pushing = FALSE;
12197 if (player->move_delay > 0)
12200 player->move_delay = -1; /* set to "uninitialized" value */
12202 /* store if player is automatically moved to next field */
12203 player->is_auto_moving = (player->programmed_action != MV_NONE);
12205 /* remove the last programmed player action */
12206 player->programmed_action = 0;
12208 if (player->MovPos)
12210 /* should only happen if pre-1.2 tape recordings are played */
12211 /* this is only for backward compatibility */
12213 int original_move_delay_value = player->move_delay_value;
12216 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12220 /* scroll remaining steps with finest movement resolution */
12221 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12223 while (player->MovPos)
12225 ScrollPlayer(player, SCROLL_GO_ON);
12226 ScrollScreen(NULL, SCROLL_GO_ON);
12228 AdvanceFrameAndPlayerCounters(player->index_nr);
12234 player->move_delay_value = original_move_delay_value;
12237 player->is_active = FALSE;
12239 if (player->last_move_dir & MV_HORIZONTAL)
12241 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12242 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12246 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12247 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12250 #if USE_FIXED_BORDER_RUNNING_GFX
12251 if (!moved && !player->is_active)
12253 player->is_moving = FALSE;
12254 player->is_digging = FALSE;
12255 player->is_collecting = FALSE;
12256 player->is_snapping = FALSE;
12257 player->is_pushing = FALSE;
12265 if (moved & MP_MOVING && !ScreenMovPos &&
12266 (player->index_nr == game.centered_player_nr ||
12267 game.centered_player_nr == -1))
12269 if (moved & MP_MOVING && !ScreenMovPos &&
12270 (player == local_player || !options.network))
12273 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12274 int offset = game.scroll_delay_value;
12276 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12278 /* actual player has left the screen -- scroll in that direction */
12279 if (jx != old_jx) /* player has moved horizontally */
12280 scroll_x += (jx - old_jx);
12281 else /* player has moved vertically */
12282 scroll_y += (jy - old_jy);
12286 if (jx != old_jx) /* player has moved horizontally */
12288 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12289 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12290 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12292 /* don't scroll over playfield boundaries */
12293 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12294 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12296 /* don't scroll more than one field at a time */
12297 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12299 /* don't scroll against the player's moving direction */
12300 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12301 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12302 scroll_x = old_scroll_x;
12304 else /* player has moved vertically */
12306 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12307 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12308 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12310 /* don't scroll over playfield boundaries */
12311 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12312 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12314 /* don't scroll more than one field at a time */
12315 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12317 /* don't scroll against the player's moving direction */
12318 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12319 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12320 scroll_y = old_scroll_y;
12324 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12327 if (!options.network && game.centered_player_nr == -1 &&
12328 !AllPlayersInVisibleScreen())
12330 scroll_x = old_scroll_x;
12331 scroll_y = old_scroll_y;
12335 if (!options.network && !AllPlayersInVisibleScreen())
12337 scroll_x = old_scroll_x;
12338 scroll_y = old_scroll_y;
12343 ScrollScreen(player, SCROLL_INIT);
12344 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12349 player->StepFrame = 0;
12351 if (moved & MP_MOVING)
12353 if (old_jx != jx && old_jy == jy)
12354 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12355 else if (old_jx == jx && old_jy != jy)
12356 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12358 DrawLevelField(jx, jy); /* for "crumbled sand" */
12360 player->last_move_dir = player->MovDir;
12361 player->is_moving = TRUE;
12362 player->is_snapping = FALSE;
12363 player->is_switching = FALSE;
12364 player->is_dropping = FALSE;
12365 player->is_dropping_pressed = FALSE;
12366 player->drop_pressed_delay = 0;
12369 /* should better be called here than above, but this breaks some tapes */
12370 ScrollPlayer(player, SCROLL_INIT);
12375 CheckGravityMovementWhenNotMoving(player);
12377 player->is_moving = FALSE;
12379 /* at this point, the player is allowed to move, but cannot move right now
12380 (e.g. because of something blocking the way) -- ensure that the player
12381 is also allowed to move in the next frame (in old versions before 3.1.1,
12382 the player was forced to wait again for eight frames before next try) */
12384 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12385 player->move_delay = 0; /* allow direct movement in the next frame */
12388 if (player->move_delay == -1) /* not yet initialized by DigField() */
12389 player->move_delay = player->move_delay_value;
12391 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12393 TestIfPlayerTouchesBadThing(jx, jy);
12394 TestIfPlayerTouchesCustomElement(jx, jy);
12397 if (!player->active)
12398 RemovePlayer(player);
12403 void ScrollPlayer(struct PlayerInfo *player, int mode)
12405 int jx = player->jx, jy = player->jy;
12406 int last_jx = player->last_jx, last_jy = player->last_jy;
12407 int move_stepsize = TILEX / player->move_delay_value;
12409 #if USE_NEW_PLAYER_SPEED
12410 if (!player->active)
12413 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12416 if (!player->active || player->MovPos == 0)
12420 if (mode == SCROLL_INIT)
12422 player->actual_frame_counter = FrameCounter;
12423 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12425 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12426 Feld[last_jx][last_jy] == EL_EMPTY)
12428 int last_field_block_delay = 0; /* start with no blocking at all */
12429 int block_delay_adjustment = player->block_delay_adjustment;
12431 /* if player blocks last field, add delay for exactly one move */
12432 if (player->block_last_field)
12434 last_field_block_delay += player->move_delay_value;
12436 /* when blocking enabled, prevent moving up despite gravity */
12437 #if USE_PLAYER_GRAVITY
12438 if (player->gravity && player->MovDir == MV_UP)
12439 block_delay_adjustment = -1;
12441 if (game.gravity && player->MovDir == MV_UP)
12442 block_delay_adjustment = -1;
12446 /* add block delay adjustment (also possible when not blocking) */
12447 last_field_block_delay += block_delay_adjustment;
12449 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12450 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12453 #if USE_NEW_PLAYER_SPEED
12454 if (player->MovPos != 0) /* player has not yet reached destination */
12460 else if (!FrameReached(&player->actual_frame_counter, 1))
12463 #if USE_NEW_PLAYER_SPEED
12464 if (player->MovPos != 0)
12466 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12467 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12469 /* before DrawPlayer() to draw correct player graphic for this case */
12470 if (player->MovPos == 0)
12471 CheckGravityMovement(player);
12474 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12475 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12477 /* before DrawPlayer() to draw correct player graphic for this case */
12478 if (player->MovPos == 0)
12479 CheckGravityMovement(player);
12482 if (player->MovPos == 0) /* player reached destination field */
12484 if (player->move_delay_reset_counter > 0)
12486 player->move_delay_reset_counter--;
12488 if (player->move_delay_reset_counter == 0)
12490 /* continue with normal speed after quickly moving through gate */
12491 HALVE_PLAYER_SPEED(player);
12493 /* be able to make the next move without delay */
12494 player->move_delay = 0;
12498 player->last_jx = jx;
12499 player->last_jy = jy;
12501 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12502 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12503 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12504 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12505 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12506 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12508 DrawPlayer(player); /* needed here only to cleanup last field */
12509 RemovePlayer(player);
12511 if (local_player->friends_still_needed == 0 ||
12512 IS_SP_ELEMENT(Feld[jx][jy]))
12513 PlayerWins(player);
12516 /* this breaks one level: "machine", level 000 */
12518 int move_direction = player->MovDir;
12519 int enter_side = MV_DIR_OPPOSITE(move_direction);
12520 int leave_side = move_direction;
12521 int old_jx = last_jx;
12522 int old_jy = last_jy;
12523 int old_element = Feld[old_jx][old_jy];
12524 int new_element = Feld[jx][jy];
12526 if (IS_CUSTOM_ELEMENT(old_element))
12527 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12529 player->index_bit, leave_side);
12531 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12532 CE_PLAYER_LEAVES_X,
12533 player->index_bit, leave_side);
12535 if (IS_CUSTOM_ELEMENT(new_element))
12536 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12537 player->index_bit, enter_side);
12539 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12540 CE_PLAYER_ENTERS_X,
12541 player->index_bit, enter_side);
12543 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12544 CE_MOVE_OF_X, move_direction);
12547 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12549 TestIfPlayerTouchesBadThing(jx, jy);
12550 TestIfPlayerTouchesCustomElement(jx, jy);
12552 /* needed because pushed element has not yet reached its destination,
12553 so it would trigger a change event at its previous field location */
12554 if (!player->is_pushing)
12555 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12557 if (!player->active)
12558 RemovePlayer(player);
12561 if (!local_player->LevelSolved && level.use_step_counter)
12571 if (TimeLeft <= 10 && setup.time_limit)
12572 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12575 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12577 DisplayGameControlValues();
12579 DrawGameValue_Time(TimeLeft);
12582 if (!TimeLeft && setup.time_limit)
12583 for (i = 0; i < MAX_PLAYERS; i++)
12584 KillPlayer(&stored_player[i]);
12587 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12589 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12591 DisplayGameControlValues();
12594 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12595 DrawGameValue_Time(TimePlayed);
12599 if (tape.single_step && tape.recording && !tape.pausing &&
12600 !player->programmed_action)
12601 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12605 void ScrollScreen(struct PlayerInfo *player, int mode)
12607 static unsigned long screen_frame_counter = 0;
12609 if (mode == SCROLL_INIT)
12611 /* set scrolling step size according to actual player's moving speed */
12612 ScrollStepSize = TILEX / player->move_delay_value;
12614 screen_frame_counter = FrameCounter;
12615 ScreenMovDir = player->MovDir;
12616 ScreenMovPos = player->MovPos;
12617 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12620 else if (!FrameReached(&screen_frame_counter, 1))
12625 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12626 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12627 redraw_mask |= REDRAW_FIELD;
12630 ScreenMovDir = MV_NONE;
12633 void TestIfPlayerTouchesCustomElement(int x, int y)
12635 static int xy[4][2] =
12642 static int trigger_sides[4][2] =
12644 /* center side border side */
12645 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12646 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12647 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12648 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12650 static int touch_dir[4] =
12652 MV_LEFT | MV_RIGHT,
12657 int center_element = Feld[x][y]; /* should always be non-moving! */
12660 for (i = 0; i < NUM_DIRECTIONS; i++)
12662 int xx = x + xy[i][0];
12663 int yy = y + xy[i][1];
12664 int center_side = trigger_sides[i][0];
12665 int border_side = trigger_sides[i][1];
12666 int border_element;
12668 if (!IN_LEV_FIELD(xx, yy))
12671 if (IS_PLAYER(x, y))
12673 struct PlayerInfo *player = PLAYERINFO(x, y);
12675 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12676 border_element = Feld[xx][yy]; /* may be moving! */
12677 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12678 border_element = Feld[xx][yy];
12679 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12680 border_element = MovingOrBlocked2Element(xx, yy);
12682 continue; /* center and border element do not touch */
12684 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12685 player->index_bit, border_side);
12686 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12687 CE_PLAYER_TOUCHES_X,
12688 player->index_bit, border_side);
12690 else if (IS_PLAYER(xx, yy))
12692 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12694 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12696 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12697 continue; /* center and border element do not touch */
12700 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12701 player->index_bit, center_side);
12702 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12703 CE_PLAYER_TOUCHES_X,
12704 player->index_bit, center_side);
12710 #if USE_ELEMENT_TOUCHING_BUGFIX
12712 void TestIfElementTouchesCustomElement(int x, int y)
12714 static int xy[4][2] =
12721 static int trigger_sides[4][2] =
12723 /* center side border side */
12724 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12725 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12726 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12727 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12729 static int touch_dir[4] =
12731 MV_LEFT | MV_RIGHT,
12736 boolean change_center_element = FALSE;
12737 int center_element = Feld[x][y]; /* should always be non-moving! */
12738 int border_element_old[NUM_DIRECTIONS];
12741 for (i = 0; i < NUM_DIRECTIONS; i++)
12743 int xx = x + xy[i][0];
12744 int yy = y + xy[i][1];
12745 int border_element;
12747 border_element_old[i] = -1;
12749 if (!IN_LEV_FIELD(xx, yy))
12752 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12753 border_element = Feld[xx][yy]; /* may be moving! */
12754 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12755 border_element = Feld[xx][yy];
12756 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12757 border_element = MovingOrBlocked2Element(xx, yy);
12759 continue; /* center and border element do not touch */
12761 border_element_old[i] = border_element;
12764 for (i = 0; i < NUM_DIRECTIONS; i++)
12766 int xx = x + xy[i][0];
12767 int yy = y + xy[i][1];
12768 int center_side = trigger_sides[i][0];
12769 int border_element = border_element_old[i];
12771 if (border_element == -1)
12774 /* check for change of border element */
12775 CheckElementChangeBySide(xx, yy, border_element, center_element,
12776 CE_TOUCHING_X, center_side);
12779 for (i = 0; i < NUM_DIRECTIONS; i++)
12781 int border_side = trigger_sides[i][1];
12782 int border_element = border_element_old[i];
12784 if (border_element == -1)
12787 /* check for change of center element (but change it only once) */
12788 if (!change_center_element)
12789 change_center_element =
12790 CheckElementChangeBySide(x, y, center_element, border_element,
12791 CE_TOUCHING_X, border_side);
12797 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12799 static int xy[4][2] =
12806 static int trigger_sides[4][2] =
12808 /* center side border side */
12809 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12810 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12811 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12812 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12814 static int touch_dir[4] =
12816 MV_LEFT | MV_RIGHT,
12821 boolean change_center_element = FALSE;
12822 int center_element = Feld[x][y]; /* should always be non-moving! */
12825 for (i = 0; i < NUM_DIRECTIONS; i++)
12827 int xx = x + xy[i][0];
12828 int yy = y + xy[i][1];
12829 int center_side = trigger_sides[i][0];
12830 int border_side = trigger_sides[i][1];
12831 int border_element;
12833 if (!IN_LEV_FIELD(xx, yy))
12836 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12837 border_element = Feld[xx][yy]; /* may be moving! */
12838 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12839 border_element = Feld[xx][yy];
12840 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12841 border_element = MovingOrBlocked2Element(xx, yy);
12843 continue; /* center and border element do not touch */
12845 /* check for change of center element (but change it only once) */
12846 if (!change_center_element)
12847 change_center_element =
12848 CheckElementChangeBySide(x, y, center_element, border_element,
12849 CE_TOUCHING_X, border_side);
12851 /* check for change of border element */
12852 CheckElementChangeBySide(xx, yy, border_element, center_element,
12853 CE_TOUCHING_X, center_side);
12859 void TestIfElementHitsCustomElement(int x, int y, int direction)
12861 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12862 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12863 int hitx = x + dx, hity = y + dy;
12864 int hitting_element = Feld[x][y];
12865 int touched_element;
12867 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12870 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12871 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12873 if (IN_LEV_FIELD(hitx, hity))
12875 int opposite_direction = MV_DIR_OPPOSITE(direction);
12876 int hitting_side = direction;
12877 int touched_side = opposite_direction;
12878 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12879 MovDir[hitx][hity] != direction ||
12880 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12886 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12887 CE_HITTING_X, touched_side);
12889 CheckElementChangeBySide(hitx, hity, touched_element,
12890 hitting_element, CE_HIT_BY_X, hitting_side);
12892 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12893 CE_HIT_BY_SOMETHING, opposite_direction);
12897 /* "hitting something" is also true when hitting the playfield border */
12898 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12899 CE_HITTING_SOMETHING, direction);
12903 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12905 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12906 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12907 int hitx = x + dx, hity = y + dy;
12908 int hitting_element = Feld[x][y];
12909 int touched_element;
12911 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12912 !IS_FREE(hitx, hity) &&
12913 (!IS_MOVING(hitx, hity) ||
12914 MovDir[hitx][hity] != direction ||
12915 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12918 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12922 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12926 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12927 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12929 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12930 EP_CAN_SMASH_EVERYTHING, direction);
12932 if (IN_LEV_FIELD(hitx, hity))
12934 int opposite_direction = MV_DIR_OPPOSITE(direction);
12935 int hitting_side = direction;
12936 int touched_side = opposite_direction;
12938 int touched_element = MovingOrBlocked2Element(hitx, hity);
12941 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12942 MovDir[hitx][hity] != direction ||
12943 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12952 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12953 CE_SMASHED_BY_SOMETHING, opposite_direction);
12955 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12956 CE_OTHER_IS_SMASHING, touched_side);
12958 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12959 CE_OTHER_GETS_SMASHED, hitting_side);
12965 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12967 int i, kill_x = -1, kill_y = -1;
12969 int bad_element = -1;
12970 static int test_xy[4][2] =
12977 static int test_dir[4] =
12985 for (i = 0; i < NUM_DIRECTIONS; i++)
12987 int test_x, test_y, test_move_dir, test_element;
12989 test_x = good_x + test_xy[i][0];
12990 test_y = good_y + test_xy[i][1];
12992 if (!IN_LEV_FIELD(test_x, test_y))
12996 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12998 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13000 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13001 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13003 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13004 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13008 bad_element = test_element;
13014 if (kill_x != -1 || kill_y != -1)
13016 if (IS_PLAYER(good_x, good_y))
13018 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13020 if (player->shield_deadly_time_left > 0 &&
13021 !IS_INDESTRUCTIBLE(bad_element))
13022 Bang(kill_x, kill_y);
13023 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13024 KillPlayer(player);
13027 Bang(good_x, good_y);
13031 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13033 int i, kill_x = -1, kill_y = -1;
13034 int bad_element = Feld[bad_x][bad_y];
13035 static int test_xy[4][2] =
13042 static int touch_dir[4] =
13044 MV_LEFT | MV_RIGHT,
13049 static int test_dir[4] =
13057 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13060 for (i = 0; i < NUM_DIRECTIONS; i++)
13062 int test_x, test_y, test_move_dir, test_element;
13064 test_x = bad_x + test_xy[i][0];
13065 test_y = bad_y + test_xy[i][1];
13066 if (!IN_LEV_FIELD(test_x, test_y))
13070 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13072 test_element = Feld[test_x][test_y];
13074 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13075 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13077 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13078 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13080 /* good thing is player or penguin that does not move away */
13081 if (IS_PLAYER(test_x, test_y))
13083 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13085 if (bad_element == EL_ROBOT && player->is_moving)
13086 continue; /* robot does not kill player if he is moving */
13088 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13090 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13091 continue; /* center and border element do not touch */
13098 else if (test_element == EL_PENGUIN)
13107 if (kill_x != -1 || kill_y != -1)
13109 if (IS_PLAYER(kill_x, kill_y))
13111 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13113 if (player->shield_deadly_time_left > 0 &&
13114 !IS_INDESTRUCTIBLE(bad_element))
13115 Bang(bad_x, bad_y);
13116 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13117 KillPlayer(player);
13120 Bang(kill_x, kill_y);
13124 void TestIfPlayerTouchesBadThing(int x, int y)
13126 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13129 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13131 TestIfGoodThingHitsBadThing(x, y, move_dir);
13134 void TestIfBadThingTouchesPlayer(int x, int y)
13136 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13139 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13141 TestIfBadThingHitsGoodThing(x, y, move_dir);
13144 void TestIfFriendTouchesBadThing(int x, int y)
13146 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13149 void TestIfBadThingTouchesFriend(int x, int y)
13151 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13154 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13156 int i, kill_x = bad_x, kill_y = bad_y;
13157 static int xy[4][2] =
13165 for (i = 0; i < NUM_DIRECTIONS; i++)
13169 x = bad_x + xy[i][0];
13170 y = bad_y + xy[i][1];
13171 if (!IN_LEV_FIELD(x, y))
13174 element = Feld[x][y];
13175 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13176 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13184 if (kill_x != bad_x || kill_y != bad_y)
13185 Bang(bad_x, bad_y);
13188 void KillPlayer(struct PlayerInfo *player)
13190 int jx = player->jx, jy = player->jy;
13192 if (!player->active)
13195 /* the following code was introduced to prevent an infinite loop when calling
13197 -> CheckTriggeredElementChangeExt()
13198 -> ExecuteCustomElementAction()
13200 -> (infinitely repeating the above sequence of function calls)
13201 which occurs when killing the player while having a CE with the setting
13202 "kill player X when explosion of <player X>"; the solution using a new
13203 field "player->killed" was chosen for backwards compatibility, although
13204 clever use of the fields "player->active" etc. would probably also work */
13206 if (player->killed)
13210 player->killed = TRUE;
13212 /* remove accessible field at the player's position */
13213 Feld[jx][jy] = EL_EMPTY;
13215 /* deactivate shield (else Bang()/Explode() would not work right) */
13216 player->shield_normal_time_left = 0;
13217 player->shield_deadly_time_left = 0;
13220 BuryPlayer(player);
13223 static void KillPlayerUnlessEnemyProtected(int x, int y)
13225 if (!PLAYER_ENEMY_PROTECTED(x, y))
13226 KillPlayer(PLAYERINFO(x, y));
13229 static void KillPlayerUnlessExplosionProtected(int x, int y)
13231 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13232 KillPlayer(PLAYERINFO(x, y));
13235 void BuryPlayer(struct PlayerInfo *player)
13237 int jx = player->jx, jy = player->jy;
13239 if (!player->active)
13242 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13243 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13245 player->GameOver = TRUE;
13246 RemovePlayer(player);
13249 void RemovePlayer(struct PlayerInfo *player)
13251 int jx = player->jx, jy = player->jy;
13252 int i, found = FALSE;
13254 player->present = FALSE;
13255 player->active = FALSE;
13257 if (!ExplodeField[jx][jy])
13258 StorePlayer[jx][jy] = 0;
13260 if (player->is_moving)
13261 DrawLevelField(player->last_jx, player->last_jy);
13263 for (i = 0; i < MAX_PLAYERS; i++)
13264 if (stored_player[i].active)
13268 AllPlayersGone = TRUE;
13274 #if USE_NEW_SNAP_DELAY
13275 static void setFieldForSnapping(int x, int y, int element, int direction)
13277 struct ElementInfo *ei = &element_info[element];
13278 int direction_bit = MV_DIR_TO_BIT(direction);
13279 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13280 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13281 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13283 Feld[x][y] = EL_ELEMENT_SNAPPING;
13284 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13286 ResetGfxAnimation(x, y);
13288 GfxElement[x][y] = element;
13289 GfxAction[x][y] = action;
13290 GfxDir[x][y] = direction;
13291 GfxFrame[x][y] = -1;
13296 =============================================================================
13297 checkDiagonalPushing()
13298 -----------------------------------------------------------------------------
13299 check if diagonal input device direction results in pushing of object
13300 (by checking if the alternative direction is walkable, diggable, ...)
13301 =============================================================================
13304 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13305 int x, int y, int real_dx, int real_dy)
13307 int jx, jy, dx, dy, xx, yy;
13309 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13312 /* diagonal direction: check alternative direction */
13317 xx = jx + (dx == 0 ? real_dx : 0);
13318 yy = jy + (dy == 0 ? real_dy : 0);
13320 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13324 =============================================================================
13326 -----------------------------------------------------------------------------
13327 x, y: field next to player (non-diagonal) to try to dig to
13328 real_dx, real_dy: direction as read from input device (can be diagonal)
13329 =============================================================================
13332 int DigField(struct PlayerInfo *player,
13333 int oldx, int oldy, int x, int y,
13334 int real_dx, int real_dy, int mode)
13336 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13337 boolean player_was_pushing = player->is_pushing;
13338 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13339 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13340 int jx = oldx, jy = oldy;
13341 int dx = x - jx, dy = y - jy;
13342 int nextx = x + dx, nexty = y + dy;
13343 int move_direction = (dx == -1 ? MV_LEFT :
13344 dx == +1 ? MV_RIGHT :
13346 dy == +1 ? MV_DOWN : MV_NONE);
13347 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13348 int dig_side = MV_DIR_OPPOSITE(move_direction);
13349 int old_element = Feld[jx][jy];
13350 #if USE_FIXED_DONT_RUN_INTO
13351 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13357 if (is_player) /* function can also be called by EL_PENGUIN */
13359 if (player->MovPos == 0)
13361 player->is_digging = FALSE;
13362 player->is_collecting = FALSE;
13365 if (player->MovPos == 0) /* last pushing move finished */
13366 player->is_pushing = FALSE;
13368 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13370 player->is_switching = FALSE;
13371 player->push_delay = -1;
13373 return MP_NO_ACTION;
13377 #if !USE_FIXED_DONT_RUN_INTO
13378 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13379 return MP_NO_ACTION;
13382 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13383 old_element = Back[jx][jy];
13385 /* in case of element dropped at player position, check background */
13386 else if (Back[jx][jy] != EL_EMPTY &&
13387 game.engine_version >= VERSION_IDENT(2,2,0,0))
13388 old_element = Back[jx][jy];
13390 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13391 return MP_NO_ACTION; /* field has no opening in this direction */
13393 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13394 return MP_NO_ACTION; /* field has no opening in this direction */
13396 #if USE_FIXED_DONT_RUN_INTO
13397 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13401 Feld[jx][jy] = player->artwork_element;
13402 InitMovingField(jx, jy, MV_DOWN);
13403 Store[jx][jy] = EL_ACID;
13404 ContinueMoving(jx, jy);
13405 BuryPlayer(player);
13407 return MP_DONT_RUN_INTO;
13411 #if USE_FIXED_DONT_RUN_INTO
13412 if (player_can_move && DONT_RUN_INTO(element))
13414 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13416 return MP_DONT_RUN_INTO;
13420 #if USE_FIXED_DONT_RUN_INTO
13421 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13422 return MP_NO_ACTION;
13425 #if !USE_FIXED_DONT_RUN_INTO
13426 element = Feld[x][y];
13429 collect_count = element_info[element].collect_count_initial;
13431 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13432 return MP_NO_ACTION;
13434 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13435 player_can_move = player_can_move_or_snap;
13437 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13438 game.engine_version >= VERSION_IDENT(2,2,0,0))
13440 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13441 player->index_bit, dig_side);
13442 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13443 player->index_bit, dig_side);
13445 if (element == EL_DC_LANDMINE)
13448 if (Feld[x][y] != element) /* field changed by snapping */
13451 return MP_NO_ACTION;
13454 #if USE_PLAYER_GRAVITY
13455 if (player->gravity && is_player && !player->is_auto_moving &&
13456 canFallDown(player) && move_direction != MV_DOWN &&
13457 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13458 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13460 if (game.gravity && is_player && !player->is_auto_moving &&
13461 canFallDown(player) && move_direction != MV_DOWN &&
13462 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13463 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13466 if (player_can_move &&
13467 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13469 int sound_element = SND_ELEMENT(element);
13470 int sound_action = ACTION_WALKING;
13472 if (IS_RND_GATE(element))
13474 if (!player->key[RND_GATE_NR(element)])
13475 return MP_NO_ACTION;
13477 else if (IS_RND_GATE_GRAY(element))
13479 if (!player->key[RND_GATE_GRAY_NR(element)])
13480 return MP_NO_ACTION;
13482 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13484 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13485 return MP_NO_ACTION;
13487 else if (element == EL_EXIT_OPEN ||
13488 element == EL_EM_EXIT_OPEN ||
13489 element == EL_STEEL_EXIT_OPEN ||
13490 element == EL_EM_STEEL_EXIT_OPEN ||
13491 element == EL_SP_EXIT_OPEN ||
13492 element == EL_SP_EXIT_OPENING)
13494 sound_action = ACTION_PASSING; /* player is passing exit */
13496 else if (element == EL_EMPTY)
13498 sound_action = ACTION_MOVING; /* nothing to walk on */
13501 /* play sound from background or player, whatever is available */
13502 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13503 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13505 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13507 else if (player_can_move &&
13508 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13510 if (!ACCESS_FROM(element, opposite_direction))
13511 return MP_NO_ACTION; /* field not accessible from this direction */
13513 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13514 return MP_NO_ACTION;
13516 if (IS_EM_GATE(element))
13518 if (!player->key[EM_GATE_NR(element)])
13519 return MP_NO_ACTION;
13521 else if (IS_EM_GATE_GRAY(element))
13523 if (!player->key[EM_GATE_GRAY_NR(element)])
13524 return MP_NO_ACTION;
13526 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13528 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13529 return MP_NO_ACTION;
13531 else if (IS_EMC_GATE(element))
13533 if (!player->key[EMC_GATE_NR(element)])
13534 return MP_NO_ACTION;
13536 else if (IS_EMC_GATE_GRAY(element))
13538 if (!player->key[EMC_GATE_GRAY_NR(element)])
13539 return MP_NO_ACTION;
13541 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13543 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13544 return MP_NO_ACTION;
13546 else if (element == EL_DC_GATE_WHITE ||
13547 element == EL_DC_GATE_WHITE_GRAY ||
13548 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13550 if (player->num_white_keys == 0)
13551 return MP_NO_ACTION;
13553 player->num_white_keys--;
13555 else if (IS_SP_PORT(element))
13557 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13558 element == EL_SP_GRAVITY_PORT_RIGHT ||
13559 element == EL_SP_GRAVITY_PORT_UP ||
13560 element == EL_SP_GRAVITY_PORT_DOWN)
13561 #if USE_PLAYER_GRAVITY
13562 player->gravity = !player->gravity;
13564 game.gravity = !game.gravity;
13566 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13567 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13568 element == EL_SP_GRAVITY_ON_PORT_UP ||
13569 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13570 #if USE_PLAYER_GRAVITY
13571 player->gravity = TRUE;
13573 game.gravity = TRUE;
13575 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13576 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13577 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13578 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13579 #if USE_PLAYER_GRAVITY
13580 player->gravity = FALSE;
13582 game.gravity = FALSE;
13586 /* automatically move to the next field with double speed */
13587 player->programmed_action = move_direction;
13589 if (player->move_delay_reset_counter == 0)
13591 player->move_delay_reset_counter = 2; /* two double speed steps */
13593 DOUBLE_PLAYER_SPEED(player);
13596 PlayLevelSoundAction(x, y, ACTION_PASSING);
13598 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13602 if (mode != DF_SNAP)
13604 GfxElement[x][y] = GFX_ELEMENT(element);
13605 player->is_digging = TRUE;
13608 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13610 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13611 player->index_bit, dig_side);
13613 if (mode == DF_SNAP)
13615 #if USE_NEW_SNAP_DELAY
13616 if (level.block_snap_field)
13617 setFieldForSnapping(x, y, element, move_direction);
13619 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13621 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13624 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13625 player->index_bit, dig_side);
13628 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13632 if (is_player && mode != DF_SNAP)
13634 GfxElement[x][y] = element;
13635 player->is_collecting = TRUE;
13638 if (element == EL_SPEED_PILL)
13640 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13642 else if (element == EL_EXTRA_TIME && level.time > 0)
13644 TimeLeft += level.extra_time;
13647 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13649 DisplayGameControlValues();
13651 DrawGameValue_Time(TimeLeft);
13654 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13656 player->shield_normal_time_left += level.shield_normal_time;
13657 if (element == EL_SHIELD_DEADLY)
13658 player->shield_deadly_time_left += level.shield_deadly_time;
13660 else if (element == EL_DYNAMITE ||
13661 element == EL_EM_DYNAMITE ||
13662 element == EL_SP_DISK_RED)
13664 if (player->inventory_size < MAX_INVENTORY_SIZE)
13665 player->inventory_element[player->inventory_size++] = element;
13667 DrawGameDoorValues();
13669 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13671 player->dynabomb_count++;
13672 player->dynabombs_left++;
13674 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13676 player->dynabomb_size++;
13678 else if (element == EL_DYNABOMB_INCREASE_POWER)
13680 player->dynabomb_xl = TRUE;
13682 else if (IS_KEY(element))
13684 player->key[KEY_NR(element)] = TRUE;
13686 DrawGameDoorValues();
13688 else if (element == EL_DC_KEY_WHITE)
13690 player->num_white_keys++;
13692 /* display white keys? */
13693 /* DrawGameDoorValues(); */
13695 else if (IS_ENVELOPE(element))
13697 player->show_envelope = element;
13699 else if (element == EL_EMC_LENSES)
13701 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13703 RedrawAllInvisibleElementsForLenses();
13705 else if (element == EL_EMC_MAGNIFIER)
13707 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13709 RedrawAllInvisibleElementsForMagnifier();
13711 else if (IS_DROPPABLE(element) ||
13712 IS_THROWABLE(element)) /* can be collected and dropped */
13716 if (collect_count == 0)
13717 player->inventory_infinite_element = element;
13719 for (i = 0; i < collect_count; i++)
13720 if (player->inventory_size < MAX_INVENTORY_SIZE)
13721 player->inventory_element[player->inventory_size++] = element;
13723 DrawGameDoorValues();
13725 else if (collect_count > 0)
13727 local_player->gems_still_needed -= collect_count;
13728 if (local_player->gems_still_needed < 0)
13729 local_player->gems_still_needed = 0;
13732 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13734 DisplayGameControlValues();
13736 DrawGameValue_Emeralds(local_player->gems_still_needed);
13740 RaiseScoreElement(element);
13741 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13744 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13745 player->index_bit, dig_side);
13747 if (mode == DF_SNAP)
13749 #if USE_NEW_SNAP_DELAY
13750 if (level.block_snap_field)
13751 setFieldForSnapping(x, y, element, move_direction);
13753 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13755 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13758 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13759 player->index_bit, dig_side);
13762 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13764 if (mode == DF_SNAP && element != EL_BD_ROCK)
13765 return MP_NO_ACTION;
13767 if (CAN_FALL(element) && dy)
13768 return MP_NO_ACTION;
13770 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13771 !(element == EL_SPRING && level.use_spring_bug))
13772 return MP_NO_ACTION;
13774 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13775 ((move_direction & MV_VERTICAL &&
13776 ((element_info[element].move_pattern & MV_LEFT &&
13777 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13778 (element_info[element].move_pattern & MV_RIGHT &&
13779 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13780 (move_direction & MV_HORIZONTAL &&
13781 ((element_info[element].move_pattern & MV_UP &&
13782 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13783 (element_info[element].move_pattern & MV_DOWN &&
13784 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13785 return MP_NO_ACTION;
13787 /* do not push elements already moving away faster than player */
13788 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13789 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13790 return MP_NO_ACTION;
13792 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13794 if (player->push_delay_value == -1 || !player_was_pushing)
13795 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13797 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13799 if (player->push_delay_value == -1)
13800 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13802 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13804 if (!player->is_pushing)
13805 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13808 player->is_pushing = TRUE;
13809 player->is_active = TRUE;
13811 if (!(IN_LEV_FIELD(nextx, nexty) &&
13812 (IS_FREE(nextx, nexty) ||
13813 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13814 IS_SB_ELEMENT(element)))))
13815 return MP_NO_ACTION;
13817 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13818 return MP_NO_ACTION;
13820 if (player->push_delay == -1) /* new pushing; restart delay */
13821 player->push_delay = 0;
13823 if (player->push_delay < player->push_delay_value &&
13824 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13825 element != EL_SPRING && element != EL_BALLOON)
13827 /* make sure that there is no move delay before next try to push */
13828 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13829 player->move_delay = 0;
13831 return MP_NO_ACTION;
13834 if (IS_SB_ELEMENT(element))
13836 if (element == EL_SOKOBAN_FIELD_FULL)
13838 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13839 local_player->sokobanfields_still_needed++;
13842 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13844 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13845 local_player->sokobanfields_still_needed--;
13848 Feld[x][y] = EL_SOKOBAN_OBJECT;
13850 if (Back[x][y] == Back[nextx][nexty])
13851 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13852 else if (Back[x][y] != 0)
13853 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13856 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13859 if (local_player->sokobanfields_still_needed == 0 &&
13860 game.emulation == EMU_SOKOBAN)
13862 PlayerWins(player);
13864 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13868 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13870 InitMovingField(x, y, move_direction);
13871 GfxAction[x][y] = ACTION_PUSHING;
13873 if (mode == DF_SNAP)
13874 ContinueMoving(x, y);
13876 MovPos[x][y] = (dx != 0 ? dx : dy);
13878 Pushed[x][y] = TRUE;
13879 Pushed[nextx][nexty] = TRUE;
13881 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13882 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13884 player->push_delay_value = -1; /* get new value later */
13886 /* check for element change _after_ element has been pushed */
13887 if (game.use_change_when_pushing_bug)
13889 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13890 player->index_bit, dig_side);
13891 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13892 player->index_bit, dig_side);
13895 else if (IS_SWITCHABLE(element))
13897 if (PLAYER_SWITCHING(player, x, y))
13899 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13900 player->index_bit, dig_side);
13905 player->is_switching = TRUE;
13906 player->switch_x = x;
13907 player->switch_y = y;
13909 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13911 if (element == EL_ROBOT_WHEEL)
13913 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13917 DrawLevelField(x, y);
13919 else if (element == EL_SP_TERMINAL)
13923 SCAN_PLAYFIELD(xx, yy)
13925 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13927 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13928 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13931 else if (IS_BELT_SWITCH(element))
13933 ToggleBeltSwitch(x, y);
13935 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13936 element == EL_SWITCHGATE_SWITCH_DOWN ||
13937 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13938 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13940 ToggleSwitchgateSwitch(x, y);
13942 else if (element == EL_LIGHT_SWITCH ||
13943 element == EL_LIGHT_SWITCH_ACTIVE)
13945 ToggleLightSwitch(x, y);
13947 else if (element == EL_TIMEGATE_SWITCH ||
13948 element == EL_DC_TIMEGATE_SWITCH)
13950 ActivateTimegateSwitch(x, y);
13952 else if (element == EL_BALLOON_SWITCH_LEFT ||
13953 element == EL_BALLOON_SWITCH_RIGHT ||
13954 element == EL_BALLOON_SWITCH_UP ||
13955 element == EL_BALLOON_SWITCH_DOWN ||
13956 element == EL_BALLOON_SWITCH_NONE ||
13957 element == EL_BALLOON_SWITCH_ANY)
13959 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13960 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13961 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13962 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13963 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13966 else if (element == EL_LAMP)
13968 Feld[x][y] = EL_LAMP_ACTIVE;
13969 local_player->lights_still_needed--;
13971 ResetGfxAnimation(x, y);
13972 DrawLevelField(x, y);
13974 else if (element == EL_TIME_ORB_FULL)
13976 Feld[x][y] = EL_TIME_ORB_EMPTY;
13978 if (level.time > 0 || level.use_time_orb_bug)
13980 TimeLeft += level.time_orb_time;
13983 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13985 DisplayGameControlValues();
13987 DrawGameValue_Time(TimeLeft);
13991 ResetGfxAnimation(x, y);
13992 DrawLevelField(x, y);
13994 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13995 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13999 game.ball_state = !game.ball_state;
14001 SCAN_PLAYFIELD(xx, yy)
14003 int e = Feld[xx][yy];
14005 if (game.ball_state)
14007 if (e == EL_EMC_MAGIC_BALL)
14008 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14009 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14010 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14014 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14015 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14016 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14017 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14022 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14023 player->index_bit, dig_side);
14025 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14026 player->index_bit, dig_side);
14028 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14029 player->index_bit, dig_side);
14035 if (!PLAYER_SWITCHING(player, x, y))
14037 player->is_switching = TRUE;
14038 player->switch_x = x;
14039 player->switch_y = y;
14041 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14042 player->index_bit, dig_side);
14043 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14044 player->index_bit, dig_side);
14046 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14047 player->index_bit, dig_side);
14048 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14049 player->index_bit, dig_side);
14052 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14053 player->index_bit, dig_side);
14054 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14055 player->index_bit, dig_side);
14057 return MP_NO_ACTION;
14060 player->push_delay = -1;
14062 if (is_player) /* function can also be called by EL_PENGUIN */
14064 if (Feld[x][y] != element) /* really digged/collected something */
14066 player->is_collecting = !player->is_digging;
14067 player->is_active = TRUE;
14074 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14076 int jx = player->jx, jy = player->jy;
14077 int x = jx + dx, y = jy + dy;
14078 int snap_direction = (dx == -1 ? MV_LEFT :
14079 dx == +1 ? MV_RIGHT :
14081 dy == +1 ? MV_DOWN : MV_NONE);
14082 boolean can_continue_snapping = (level.continuous_snapping &&
14083 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14085 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14088 if (!player->active || !IN_LEV_FIELD(x, y))
14096 if (player->MovPos == 0)
14097 player->is_pushing = FALSE;
14099 player->is_snapping = FALSE;
14101 if (player->MovPos == 0)
14103 player->is_moving = FALSE;
14104 player->is_digging = FALSE;
14105 player->is_collecting = FALSE;
14111 #if USE_NEW_CONTINUOUS_SNAPPING
14112 /* prevent snapping with already pressed snap key when not allowed */
14113 if (player->is_snapping && !can_continue_snapping)
14116 if (player->is_snapping)
14120 player->MovDir = snap_direction;
14122 if (player->MovPos == 0)
14124 player->is_moving = FALSE;
14125 player->is_digging = FALSE;
14126 player->is_collecting = FALSE;
14129 player->is_dropping = FALSE;
14130 player->is_dropping_pressed = FALSE;
14131 player->drop_pressed_delay = 0;
14133 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14136 player->is_snapping = TRUE;
14137 player->is_active = TRUE;
14139 if (player->MovPos == 0)
14141 player->is_moving = FALSE;
14142 player->is_digging = FALSE;
14143 player->is_collecting = FALSE;
14146 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14147 DrawLevelField(player->last_jx, player->last_jy);
14149 DrawLevelField(x, y);
14154 boolean DropElement(struct PlayerInfo *player)
14156 int old_element, new_element;
14157 int dropx = player->jx, dropy = player->jy;
14158 int drop_direction = player->MovDir;
14159 int drop_side = drop_direction;
14161 int drop_element = get_next_drop_element(player);
14163 int drop_element = (player->inventory_size > 0 ?
14164 player->inventory_element[player->inventory_size - 1] :
14165 player->inventory_infinite_element != EL_UNDEFINED ?
14166 player->inventory_infinite_element :
14167 player->dynabombs_left > 0 ?
14168 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14172 player->is_dropping_pressed = TRUE;
14174 /* do not drop an element on top of another element; when holding drop key
14175 pressed without moving, dropped element must move away before the next
14176 element can be dropped (this is especially important if the next element
14177 is dynamite, which can be placed on background for historical reasons) */
14178 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14181 if (IS_THROWABLE(drop_element))
14183 dropx += GET_DX_FROM_DIR(drop_direction);
14184 dropy += GET_DY_FROM_DIR(drop_direction);
14186 if (!IN_LEV_FIELD(dropx, dropy))
14190 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14191 new_element = drop_element; /* default: no change when dropping */
14193 /* check if player is active, not moving and ready to drop */
14194 if (!player->active || player->MovPos || player->drop_delay > 0)
14197 /* check if player has anything that can be dropped */
14198 if (new_element == EL_UNDEFINED)
14201 /* check if drop key was pressed long enough for EM style dynamite */
14202 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14205 /* check if anything can be dropped at the current position */
14206 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14209 /* collected custom elements can only be dropped on empty fields */
14210 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14213 if (old_element != EL_EMPTY)
14214 Back[dropx][dropy] = old_element; /* store old element on this field */
14216 ResetGfxAnimation(dropx, dropy);
14217 ResetRandomAnimationValue(dropx, dropy);
14219 if (player->inventory_size > 0 ||
14220 player->inventory_infinite_element != EL_UNDEFINED)
14222 if (player->inventory_size > 0)
14224 player->inventory_size--;
14226 DrawGameDoorValues();
14228 if (new_element == EL_DYNAMITE)
14229 new_element = EL_DYNAMITE_ACTIVE;
14230 else if (new_element == EL_EM_DYNAMITE)
14231 new_element = EL_EM_DYNAMITE_ACTIVE;
14232 else if (new_element == EL_SP_DISK_RED)
14233 new_element = EL_SP_DISK_RED_ACTIVE;
14236 Feld[dropx][dropy] = new_element;
14238 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14239 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14240 el2img(Feld[dropx][dropy]), 0);
14242 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14244 /* needed if previous element just changed to "empty" in the last frame */
14245 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14247 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14248 player->index_bit, drop_side);
14249 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14251 player->index_bit, drop_side);
14253 TestIfElementTouchesCustomElement(dropx, dropy);
14255 else /* player is dropping a dyna bomb */
14257 player->dynabombs_left--;
14259 Feld[dropx][dropy] = new_element;
14261 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14262 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14263 el2img(Feld[dropx][dropy]), 0);
14265 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14268 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14269 InitField_WithBug1(dropx, dropy, FALSE);
14271 new_element = Feld[dropx][dropy]; /* element might have changed */
14273 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14274 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14276 int move_direction, nextx, nexty;
14278 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14279 MovDir[dropx][dropy] = drop_direction;
14281 move_direction = MovDir[dropx][dropy];
14282 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14283 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14285 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14287 #if USE_FIX_IMPACT_COLLISION
14288 /* do not cause impact style collision by dropping elements that can fall */
14289 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14291 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14295 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14296 player->is_dropping = TRUE;
14298 player->drop_pressed_delay = 0;
14299 player->is_dropping_pressed = FALSE;
14301 player->drop_x = dropx;
14302 player->drop_y = dropy;
14307 /* ------------------------------------------------------------------------- */
14308 /* game sound playing functions */
14309 /* ------------------------------------------------------------------------- */
14311 static int *loop_sound_frame = NULL;
14312 static int *loop_sound_volume = NULL;
14314 void InitPlayLevelSound()
14316 int num_sounds = getSoundListSize();
14318 checked_free(loop_sound_frame);
14319 checked_free(loop_sound_volume);
14321 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14322 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14325 static void PlayLevelSound(int x, int y, int nr)
14327 int sx = SCREENX(x), sy = SCREENY(y);
14328 int volume, stereo_position;
14329 int max_distance = 8;
14330 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14332 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14333 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14336 if (!IN_LEV_FIELD(x, y) ||
14337 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14338 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14341 volume = SOUND_MAX_VOLUME;
14343 if (!IN_SCR_FIELD(sx, sy))
14345 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14346 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14348 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14351 stereo_position = (SOUND_MAX_LEFT +
14352 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14353 (SCR_FIELDX + 2 * max_distance));
14355 if (IS_LOOP_SOUND(nr))
14357 /* This assures that quieter loop sounds do not overwrite louder ones,
14358 while restarting sound volume comparison with each new game frame. */
14360 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14363 loop_sound_volume[nr] = volume;
14364 loop_sound_frame[nr] = FrameCounter;
14367 PlaySoundExt(nr, volume, stereo_position, type);
14370 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14372 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14373 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14374 y < LEVELY(BY1) ? LEVELY(BY1) :
14375 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14379 static void PlayLevelSoundAction(int x, int y, int action)
14381 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14384 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14386 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14388 if (sound_effect != SND_UNDEFINED)
14389 PlayLevelSound(x, y, sound_effect);
14392 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14395 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14397 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14398 PlayLevelSound(x, y, sound_effect);
14401 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14403 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14405 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14406 PlayLevelSound(x, y, sound_effect);
14409 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14411 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14413 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14414 StopSound(sound_effect);
14417 static void PlayLevelMusic()
14419 if (levelset.music[level_nr] != MUS_UNDEFINED)
14420 PlayMusic(levelset.music[level_nr]); /* from config file */
14422 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14425 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14427 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14428 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14429 int x = xx - 1 - offset;
14430 int y = yy - 1 - offset;
14435 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14439 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14443 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14447 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14451 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14455 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14459 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14462 case SAMPLE_android_clone:
14463 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14466 case SAMPLE_android_move:
14467 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14470 case SAMPLE_spring:
14471 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14475 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14479 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14482 case SAMPLE_eater_eat:
14483 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14487 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14490 case SAMPLE_collect:
14491 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14494 case SAMPLE_diamond:
14495 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14498 case SAMPLE_squash:
14499 /* !!! CHECK THIS !!! */
14501 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14503 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14507 case SAMPLE_wonderfall:
14508 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14512 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14516 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14520 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14524 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14528 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14532 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14535 case SAMPLE_wonder:
14536 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14540 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14543 case SAMPLE_exit_open:
14544 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14547 case SAMPLE_exit_leave:
14548 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14551 case SAMPLE_dynamite:
14552 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14556 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14560 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14564 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14568 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14572 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14576 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14580 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14586 void ChangeTime(int value)
14588 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14592 /* EMC game engine uses value from time counter of RND game engine */
14593 level.native_em_level->lev->time = *time;
14595 DrawGameValue_Time(*time);
14598 void RaiseScore(int value)
14600 /* EMC game engine and RND game engine have separate score counters */
14601 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14602 &level.native_em_level->lev->score : &local_player->score);
14606 DrawGameValue_Score(*score);
14610 void RaiseScore(int value)
14612 local_player->score += value;
14615 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14617 DisplayGameControlValues();
14619 DrawGameValue_Score(local_player->score);
14623 void RaiseScoreElement(int element)
14628 case EL_BD_DIAMOND:
14629 case EL_EMERALD_YELLOW:
14630 case EL_EMERALD_RED:
14631 case EL_EMERALD_PURPLE:
14632 case EL_SP_INFOTRON:
14633 RaiseScore(level.score[SC_EMERALD]);
14636 RaiseScore(level.score[SC_DIAMOND]);
14639 RaiseScore(level.score[SC_CRYSTAL]);
14642 RaiseScore(level.score[SC_PEARL]);
14645 case EL_BD_BUTTERFLY:
14646 case EL_SP_ELECTRON:
14647 RaiseScore(level.score[SC_BUG]);
14650 case EL_BD_FIREFLY:
14651 case EL_SP_SNIKSNAK:
14652 RaiseScore(level.score[SC_SPACESHIP]);
14655 case EL_DARK_YAMYAM:
14656 RaiseScore(level.score[SC_YAMYAM]);
14659 RaiseScore(level.score[SC_ROBOT]);
14662 RaiseScore(level.score[SC_PACMAN]);
14665 RaiseScore(level.score[SC_NUT]);
14668 case EL_EM_DYNAMITE:
14669 case EL_SP_DISK_RED:
14670 case EL_DYNABOMB_INCREASE_NUMBER:
14671 case EL_DYNABOMB_INCREASE_SIZE:
14672 case EL_DYNABOMB_INCREASE_POWER:
14673 RaiseScore(level.score[SC_DYNAMITE]);
14675 case EL_SHIELD_NORMAL:
14676 case EL_SHIELD_DEADLY:
14677 RaiseScore(level.score[SC_SHIELD]);
14679 case EL_EXTRA_TIME:
14680 RaiseScore(level.extra_time_score);
14694 case EL_DC_KEY_WHITE:
14695 RaiseScore(level.score[SC_KEY]);
14698 RaiseScore(element_info[element].collect_score);
14703 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14705 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14707 #if defined(NETWORK_AVALIABLE)
14708 if (options.network)
14709 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14718 FadeSkipNextFadeIn();
14720 fading = fading_none;
14724 OpenDoor(DOOR_CLOSE_1);
14727 game_status = GAME_MODE_MAIN;
14730 DrawAndFadeInMainMenu(REDRAW_FIELD);
14738 FadeOut(REDRAW_FIELD);
14741 game_status = GAME_MODE_MAIN;
14743 DrawAndFadeInMainMenu(REDRAW_FIELD);
14747 else /* continue playing the game */
14749 if (tape.playing && tape.deactivate_display)
14750 TapeDeactivateDisplayOff(TRUE);
14752 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14754 if (tape.playing && tape.deactivate_display)
14755 TapeDeactivateDisplayOn();
14759 void RequestQuitGame(boolean ask_if_really_quit)
14761 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14762 boolean skip_request = AllPlayersGone || quick_quit;
14764 RequestQuitGameExt(skip_request, quick_quit,
14765 "Do you really want to quit the game ?");
14769 /* ------------------------------------------------------------------------- */
14770 /* random generator functions */
14771 /* ------------------------------------------------------------------------- */
14773 unsigned int InitEngineRandom_RND(long seed)
14775 game.num_random_calls = 0;
14778 unsigned int rnd_seed = InitEngineRandom(seed);
14780 printf("::: START RND: %d\n", rnd_seed);
14785 return InitEngineRandom(seed);
14791 unsigned int RND(int max)
14795 game.num_random_calls++;
14797 return GetEngineRandom(max);
14804 /* ------------------------------------------------------------------------- */
14805 /* game engine snapshot handling functions */
14806 /* ------------------------------------------------------------------------- */
14808 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14810 struct EngineSnapshotInfo
14812 /* runtime values for custom element collect score */
14813 int collect_score[NUM_CUSTOM_ELEMENTS];
14815 /* runtime values for group element choice position */
14816 int choice_pos[NUM_GROUP_ELEMENTS];
14818 /* runtime values for belt position animations */
14819 int belt_graphic[4 * NUM_BELT_PARTS];
14820 int belt_anim_mode[4 * NUM_BELT_PARTS];
14823 struct EngineSnapshotNodeInfo
14830 static struct EngineSnapshotInfo engine_snapshot_rnd;
14831 static ListNode *engine_snapshot_list = NULL;
14832 static char *snapshot_level_identifier = NULL;
14833 static int snapshot_level_nr = -1;
14835 void FreeEngineSnapshot()
14837 while (engine_snapshot_list != NULL)
14838 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14841 setString(&snapshot_level_identifier, NULL);
14842 snapshot_level_nr = -1;
14845 static void SaveEngineSnapshotValues_RND()
14847 static int belt_base_active_element[4] =
14849 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14850 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14851 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14852 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14856 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14858 int element = EL_CUSTOM_START + i;
14860 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14863 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14865 int element = EL_GROUP_START + i;
14867 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14870 for (i = 0; i < 4; i++)
14872 for (j = 0; j < NUM_BELT_PARTS; j++)
14874 int element = belt_base_active_element[i] + j;
14875 int graphic = el2img(element);
14876 int anim_mode = graphic_info[graphic].anim_mode;
14878 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14879 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14884 static void LoadEngineSnapshotValues_RND()
14886 unsigned long num_random_calls = game.num_random_calls;
14889 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14891 int element = EL_CUSTOM_START + i;
14893 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14896 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14898 int element = EL_GROUP_START + i;
14900 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14903 for (i = 0; i < 4; i++)
14905 for (j = 0; j < NUM_BELT_PARTS; j++)
14907 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14908 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14910 graphic_info[graphic].anim_mode = anim_mode;
14914 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14916 InitRND(tape.random_seed);
14917 for (i = 0; i < num_random_calls; i++)
14921 if (game.num_random_calls != num_random_calls)
14923 Error(ERR_INFO, "number of random calls out of sync");
14924 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14925 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14926 Error(ERR_EXIT, "this should not happen -- please debug");
14930 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14932 struct EngineSnapshotNodeInfo *bi =
14933 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14935 bi->buffer_orig = buffer;
14936 bi->buffer_copy = checked_malloc(size);
14939 memcpy(bi->buffer_copy, buffer, size);
14941 addNodeToList(&engine_snapshot_list, NULL, bi);
14944 void SaveEngineSnapshot()
14946 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14948 if (level_editor_test_game) /* do not save snapshots from editor */
14951 /* copy some special values to a structure better suited for the snapshot */
14953 SaveEngineSnapshotValues_RND();
14954 SaveEngineSnapshotValues_EM();
14956 /* save values stored in special snapshot structure */
14958 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14959 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14961 /* save further RND engine values */
14963 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14964 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14965 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14967 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14968 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14969 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14970 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14972 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14973 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14974 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14975 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14976 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14978 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14979 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14980 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14982 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14984 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14986 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14987 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14989 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14990 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14991 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14992 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14993 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14994 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14995 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14996 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14997 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14998 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14999 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15000 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15001 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15002 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15003 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15004 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15005 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15006 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15008 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15009 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15011 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15012 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15013 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15015 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15016 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15018 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15019 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15020 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15021 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15022 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15024 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15025 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15027 /* save level identification information */
15029 setString(&snapshot_level_identifier, leveldir_current->identifier);
15030 snapshot_level_nr = level_nr;
15033 ListNode *node = engine_snapshot_list;
15036 while (node != NULL)
15038 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15043 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15047 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15049 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15052 void LoadEngineSnapshot()
15054 ListNode *node = engine_snapshot_list;
15056 if (engine_snapshot_list == NULL)
15059 while (node != NULL)
15061 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15066 /* restore special values from snapshot structure */
15068 LoadEngineSnapshotValues_RND();
15069 LoadEngineSnapshotValues_EM();
15072 boolean CheckEngineSnapshot()
15074 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15075 snapshot_level_nr == level_nr);
15079 /* ---------- new game button stuff ---------------------------------------- */
15081 /* graphic position values for game buttons */
15082 #define GAME_BUTTON_XSIZE 30
15083 #define GAME_BUTTON_YSIZE 30
15084 #define GAME_BUTTON_XPOS 5
15085 #define GAME_BUTTON_YPOS 215
15086 #define SOUND_BUTTON_XPOS 5
15087 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15089 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15090 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15091 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15092 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15093 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15094 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15102 } gamebutton_info[NUM_GAME_BUTTONS] =
15106 &game.button.stop.x, &game.button.stop.y,
15107 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15112 &game.button.pause.x, &game.button.pause.y,
15113 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15114 GAME_CTRL_ID_PAUSE,
15118 &game.button.play.x, &game.button.play.y,
15119 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15124 &game.button.sound_music.x, &game.button.sound_music.y,
15125 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15126 SOUND_CTRL_ID_MUSIC,
15127 "background music on/off"
15130 &game.button.sound_loops.x, &game.button.sound_loops.y,
15131 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15132 SOUND_CTRL_ID_LOOPS,
15133 "sound loops on/off"
15136 &game.button.sound_simple.x,&game.button.sound_simple.y,
15137 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15138 SOUND_CTRL_ID_SIMPLE,
15139 "normal sounds on/off"
15143 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15148 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15149 GAME_CTRL_ID_PAUSE,
15153 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15158 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15159 SOUND_CTRL_ID_MUSIC,
15160 "background music on/off"
15163 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15164 SOUND_CTRL_ID_LOOPS,
15165 "sound loops on/off"
15168 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15169 SOUND_CTRL_ID_SIMPLE,
15170 "normal sounds on/off"
15175 void CreateGameButtons()
15179 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15181 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15182 struct GadgetInfo *gi;
15185 unsigned long event_mask;
15187 int gd_xoffset, gd_yoffset;
15188 int gd_x1, gd_x2, gd_y1, gd_y2;
15191 x = DX + *gamebutton_info[i].x;
15192 y = DY + *gamebutton_info[i].y;
15193 gd_xoffset = gamebutton_info[i].gd_x;
15194 gd_yoffset = gamebutton_info[i].gd_y;
15195 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15196 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15198 if (id == GAME_CTRL_ID_STOP ||
15199 id == GAME_CTRL_ID_PAUSE ||
15200 id == GAME_CTRL_ID_PLAY)
15202 button_type = GD_TYPE_NORMAL_BUTTON;
15204 event_mask = GD_EVENT_RELEASED;
15205 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15206 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15210 button_type = GD_TYPE_CHECK_BUTTON;
15212 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15213 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15214 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15215 event_mask = GD_EVENT_PRESSED;
15216 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15217 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15220 gi = CreateGadget(GDI_CUSTOM_ID, id,
15221 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15226 GDI_X, DX + gd_xoffset,
15227 GDI_Y, DY + gd_yoffset,
15229 GDI_WIDTH, GAME_BUTTON_XSIZE,
15230 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15231 GDI_TYPE, button_type,
15232 GDI_STATE, GD_BUTTON_UNPRESSED,
15233 GDI_CHECKED, checked,
15234 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15235 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15236 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15237 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15238 GDI_EVENT_MASK, event_mask,
15239 GDI_CALLBACK_ACTION, HandleGameButtons,
15243 Error(ERR_EXIT, "cannot create gadget");
15245 game_gadget[id] = gi;
15249 void FreeGameButtons()
15253 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15254 FreeGadget(game_gadget[i]);
15257 static void MapGameButtons()
15261 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15262 MapGadget(game_gadget[i]);
15265 void UnmapGameButtons()
15269 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15270 UnmapGadget(game_gadget[i]);
15273 static void HandleGameButtons(struct GadgetInfo *gi)
15275 int id = gi->custom_id;
15277 if (game_status != GAME_MODE_PLAYING)
15282 case GAME_CTRL_ID_STOP:
15286 RequestQuitGame(TRUE);
15289 case GAME_CTRL_ID_PAUSE:
15290 if (options.network)
15292 #if defined(NETWORK_AVALIABLE)
15294 SendToServer_ContinuePlaying();
15296 SendToServer_PausePlaying();
15300 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15303 case GAME_CTRL_ID_PLAY:
15306 #if defined(NETWORK_AVALIABLE)
15307 if (options.network)
15308 SendToServer_ContinuePlaying();
15312 tape.pausing = FALSE;
15313 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15318 case SOUND_CTRL_ID_MUSIC:
15319 if (setup.sound_music)
15321 setup.sound_music = FALSE;
15324 else if (audio.music_available)
15326 setup.sound = setup.sound_music = TRUE;
15328 SetAudioMode(setup.sound);
15334 case SOUND_CTRL_ID_LOOPS:
15335 if (setup.sound_loops)
15336 setup.sound_loops = FALSE;
15337 else if (audio.loops_available)
15339 setup.sound = setup.sound_loops = TRUE;
15340 SetAudioMode(setup.sound);
15344 case SOUND_CTRL_ID_SIMPLE:
15345 if (setup.sound_simple)
15346 setup.sound_simple = FALSE;
15347 else if (audio.sound_available)
15349 setup.sound = setup.sound_simple = TRUE;
15350 SetAudioMode(setup.sound);