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);
3599 if (!game.restart_level)
3600 CloseDoor(DOOR_CLOSE_1);
3603 if (level_editor_test_game)
3604 FadeSkipNextFadeIn();
3606 FadeSetEnterScreen();
3608 if (level_editor_test_game)
3609 fading = fading_none;
3611 fading = menu.destination;
3615 FadeOut(REDRAW_FIELD);
3618 FadeOut(REDRAW_FIELD);
3621 /* !!! FIX THIS (START) !!! */
3622 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3624 InitGameEngine_EM();
3626 /* blit playfield from scroll buffer to normal back buffer for fading in */
3627 BlitScreenToBitmap_EM(backbuffer);
3634 /* after drawing the level, correct some elements */
3635 if (game.timegate_time_left == 0)
3636 CloseAllOpenTimegates();
3638 /* blit playfield from scroll buffer to normal back buffer for fading in */
3639 if (setup.soft_scrolling)
3640 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3642 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3644 /* !!! FIX THIS (END) !!! */
3647 FadeIn(REDRAW_FIELD);
3650 FadeIn(REDRAW_FIELD);
3655 if (!game.restart_level)
3657 /* copy default game door content to main double buffer */
3658 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3659 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3662 SetPanelBackground();
3663 SetDrawBackgroundMask(REDRAW_DOOR_1);
3665 DrawGameDoorValues();
3667 if (!game.restart_level)
3671 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3672 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3673 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3677 /* copy actual game door content to door double buffer for OpenDoor() */
3678 BlitBitmap(drawto, bitmap_db_door,
3679 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3681 OpenDoor(DOOR_OPEN_ALL);
3683 PlaySound(SND_GAME_STARTING);
3685 if (setup.sound_music)
3688 KeyboardAutoRepeatOffUnlessAutoplay();
3692 for (i = 0; i < MAX_PLAYERS; i++)
3693 printf("Player %d %sactive.\n",
3694 i + 1, (stored_player[i].active ? "" : "not "));
3705 game.restart_level = FALSE;
3708 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3710 /* this is used for non-R'n'D game engines to update certain engine values */
3712 /* needed to determine if sounds are played within the visible screen area */
3713 scroll_x = actual_scroll_x;
3714 scroll_y = actual_scroll_y;
3717 void InitMovDir(int x, int y)
3719 int i, element = Feld[x][y];
3720 static int xy[4][2] =
3727 static int direction[3][4] =
3729 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3730 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3731 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3740 Feld[x][y] = EL_BUG;
3741 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3744 case EL_SPACESHIP_RIGHT:
3745 case EL_SPACESHIP_UP:
3746 case EL_SPACESHIP_LEFT:
3747 case EL_SPACESHIP_DOWN:
3748 Feld[x][y] = EL_SPACESHIP;
3749 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3752 case EL_BD_BUTTERFLY_RIGHT:
3753 case EL_BD_BUTTERFLY_UP:
3754 case EL_BD_BUTTERFLY_LEFT:
3755 case EL_BD_BUTTERFLY_DOWN:
3756 Feld[x][y] = EL_BD_BUTTERFLY;
3757 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3760 case EL_BD_FIREFLY_RIGHT:
3761 case EL_BD_FIREFLY_UP:
3762 case EL_BD_FIREFLY_LEFT:
3763 case EL_BD_FIREFLY_DOWN:
3764 Feld[x][y] = EL_BD_FIREFLY;
3765 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3768 case EL_PACMAN_RIGHT:
3770 case EL_PACMAN_LEFT:
3771 case EL_PACMAN_DOWN:
3772 Feld[x][y] = EL_PACMAN;
3773 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3776 case EL_YAMYAM_LEFT:
3777 case EL_YAMYAM_RIGHT:
3779 case EL_YAMYAM_DOWN:
3780 Feld[x][y] = EL_YAMYAM;
3781 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3784 case EL_SP_SNIKSNAK:
3785 MovDir[x][y] = MV_UP;
3788 case EL_SP_ELECTRON:
3789 MovDir[x][y] = MV_LEFT;
3796 Feld[x][y] = EL_MOLE;
3797 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3801 if (IS_CUSTOM_ELEMENT(element))
3803 struct ElementInfo *ei = &element_info[element];
3804 int move_direction_initial = ei->move_direction_initial;
3805 int move_pattern = ei->move_pattern;
3807 if (move_direction_initial == MV_START_PREVIOUS)
3809 if (MovDir[x][y] != MV_NONE)
3812 move_direction_initial = MV_START_AUTOMATIC;
3815 if (move_direction_initial == MV_START_RANDOM)
3816 MovDir[x][y] = 1 << RND(4);
3817 else if (move_direction_initial & MV_ANY_DIRECTION)
3818 MovDir[x][y] = move_direction_initial;
3819 else if (move_pattern == MV_ALL_DIRECTIONS ||
3820 move_pattern == MV_TURNING_LEFT ||
3821 move_pattern == MV_TURNING_RIGHT ||
3822 move_pattern == MV_TURNING_LEFT_RIGHT ||
3823 move_pattern == MV_TURNING_RIGHT_LEFT ||
3824 move_pattern == MV_TURNING_RANDOM)
3825 MovDir[x][y] = 1 << RND(4);
3826 else if (move_pattern == MV_HORIZONTAL)
3827 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3828 else if (move_pattern == MV_VERTICAL)
3829 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3830 else if (move_pattern & MV_ANY_DIRECTION)
3831 MovDir[x][y] = element_info[element].move_pattern;
3832 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3833 move_pattern == MV_ALONG_RIGHT_SIDE)
3835 /* use random direction as default start direction */
3836 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3837 MovDir[x][y] = 1 << RND(4);
3839 for (i = 0; i < NUM_DIRECTIONS; i++)
3841 int x1 = x + xy[i][0];
3842 int y1 = y + xy[i][1];
3844 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3846 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3847 MovDir[x][y] = direction[0][i];
3849 MovDir[x][y] = direction[1][i];
3858 MovDir[x][y] = 1 << RND(4);
3860 if (element != EL_BUG &&
3861 element != EL_SPACESHIP &&
3862 element != EL_BD_BUTTERFLY &&
3863 element != EL_BD_FIREFLY)
3866 for (i = 0; i < NUM_DIRECTIONS; i++)
3868 int x1 = x + xy[i][0];
3869 int y1 = y + xy[i][1];
3871 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3873 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3875 MovDir[x][y] = direction[0][i];
3878 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3879 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3881 MovDir[x][y] = direction[1][i];
3890 GfxDir[x][y] = MovDir[x][y];
3893 void InitAmoebaNr(int x, int y)
3896 int group_nr = AmoebeNachbarNr(x, y);
3900 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3902 if (AmoebaCnt[i] == 0)
3910 AmoebaNr[x][y] = group_nr;
3911 AmoebaCnt[group_nr]++;
3912 AmoebaCnt2[group_nr]++;
3915 static void PlayerWins(struct PlayerInfo *player)
3917 player->LevelSolved = TRUE;
3918 player->GameOver = TRUE;
3920 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3921 level.native_em_level->lev->score : player->score);
3926 static int time, time_final;
3927 static int score, score_final;
3928 static int game_over_delay_1 = 0;
3929 static int game_over_delay_2 = 0;
3930 int game_over_delay_value_1 = 50;
3931 int game_over_delay_value_2 = 50;
3933 if (!local_player->LevelSolved_GameWon)
3937 /* do not start end game actions before the player stops moving (to exit) */
3938 if (local_player->MovPos)
3941 local_player->LevelSolved_GameWon = TRUE;
3942 local_player->LevelSolved_SaveTape = tape.recording;
3943 local_player->LevelSolved_SaveScore = !tape.playing;
3945 if (tape.auto_play) /* tape might already be stopped here */
3946 tape.auto_play_level_solved = TRUE;
3952 game_over_delay_1 = game_over_delay_value_1;
3953 game_over_delay_2 = game_over_delay_value_2;
3955 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3956 score = score_final = local_player->score_final;
3961 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3963 else if (level.time == 0 && TimePlayed < 999)
3966 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3969 local_player->score_final = score_final;
3971 if (level_editor_test_game)
3974 score = score_final;
3977 game_control_value[GAME_CONTROL_TIME] = time;
3978 game_control_value[GAME_CONTROL_SCORE] = score;
3980 DisplayGameControlValues();
3982 DrawGameValue_Time(time);
3983 DrawGameValue_Score(score);
3987 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3989 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3991 /* close exit door after last player */
3992 if ((AllPlayersGone &&
3993 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3994 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3995 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3996 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3997 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3999 int element = Feld[ExitX][ExitY];
4002 if (element == EL_EM_EXIT_OPEN ||
4003 element == EL_EM_STEEL_EXIT_OPEN)
4010 Feld[ExitX][ExitY] =
4011 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4012 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4013 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4014 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4015 EL_EM_STEEL_EXIT_CLOSING);
4017 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4021 /* player disappears */
4022 DrawLevelField(ExitX, ExitY);
4025 for (i = 0; i < MAX_PLAYERS; i++)
4027 struct PlayerInfo *player = &stored_player[i];
4029 if (player->present)
4031 RemovePlayer(player);
4033 /* player disappears */
4034 DrawLevelField(player->jx, player->jy);
4039 PlaySound(SND_GAME_WINNING);
4042 if (game_over_delay_1 > 0)
4044 game_over_delay_1--;
4049 if (time != time_final)
4051 int time_to_go = ABS(time_final - time);
4052 int time_count_dir = (time < time_final ? +1 : -1);
4053 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4055 time += time_count_steps * time_count_dir;
4056 score += time_count_steps * level.score[SC_TIME_BONUS];
4059 game_control_value[GAME_CONTROL_TIME] = time;
4060 game_control_value[GAME_CONTROL_SCORE] = score;
4062 DisplayGameControlValues();
4064 DrawGameValue_Time(time);
4065 DrawGameValue_Score(score);
4068 if (time == time_final)
4069 StopSound(SND_GAME_LEVELTIME_BONUS);
4070 else if (setup.sound_loops)
4071 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4073 PlaySound(SND_GAME_LEVELTIME_BONUS);
4078 local_player->LevelSolved_PanelOff = TRUE;
4080 if (game_over_delay_2 > 0)
4082 game_over_delay_2--;
4095 boolean raise_level = FALSE;
4097 local_player->LevelSolved_GameEnd = TRUE;
4099 CloseDoor(DOOR_CLOSE_1);
4101 if (local_player->LevelSolved_SaveTape)
4108 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4110 SaveTape(tape.level_nr); /* ask to save tape */
4114 if (level_editor_test_game)
4116 game_status = GAME_MODE_MAIN;
4119 DrawAndFadeInMainMenu(REDRAW_FIELD);
4127 if (!local_player->LevelSolved_SaveScore)
4130 FadeOut(REDRAW_FIELD);
4133 game_status = GAME_MODE_MAIN;
4135 DrawAndFadeInMainMenu(REDRAW_FIELD);
4140 if (level_nr == leveldir_current->handicap_level)
4142 leveldir_current->handicap_level++;
4143 SaveLevelSetup_SeriesInfo();
4146 if (level_nr < leveldir_current->last_level)
4147 raise_level = TRUE; /* advance to next level */
4149 if ((hi_pos = NewHiScore()) >= 0)
4151 game_status = GAME_MODE_SCORES;
4153 DrawHallOfFame(hi_pos);
4164 FadeOut(REDRAW_FIELD);
4167 game_status = GAME_MODE_MAIN;
4175 DrawAndFadeInMainMenu(REDRAW_FIELD);
4184 LoadScore(level_nr);
4186 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4187 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4190 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4192 if (local_player->score_final > highscore[k].Score)
4194 /* player has made it to the hall of fame */
4196 if (k < MAX_SCORE_ENTRIES - 1)
4198 int m = MAX_SCORE_ENTRIES - 1;
4201 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4202 if (strEqual(setup.player_name, highscore[l].Name))
4204 if (m == k) /* player's new highscore overwrites his old one */
4208 for (l = m; l > k; l--)
4210 strcpy(highscore[l].Name, highscore[l - 1].Name);
4211 highscore[l].Score = highscore[l - 1].Score;
4218 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4219 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4220 highscore[k].Score = local_player->score_final;
4226 else if (!strncmp(setup.player_name, highscore[k].Name,
4227 MAX_PLAYER_NAME_LEN))
4228 break; /* player already there with a higher score */
4234 SaveScore(level_nr);
4239 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4241 int element = Feld[x][y];
4242 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4243 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4244 int horiz_move = (dx != 0);
4245 int sign = (horiz_move ? dx : dy);
4246 int step = sign * element_info[element].move_stepsize;
4248 /* special values for move stepsize for spring and things on conveyor belt */
4251 if (CAN_FALL(element) &&
4252 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4253 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4254 else if (element == EL_SPRING)
4255 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4261 inline static int getElementMoveStepsize(int x, int y)
4263 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4266 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4268 if (player->GfxAction != action || player->GfxDir != dir)
4271 printf("Player frame reset! (%d => %d, %d => %d)\n",
4272 player->GfxAction, action, player->GfxDir, dir);
4275 player->GfxAction = action;
4276 player->GfxDir = dir;
4278 player->StepFrame = 0;
4282 #if USE_GFX_RESET_GFX_ANIMATION
4283 static void ResetGfxFrame(int x, int y, boolean redraw)
4285 int element = Feld[x][y];
4286 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4287 int last_gfx_frame = GfxFrame[x][y];
4289 if (graphic_info[graphic].anim_global_sync)
4290 GfxFrame[x][y] = FrameCounter;
4291 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4292 GfxFrame[x][y] = CustomValue[x][y];
4293 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4294 GfxFrame[x][y] = element_info[element].collect_score;
4295 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4296 GfxFrame[x][y] = ChangeDelay[x][y];
4298 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4299 DrawLevelGraphicAnimation(x, y, graphic);
4303 static void ResetGfxAnimation(int x, int y)
4305 GfxAction[x][y] = ACTION_DEFAULT;
4306 GfxDir[x][y] = MovDir[x][y];
4309 #if USE_GFX_RESET_GFX_ANIMATION
4310 ResetGfxFrame(x, y, FALSE);
4314 static void ResetRandomAnimationValue(int x, int y)
4316 GfxRandom[x][y] = INIT_GFX_RANDOM();
4319 void InitMovingField(int x, int y, int direction)
4321 int element = Feld[x][y];
4322 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4323 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4326 boolean is_moving_before, is_moving_after;
4328 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4331 /* check if element was/is moving or being moved before/after mode change */
4334 is_moving_before = (WasJustMoving[x][y] != 0);
4336 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4337 is_moving_before = WasJustMoving[x][y];
4340 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4342 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4344 /* reset animation only for moving elements which change direction of moving
4345 or which just started or stopped moving
4346 (else CEs with property "can move" / "not moving" are reset each frame) */
4347 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4349 if (is_moving_before != is_moving_after ||
4350 direction != MovDir[x][y])
4351 ResetGfxAnimation(x, y);
4353 if ((is_moving_before || is_moving_after) && !continues_moving)
4354 ResetGfxAnimation(x, y);
4357 if (!continues_moving)
4358 ResetGfxAnimation(x, y);
4361 MovDir[x][y] = direction;
4362 GfxDir[x][y] = direction;
4364 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4365 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4366 direction == MV_DOWN && CAN_FALL(element) ?
4367 ACTION_FALLING : ACTION_MOVING);
4369 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4370 ACTION_FALLING : ACTION_MOVING);
4373 /* this is needed for CEs with property "can move" / "not moving" */
4375 if (is_moving_after)
4377 if (Feld[newx][newy] == EL_EMPTY)
4378 Feld[newx][newy] = EL_BLOCKED;
4380 MovDir[newx][newy] = MovDir[x][y];
4382 #if USE_NEW_CUSTOM_VALUE
4383 CustomValue[newx][newy] = CustomValue[x][y];
4386 GfxFrame[newx][newy] = GfxFrame[x][y];
4387 GfxRandom[newx][newy] = GfxRandom[x][y];
4388 GfxAction[newx][newy] = GfxAction[x][y];
4389 GfxDir[newx][newy] = GfxDir[x][y];
4393 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4395 int direction = MovDir[x][y];
4396 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4397 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4403 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4405 int oldx = x, oldy = y;
4406 int direction = MovDir[x][y];
4408 if (direction == MV_LEFT)
4410 else if (direction == MV_RIGHT)
4412 else if (direction == MV_UP)
4414 else if (direction == MV_DOWN)
4417 *comes_from_x = oldx;
4418 *comes_from_y = oldy;
4421 int MovingOrBlocked2Element(int x, int y)
4423 int element = Feld[x][y];
4425 if (element == EL_BLOCKED)
4429 Blocked2Moving(x, y, &oldx, &oldy);
4430 return Feld[oldx][oldy];
4436 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4438 /* like MovingOrBlocked2Element(), but if element is moving
4439 and (x,y) is the field the moving element is just leaving,
4440 return EL_BLOCKED instead of the element value */
4441 int element = Feld[x][y];
4443 if (IS_MOVING(x, y))
4445 if (element == EL_BLOCKED)
4449 Blocked2Moving(x, y, &oldx, &oldy);
4450 return Feld[oldx][oldy];
4459 static void RemoveField(int x, int y)
4461 Feld[x][y] = EL_EMPTY;
4467 #if USE_NEW_CUSTOM_VALUE
4468 CustomValue[x][y] = 0;
4472 ChangeDelay[x][y] = 0;
4473 ChangePage[x][y] = -1;
4474 Pushed[x][y] = FALSE;
4477 ExplodeField[x][y] = EX_TYPE_NONE;
4480 GfxElement[x][y] = EL_UNDEFINED;
4481 GfxAction[x][y] = ACTION_DEFAULT;
4482 GfxDir[x][y] = MV_NONE;
4485 void RemoveMovingField(int x, int y)
4487 int oldx = x, oldy = y, newx = x, newy = y;
4488 int element = Feld[x][y];
4489 int next_element = EL_UNDEFINED;
4491 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4494 if (IS_MOVING(x, y))
4496 Moving2Blocked(x, y, &newx, &newy);
4498 if (Feld[newx][newy] != EL_BLOCKED)
4500 /* element is moving, but target field is not free (blocked), but
4501 already occupied by something different (example: acid pool);
4502 in this case, only remove the moving field, but not the target */
4504 RemoveField(oldx, oldy);
4506 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4508 DrawLevelField(oldx, oldy);
4513 else if (element == EL_BLOCKED)
4515 Blocked2Moving(x, y, &oldx, &oldy);
4516 if (!IS_MOVING(oldx, oldy))
4520 if (element == EL_BLOCKED &&
4521 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4522 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4523 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4524 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4525 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4526 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4527 next_element = get_next_element(Feld[oldx][oldy]);
4529 RemoveField(oldx, oldy);
4530 RemoveField(newx, newy);
4532 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4534 if (next_element != EL_UNDEFINED)
4535 Feld[oldx][oldy] = next_element;
4537 DrawLevelField(oldx, oldy);
4538 DrawLevelField(newx, newy);
4541 void DrawDynamite(int x, int y)
4543 int sx = SCREENX(x), sy = SCREENY(y);
4544 int graphic = el2img(Feld[x][y]);
4547 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4550 if (IS_WALKABLE_INSIDE(Back[x][y]))
4554 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4555 else if (Store[x][y])
4556 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4558 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4560 if (Back[x][y] || Store[x][y])
4561 DrawGraphicThruMask(sx, sy, graphic, frame);
4563 DrawGraphic(sx, sy, graphic, frame);
4566 void CheckDynamite(int x, int y)
4568 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4572 if (MovDelay[x][y] != 0)
4575 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4581 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4586 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4588 boolean num_checked_players = 0;
4591 for (i = 0; i < MAX_PLAYERS; i++)
4593 if (stored_player[i].active)
4595 int sx = stored_player[i].jx;
4596 int sy = stored_player[i].jy;
4598 if (num_checked_players == 0)
4605 *sx1 = MIN(*sx1, sx);
4606 *sy1 = MIN(*sy1, sy);
4607 *sx2 = MAX(*sx2, sx);
4608 *sy2 = MAX(*sy2, sy);
4611 num_checked_players++;
4616 static boolean checkIfAllPlayersFitToScreen_RND()
4618 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4620 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4622 return (sx2 - sx1 < SCR_FIELDX &&
4623 sy2 - sy1 < SCR_FIELDY);
4626 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4628 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4630 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4632 *sx = (sx1 + sx2) / 2;
4633 *sy = (sy1 + sy2) / 2;
4636 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4637 boolean center_screen, boolean quick_relocation)
4639 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4640 boolean no_delay = (tape.warp_forward);
4641 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4642 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4644 if (quick_relocation)
4646 int offset = game.scroll_delay_value;
4648 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4650 if (!level.shifted_relocation || center_screen)
4652 /* quick relocation (without scrolling), with centering of screen */
4654 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4655 x > SBX_Right + MIDPOSX ? SBX_Right :
4658 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4659 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4664 /* quick relocation (without scrolling), but do not center screen */
4666 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4667 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4670 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4671 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4674 int offset_x = x + (scroll_x - center_scroll_x);
4675 int offset_y = y + (scroll_y - center_scroll_y);
4677 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4678 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4679 offset_x - MIDPOSX);
4681 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4682 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4683 offset_y - MIDPOSY);
4688 /* quick relocation (without scrolling), inside visible screen area */
4690 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4691 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4692 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4694 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4695 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4696 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4698 /* don't scroll over playfield boundaries */
4699 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4700 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4702 /* don't scroll over playfield boundaries */
4703 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4704 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4707 RedrawPlayfield(TRUE, 0,0,0,0);
4712 int scroll_xx, scroll_yy;
4714 if (!level.shifted_relocation || center_screen)
4716 /* visible relocation (with scrolling), with centering of screen */
4718 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4719 x > SBX_Right + MIDPOSX ? SBX_Right :
4722 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4723 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4728 /* visible relocation (with scrolling), but do not center screen */
4730 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4731 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4734 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4735 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4738 int offset_x = x + (scroll_x - center_scroll_x);
4739 int offset_y = y + (scroll_y - center_scroll_y);
4741 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4742 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4743 offset_x - MIDPOSX);
4745 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4746 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4747 offset_y - MIDPOSY);
4752 /* visible relocation (with scrolling), with centering of screen */
4754 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4755 x > SBX_Right + MIDPOSX ? SBX_Right :
4758 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4759 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4763 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4765 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4768 int fx = FX, fy = FY;
4770 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4771 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4773 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4779 fx += dx * TILEX / 2;
4780 fy += dy * TILEY / 2;
4782 ScrollLevel(dx, dy);
4785 /* scroll in two steps of half tile size to make things smoother */
4786 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4788 Delay(wait_delay_value);
4790 /* scroll second step to align at full tile size */
4792 Delay(wait_delay_value);
4797 Delay(wait_delay_value);
4801 void RelocatePlayer(int jx, int jy, int el_player_raw)
4803 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4804 int player_nr = GET_PLAYER_NR(el_player);
4805 struct PlayerInfo *player = &stored_player[player_nr];
4806 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4807 boolean no_delay = (tape.warp_forward);
4808 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4809 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4810 int old_jx = player->jx;
4811 int old_jy = player->jy;
4812 int old_element = Feld[old_jx][old_jy];
4813 int element = Feld[jx][jy];
4814 boolean player_relocated = (old_jx != jx || old_jy != jy);
4816 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4817 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4818 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4819 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4820 int leave_side_horiz = move_dir_horiz;
4821 int leave_side_vert = move_dir_vert;
4822 int enter_side = enter_side_horiz | enter_side_vert;
4823 int leave_side = leave_side_horiz | leave_side_vert;
4825 if (player->GameOver) /* do not reanimate dead player */
4828 if (!player_relocated) /* no need to relocate the player */
4831 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4833 RemoveField(jx, jy); /* temporarily remove newly placed player */
4834 DrawLevelField(jx, jy);
4837 if (player->present)
4839 while (player->MovPos)
4841 ScrollPlayer(player, SCROLL_GO_ON);
4842 ScrollScreen(NULL, SCROLL_GO_ON);
4844 AdvanceFrameAndPlayerCounters(player->index_nr);
4849 Delay(wait_delay_value);
4852 DrawPlayer(player); /* needed here only to cleanup last field */
4853 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4855 player->is_moving = FALSE;
4858 if (IS_CUSTOM_ELEMENT(old_element))
4859 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4861 player->index_bit, leave_side);
4863 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4865 player->index_bit, leave_side);
4867 Feld[jx][jy] = el_player;
4868 InitPlayerField(jx, jy, el_player, TRUE);
4870 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4872 Feld[jx][jy] = element;
4873 InitField(jx, jy, FALSE);
4876 /* only visually relocate centered player */
4877 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4878 FALSE, level.instant_relocation);
4880 TestIfPlayerTouchesBadThing(jx, jy);
4881 TestIfPlayerTouchesCustomElement(jx, jy);
4883 if (IS_CUSTOM_ELEMENT(element))
4884 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4885 player->index_bit, enter_side);
4887 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4888 player->index_bit, enter_side);
4891 void Explode(int ex, int ey, int phase, int mode)
4897 /* !!! eliminate this variable !!! */
4898 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4900 if (game.explosions_delayed)
4902 ExplodeField[ex][ey] = mode;
4906 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4908 int center_element = Feld[ex][ey];
4909 int artwork_element, explosion_element; /* set these values later */
4912 /* --- This is only really needed (and now handled) in "Impact()". --- */
4913 /* do not explode moving elements that left the explode field in time */
4914 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4915 center_element == EL_EMPTY &&
4916 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4921 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4922 if (mode == EX_TYPE_NORMAL ||
4923 mode == EX_TYPE_CENTER ||
4924 mode == EX_TYPE_CROSS)
4925 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4928 /* remove things displayed in background while burning dynamite */
4929 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4932 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4934 /* put moving element to center field (and let it explode there) */
4935 center_element = MovingOrBlocked2Element(ex, ey);
4936 RemoveMovingField(ex, ey);
4937 Feld[ex][ey] = center_element;
4940 /* now "center_element" is finally determined -- set related values now */
4941 artwork_element = center_element; /* for custom player artwork */
4942 explosion_element = center_element; /* for custom player artwork */
4944 if (IS_PLAYER(ex, ey))
4946 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4948 artwork_element = stored_player[player_nr].artwork_element;
4950 if (level.use_explosion_element[player_nr])
4952 explosion_element = level.explosion_element[player_nr];
4953 artwork_element = explosion_element;
4958 if (mode == EX_TYPE_NORMAL ||
4959 mode == EX_TYPE_CENTER ||
4960 mode == EX_TYPE_CROSS)
4961 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4964 last_phase = element_info[explosion_element].explosion_delay + 1;
4966 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4968 int xx = x - ex + 1;
4969 int yy = y - ey + 1;
4972 if (!IN_LEV_FIELD(x, y) ||
4973 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4974 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4977 element = Feld[x][y];
4979 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4981 element = MovingOrBlocked2Element(x, y);
4983 if (!IS_EXPLOSION_PROOF(element))
4984 RemoveMovingField(x, y);
4987 /* indestructible elements can only explode in center (but not flames) */
4988 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4989 mode == EX_TYPE_BORDER)) ||
4990 element == EL_FLAMES)
4993 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4994 behaviour, for example when touching a yamyam that explodes to rocks
4995 with active deadly shield, a rock is created under the player !!! */
4996 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4998 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4999 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5000 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5002 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5005 if (IS_ACTIVE_BOMB(element))
5007 /* re-activate things under the bomb like gate or penguin */
5008 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5015 /* save walkable background elements while explosion on same tile */
5016 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5017 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5018 Back[x][y] = element;
5020 /* ignite explodable elements reached by other explosion */
5021 if (element == EL_EXPLOSION)
5022 element = Store2[x][y];
5024 if (AmoebaNr[x][y] &&
5025 (element == EL_AMOEBA_FULL ||
5026 element == EL_BD_AMOEBA ||
5027 element == EL_AMOEBA_GROWING))
5029 AmoebaCnt[AmoebaNr[x][y]]--;
5030 AmoebaCnt2[AmoebaNr[x][y]]--;
5035 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5037 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5039 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5041 if (PLAYERINFO(ex, ey)->use_murphy)
5042 Store[x][y] = EL_EMPTY;
5045 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5046 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5047 else if (ELEM_IS_PLAYER(center_element))
5048 Store[x][y] = EL_EMPTY;
5049 else if (center_element == EL_YAMYAM)
5050 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5051 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5052 Store[x][y] = element_info[center_element].content.e[xx][yy];
5054 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5055 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5056 otherwise) -- FIX THIS !!! */
5057 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5058 Store[x][y] = element_info[element].content.e[1][1];
5060 else if (!CAN_EXPLODE(element))
5061 Store[x][y] = element_info[element].content.e[1][1];
5064 Store[x][y] = EL_EMPTY;
5066 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5067 center_element == EL_AMOEBA_TO_DIAMOND)
5068 Store2[x][y] = element;
5070 Feld[x][y] = EL_EXPLOSION;
5071 GfxElement[x][y] = artwork_element;
5073 ExplodePhase[x][y] = 1;
5074 ExplodeDelay[x][y] = last_phase;
5079 if (center_element == EL_YAMYAM)
5080 game.yamyam_content_nr =
5081 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5093 GfxFrame[x][y] = 0; /* restart explosion animation */
5095 last_phase = ExplodeDelay[x][y];
5097 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5101 /* activate this even in non-DEBUG version until cause for crash in
5102 getGraphicAnimationFrame() (see below) is found and eliminated */
5108 /* this can happen if the player leaves an explosion just in time */
5109 if (GfxElement[x][y] == EL_UNDEFINED)
5110 GfxElement[x][y] = EL_EMPTY;
5112 if (GfxElement[x][y] == EL_UNDEFINED)
5115 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5116 printf("Explode(): This should never happen!\n");
5119 GfxElement[x][y] = EL_EMPTY;
5125 border_element = Store2[x][y];
5126 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5127 border_element = StorePlayer[x][y];
5129 if (phase == element_info[border_element].ignition_delay ||
5130 phase == last_phase)
5132 boolean border_explosion = FALSE;
5134 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5135 !PLAYER_EXPLOSION_PROTECTED(x, y))
5137 KillPlayerUnlessExplosionProtected(x, y);
5138 border_explosion = TRUE;
5140 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5142 Feld[x][y] = Store2[x][y];
5145 border_explosion = TRUE;
5147 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5149 AmoebeUmwandeln(x, y);
5151 border_explosion = TRUE;
5154 /* if an element just explodes due to another explosion (chain-reaction),
5155 do not immediately end the new explosion when it was the last frame of
5156 the explosion (as it would be done in the following "if"-statement!) */
5157 if (border_explosion && phase == last_phase)
5161 if (phase == last_phase)
5165 element = Feld[x][y] = Store[x][y];
5166 Store[x][y] = Store2[x][y] = 0;
5167 GfxElement[x][y] = EL_UNDEFINED;
5169 /* player can escape from explosions and might therefore be still alive */
5170 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5171 element <= EL_PLAYER_IS_EXPLODING_4)
5173 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5174 int explosion_element = EL_PLAYER_1 + player_nr;
5175 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5176 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5178 if (level.use_explosion_element[player_nr])
5179 explosion_element = level.explosion_element[player_nr];
5181 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5182 element_info[explosion_element].content.e[xx][yy]);
5185 /* restore probably existing indestructible background element */
5186 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5187 element = Feld[x][y] = Back[x][y];
5190 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5191 GfxDir[x][y] = MV_NONE;
5192 ChangeDelay[x][y] = 0;
5193 ChangePage[x][y] = -1;
5195 #if USE_NEW_CUSTOM_VALUE
5196 CustomValue[x][y] = 0;
5199 InitField_WithBug2(x, y, FALSE);
5201 DrawLevelField(x, y);
5203 TestIfElementTouchesCustomElement(x, y);
5205 if (GFX_CRUMBLED(element))
5206 DrawLevelFieldCrumbledSandNeighbours(x, y);
5208 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5209 StorePlayer[x][y] = 0;
5211 if (ELEM_IS_PLAYER(element))
5212 RelocatePlayer(x, y, element);
5214 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5216 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5217 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5220 DrawLevelFieldCrumbledSand(x, y);
5222 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5224 DrawLevelElement(x, y, Back[x][y]);
5225 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5227 else if (IS_WALKABLE_UNDER(Back[x][y]))
5229 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5230 DrawLevelElementThruMask(x, y, Back[x][y]);
5232 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5233 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5237 void DynaExplode(int ex, int ey)
5240 int dynabomb_element = Feld[ex][ey];
5241 int dynabomb_size = 1;
5242 boolean dynabomb_xl = FALSE;
5243 struct PlayerInfo *player;
5244 static int xy[4][2] =
5252 if (IS_ACTIVE_BOMB(dynabomb_element))
5254 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5255 dynabomb_size = player->dynabomb_size;
5256 dynabomb_xl = player->dynabomb_xl;
5257 player->dynabombs_left++;
5260 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5262 for (i = 0; i < NUM_DIRECTIONS; i++)
5264 for (j = 1; j <= dynabomb_size; j++)
5266 int x = ex + j * xy[i][0];
5267 int y = ey + j * xy[i][1];
5270 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5273 element = Feld[x][y];
5275 /* do not restart explosions of fields with active bombs */
5276 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5279 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5281 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5282 !IS_DIGGABLE(element) && !dynabomb_xl)
5288 void Bang(int x, int y)
5290 int element = MovingOrBlocked2Element(x, y);
5291 int explosion_type = EX_TYPE_NORMAL;
5293 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5295 struct PlayerInfo *player = PLAYERINFO(x, y);
5297 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5298 player->element_nr);
5300 if (level.use_explosion_element[player->index_nr])
5302 int explosion_element = level.explosion_element[player->index_nr];
5304 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5305 explosion_type = EX_TYPE_CROSS;
5306 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5307 explosion_type = EX_TYPE_CENTER;
5315 case EL_BD_BUTTERFLY:
5318 case EL_DARK_YAMYAM:
5322 RaiseScoreElement(element);
5325 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5326 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5327 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5328 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5329 case EL_DYNABOMB_INCREASE_NUMBER:
5330 case EL_DYNABOMB_INCREASE_SIZE:
5331 case EL_DYNABOMB_INCREASE_POWER:
5332 explosion_type = EX_TYPE_DYNA;
5335 case EL_DC_LANDMINE:
5337 case EL_EM_EXIT_OPEN:
5338 case EL_EM_STEEL_EXIT_OPEN:
5340 explosion_type = EX_TYPE_CENTER;
5345 case EL_LAMP_ACTIVE:
5346 case EL_AMOEBA_TO_DIAMOND:
5347 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5348 explosion_type = EX_TYPE_CENTER;
5352 if (element_info[element].explosion_type == EXPLODES_CROSS)
5353 explosion_type = EX_TYPE_CROSS;
5354 else if (element_info[element].explosion_type == EXPLODES_1X1)
5355 explosion_type = EX_TYPE_CENTER;
5359 if (explosion_type == EX_TYPE_DYNA)
5362 Explode(x, y, EX_PHASE_START, explosion_type);
5364 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5367 void SplashAcid(int x, int y)
5369 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5370 (!IN_LEV_FIELD(x - 1, y - 2) ||
5371 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5372 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
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_RIGHT;
5379 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5382 static void InitBeltMovement()
5384 static int belt_base_element[4] =
5386 EL_CONVEYOR_BELT_1_LEFT,
5387 EL_CONVEYOR_BELT_2_LEFT,
5388 EL_CONVEYOR_BELT_3_LEFT,
5389 EL_CONVEYOR_BELT_4_LEFT
5391 static int belt_base_active_element[4] =
5393 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5394 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5395 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5396 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5401 /* set frame order for belt animation graphic according to belt direction */
5402 for (i = 0; i < NUM_BELTS; i++)
5406 for (j = 0; j < NUM_BELT_PARTS; j++)
5408 int element = belt_base_active_element[belt_nr] + j;
5409 int graphic = el2img(element);
5411 if (game.belt_dir[i] == MV_LEFT)
5412 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5414 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5418 SCAN_PLAYFIELD(x, y)
5420 int element = Feld[x][y];
5422 for (i = 0; i < NUM_BELTS; i++)
5424 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5426 int e_belt_nr = getBeltNrFromBeltElement(element);
5429 if (e_belt_nr == belt_nr)
5431 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5433 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5440 static void ToggleBeltSwitch(int x, int y)
5442 static int belt_base_element[4] =
5444 EL_CONVEYOR_BELT_1_LEFT,
5445 EL_CONVEYOR_BELT_2_LEFT,
5446 EL_CONVEYOR_BELT_3_LEFT,
5447 EL_CONVEYOR_BELT_4_LEFT
5449 static int belt_base_active_element[4] =
5451 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5452 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5453 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5454 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5456 static int belt_base_switch_element[4] =
5458 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5459 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5460 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5461 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5463 static int belt_move_dir[4] =
5471 int element = Feld[x][y];
5472 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5473 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5474 int belt_dir = belt_move_dir[belt_dir_nr];
5477 if (!IS_BELT_SWITCH(element))
5480 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5481 game.belt_dir[belt_nr] = belt_dir;
5483 if (belt_dir_nr == 3)
5486 /* set frame order for belt animation graphic according to belt direction */
5487 for (i = 0; i < NUM_BELT_PARTS; i++)
5489 int element = belt_base_active_element[belt_nr] + i;
5490 int graphic = el2img(element);
5492 if (belt_dir == MV_LEFT)
5493 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5495 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5498 SCAN_PLAYFIELD(xx, yy)
5500 int element = Feld[xx][yy];
5502 if (IS_BELT_SWITCH(element))
5504 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5506 if (e_belt_nr == belt_nr)
5508 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5509 DrawLevelField(xx, yy);
5512 else if (IS_BELT(element) && belt_dir != MV_NONE)
5514 int e_belt_nr = getBeltNrFromBeltElement(element);
5516 if (e_belt_nr == belt_nr)
5518 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5520 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5521 DrawLevelField(xx, yy);
5524 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5526 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5528 if (e_belt_nr == belt_nr)
5530 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5532 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5533 DrawLevelField(xx, yy);
5539 static void ToggleSwitchgateSwitch(int x, int y)
5543 game.switchgate_pos = !game.switchgate_pos;
5545 SCAN_PLAYFIELD(xx, yy)
5547 int element = Feld[xx][yy];
5549 #if !USE_BOTH_SWITCHGATE_SWITCHES
5550 if (element == EL_SWITCHGATE_SWITCH_UP ||
5551 element == EL_SWITCHGATE_SWITCH_DOWN)
5553 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5554 DrawLevelField(xx, yy);
5556 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5557 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5559 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5560 DrawLevelField(xx, yy);
5563 if (element == EL_SWITCHGATE_SWITCH_UP)
5565 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5566 DrawLevelField(xx, yy);
5568 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5570 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5571 DrawLevelField(xx, yy);
5573 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5575 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5576 DrawLevelField(xx, yy);
5578 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5580 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5581 DrawLevelField(xx, yy);
5584 else if (element == EL_SWITCHGATE_OPEN ||
5585 element == EL_SWITCHGATE_OPENING)
5587 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5589 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5591 else if (element == EL_SWITCHGATE_CLOSED ||
5592 element == EL_SWITCHGATE_CLOSING)
5594 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5596 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5601 static int getInvisibleActiveFromInvisibleElement(int element)
5603 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5604 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5605 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5609 static int getInvisibleFromInvisibleActiveElement(int element)
5611 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5612 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5613 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5617 static void RedrawAllLightSwitchesAndInvisibleElements()
5621 SCAN_PLAYFIELD(x, y)
5623 int element = Feld[x][y];
5625 if (element == EL_LIGHT_SWITCH &&
5626 game.light_time_left > 0)
5628 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5629 DrawLevelField(x, y);
5631 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5632 game.light_time_left == 0)
5634 Feld[x][y] = EL_LIGHT_SWITCH;
5635 DrawLevelField(x, y);
5637 else if (element == EL_EMC_DRIPPER &&
5638 game.light_time_left > 0)
5640 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5641 DrawLevelField(x, y);
5643 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5644 game.light_time_left == 0)
5646 Feld[x][y] = EL_EMC_DRIPPER;
5647 DrawLevelField(x, y);
5649 else if (element == EL_INVISIBLE_STEELWALL ||
5650 element == EL_INVISIBLE_WALL ||
5651 element == EL_INVISIBLE_SAND)
5653 if (game.light_time_left > 0)
5654 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5656 DrawLevelField(x, y);
5658 /* uncrumble neighbour fields, if needed */
5659 if (element == EL_INVISIBLE_SAND)
5660 DrawLevelFieldCrumbledSandNeighbours(x, y);
5662 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5663 element == EL_INVISIBLE_WALL_ACTIVE ||
5664 element == EL_INVISIBLE_SAND_ACTIVE)
5666 if (game.light_time_left == 0)
5667 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5669 DrawLevelField(x, y);
5671 /* re-crumble neighbour fields, if needed */
5672 if (element == EL_INVISIBLE_SAND)
5673 DrawLevelFieldCrumbledSandNeighbours(x, y);
5678 static void RedrawAllInvisibleElementsForLenses()
5682 SCAN_PLAYFIELD(x, y)
5684 int element = Feld[x][y];
5686 if (element == EL_EMC_DRIPPER &&
5687 game.lenses_time_left > 0)
5689 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5690 DrawLevelField(x, y);
5692 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5693 game.lenses_time_left == 0)
5695 Feld[x][y] = EL_EMC_DRIPPER;
5696 DrawLevelField(x, y);
5698 else if (element == EL_INVISIBLE_STEELWALL ||
5699 element == EL_INVISIBLE_WALL ||
5700 element == EL_INVISIBLE_SAND)
5702 if (game.lenses_time_left > 0)
5703 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5705 DrawLevelField(x, y);
5707 /* uncrumble neighbour fields, if needed */
5708 if (element == EL_INVISIBLE_SAND)
5709 DrawLevelFieldCrumbledSandNeighbours(x, y);
5711 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5712 element == EL_INVISIBLE_WALL_ACTIVE ||
5713 element == EL_INVISIBLE_SAND_ACTIVE)
5715 if (game.lenses_time_left == 0)
5716 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5718 DrawLevelField(x, y);
5720 /* re-crumble neighbour fields, if needed */
5721 if (element == EL_INVISIBLE_SAND)
5722 DrawLevelFieldCrumbledSandNeighbours(x, y);
5727 static void RedrawAllInvisibleElementsForMagnifier()
5731 SCAN_PLAYFIELD(x, y)
5733 int element = Feld[x][y];
5735 if (element == EL_EMC_FAKE_GRASS &&
5736 game.magnify_time_left > 0)
5738 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5739 DrawLevelField(x, y);
5741 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5742 game.magnify_time_left == 0)
5744 Feld[x][y] = EL_EMC_FAKE_GRASS;
5745 DrawLevelField(x, y);
5747 else if (IS_GATE_GRAY(element) &&
5748 game.magnify_time_left > 0)
5750 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5751 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5752 IS_EM_GATE_GRAY(element) ?
5753 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5754 IS_EMC_GATE_GRAY(element) ?
5755 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5757 DrawLevelField(x, y);
5759 else if (IS_GATE_GRAY_ACTIVE(element) &&
5760 game.magnify_time_left == 0)
5762 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5763 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5764 IS_EM_GATE_GRAY_ACTIVE(element) ?
5765 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5766 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5767 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5769 DrawLevelField(x, y);
5774 static void ToggleLightSwitch(int x, int y)
5776 int element = Feld[x][y];
5778 game.light_time_left =
5779 (element == EL_LIGHT_SWITCH ?
5780 level.time_light * FRAMES_PER_SECOND : 0);
5782 RedrawAllLightSwitchesAndInvisibleElements();
5785 static void ActivateTimegateSwitch(int x, int y)
5789 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5791 SCAN_PLAYFIELD(xx, yy)
5793 int element = Feld[xx][yy];
5795 if (element == EL_TIMEGATE_CLOSED ||
5796 element == EL_TIMEGATE_CLOSING)
5798 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5799 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5803 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5805 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5806 DrawLevelField(xx, yy);
5813 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5814 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5816 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5820 void Impact(int x, int y)
5822 boolean last_line = (y == lev_fieldy - 1);
5823 boolean object_hit = FALSE;
5824 boolean impact = (last_line || object_hit);
5825 int element = Feld[x][y];
5826 int smashed = EL_STEELWALL;
5828 if (!last_line) /* check if element below was hit */
5830 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5833 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5834 MovDir[x][y + 1] != MV_DOWN ||
5835 MovPos[x][y + 1] <= TILEY / 2));
5837 /* do not smash moving elements that left the smashed field in time */
5838 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5839 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5842 #if USE_QUICKSAND_IMPACT_BUGFIX
5843 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5845 RemoveMovingField(x, y + 1);
5846 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5847 Feld[x][y + 2] = EL_ROCK;
5848 DrawLevelField(x, y + 2);
5853 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5855 RemoveMovingField(x, y + 1);
5856 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5857 Feld[x][y + 2] = EL_ROCK;
5858 DrawLevelField(x, y + 2);
5865 smashed = MovingOrBlocked2Element(x, y + 1);
5867 impact = (last_line || object_hit);
5870 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5872 SplashAcid(x, y + 1);
5876 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5877 /* only reset graphic animation if graphic really changes after impact */
5879 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5881 ResetGfxAnimation(x, y);
5882 DrawLevelField(x, y);
5885 if (impact && CAN_EXPLODE_IMPACT(element))
5890 else if (impact && element == EL_PEARL &&
5891 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5893 ResetGfxAnimation(x, y);
5895 Feld[x][y] = EL_PEARL_BREAKING;
5896 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5899 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5901 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5906 if (impact && element == EL_AMOEBA_DROP)
5908 if (object_hit && IS_PLAYER(x, y + 1))
5909 KillPlayerUnlessEnemyProtected(x, y + 1);
5910 else if (object_hit && smashed == EL_PENGUIN)
5914 Feld[x][y] = EL_AMOEBA_GROWING;
5915 Store[x][y] = EL_AMOEBA_WET;
5917 ResetRandomAnimationValue(x, y);
5922 if (object_hit) /* check which object was hit */
5924 if ((CAN_PASS_MAGIC_WALL(element) &&
5925 (smashed == EL_MAGIC_WALL ||
5926 smashed == EL_BD_MAGIC_WALL)) ||
5927 (CAN_PASS_DC_MAGIC_WALL(element) &&
5928 smashed == EL_DC_MAGIC_WALL))
5931 int activated_magic_wall =
5932 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5933 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5934 EL_DC_MAGIC_WALL_ACTIVE);
5936 /* activate magic wall / mill */
5937 SCAN_PLAYFIELD(xx, yy)
5939 if (Feld[xx][yy] == smashed)
5940 Feld[xx][yy] = activated_magic_wall;
5943 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5944 game.magic_wall_active = TRUE;
5946 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5947 SND_MAGIC_WALL_ACTIVATING :
5948 smashed == EL_BD_MAGIC_WALL ?
5949 SND_BD_MAGIC_WALL_ACTIVATING :
5950 SND_DC_MAGIC_WALL_ACTIVATING));
5953 if (IS_PLAYER(x, y + 1))
5955 if (CAN_SMASH_PLAYER(element))
5957 KillPlayerUnlessEnemyProtected(x, y + 1);
5961 else if (smashed == EL_PENGUIN)
5963 if (CAN_SMASH_PLAYER(element))
5969 else if (element == EL_BD_DIAMOND)
5971 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5977 else if (((element == EL_SP_INFOTRON ||
5978 element == EL_SP_ZONK) &&
5979 (smashed == EL_SP_SNIKSNAK ||
5980 smashed == EL_SP_ELECTRON ||
5981 smashed == EL_SP_DISK_ORANGE)) ||
5982 (element == EL_SP_INFOTRON &&
5983 smashed == EL_SP_DISK_YELLOW))
5988 else if (CAN_SMASH_EVERYTHING(element))
5990 if (IS_CLASSIC_ENEMY(smashed) ||
5991 CAN_EXPLODE_SMASHED(smashed))
5996 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5998 if (smashed == EL_LAMP ||
5999 smashed == EL_LAMP_ACTIVE)
6004 else if (smashed == EL_NUT)
6006 Feld[x][y + 1] = EL_NUT_BREAKING;
6007 PlayLevelSound(x, y, SND_NUT_BREAKING);
6008 RaiseScoreElement(EL_NUT);
6011 else if (smashed == EL_PEARL)
6013 ResetGfxAnimation(x, y);
6015 Feld[x][y + 1] = EL_PEARL_BREAKING;
6016 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6019 else if (smashed == EL_DIAMOND)
6021 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6022 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6025 else if (IS_BELT_SWITCH(smashed))
6027 ToggleBeltSwitch(x, y + 1);
6029 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6030 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6031 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6032 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6034 ToggleSwitchgateSwitch(x, y + 1);
6036 else if (smashed == EL_LIGHT_SWITCH ||
6037 smashed == EL_LIGHT_SWITCH_ACTIVE)
6039 ToggleLightSwitch(x, y + 1);
6044 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6047 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6049 CheckElementChangeBySide(x, y + 1, smashed, element,
6050 CE_SWITCHED, CH_SIDE_TOP);
6051 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6057 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6062 /* play sound of magic wall / mill */
6064 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6065 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6066 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6068 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6069 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6070 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6071 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6072 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6073 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6078 /* play sound of object that hits the ground */
6079 if (last_line || object_hit)
6080 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6083 inline static void TurnRoundExt(int x, int y)
6095 { 0, 0 }, { 0, 0 }, { 0, 0 },
6100 int left, right, back;
6104 { MV_DOWN, MV_UP, MV_RIGHT },
6105 { MV_UP, MV_DOWN, MV_LEFT },
6107 { MV_LEFT, MV_RIGHT, MV_DOWN },
6111 { MV_RIGHT, MV_LEFT, MV_UP }
6114 int element = Feld[x][y];
6115 int move_pattern = element_info[element].move_pattern;
6117 int old_move_dir = MovDir[x][y];
6118 int left_dir = turn[old_move_dir].left;
6119 int right_dir = turn[old_move_dir].right;
6120 int back_dir = turn[old_move_dir].back;
6122 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6123 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6124 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6125 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6127 int left_x = x + left_dx, left_y = y + left_dy;
6128 int right_x = x + right_dx, right_y = y + right_dy;
6129 int move_x = x + move_dx, move_y = y + move_dy;
6133 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6135 TestIfBadThingTouchesOtherBadThing(x, y);
6137 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6138 MovDir[x][y] = right_dir;
6139 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6140 MovDir[x][y] = left_dir;
6142 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6144 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6147 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6149 TestIfBadThingTouchesOtherBadThing(x, y);
6151 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6152 MovDir[x][y] = left_dir;
6153 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6154 MovDir[x][y] = right_dir;
6156 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6158 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6161 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6163 TestIfBadThingTouchesOtherBadThing(x, y);
6165 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6166 MovDir[x][y] = left_dir;
6167 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6168 MovDir[x][y] = right_dir;
6170 if (MovDir[x][y] != old_move_dir)
6173 else if (element == EL_YAMYAM)
6175 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6176 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6178 if (can_turn_left && can_turn_right)
6179 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6180 else if (can_turn_left)
6181 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6182 else if (can_turn_right)
6183 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6185 MovDir[x][y] = back_dir;
6187 MovDelay[x][y] = 16 + 16 * RND(3);
6189 else if (element == EL_DARK_YAMYAM)
6191 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6193 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6196 if (can_turn_left && can_turn_right)
6197 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6198 else if (can_turn_left)
6199 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6200 else if (can_turn_right)
6201 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6203 MovDir[x][y] = back_dir;
6205 MovDelay[x][y] = 16 + 16 * RND(3);
6207 else if (element == EL_PACMAN)
6209 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6210 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6212 if (can_turn_left && can_turn_right)
6213 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6214 else if (can_turn_left)
6215 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6216 else if (can_turn_right)
6217 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6219 MovDir[x][y] = back_dir;
6221 MovDelay[x][y] = 6 + RND(40);
6223 else if (element == EL_PIG)
6225 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6226 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6227 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6228 boolean should_turn_left, should_turn_right, should_move_on;
6230 int rnd = RND(rnd_value);
6232 should_turn_left = (can_turn_left &&
6234 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6235 y + back_dy + left_dy)));
6236 should_turn_right = (can_turn_right &&
6238 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6239 y + back_dy + right_dy)));
6240 should_move_on = (can_move_on &&
6243 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6244 y + move_dy + left_dy) ||
6245 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6246 y + move_dy + right_dy)));
6248 if (should_turn_left || should_turn_right || should_move_on)
6250 if (should_turn_left && should_turn_right && should_move_on)
6251 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6252 rnd < 2 * rnd_value / 3 ? right_dir :
6254 else if (should_turn_left && should_turn_right)
6255 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6256 else if (should_turn_left && should_move_on)
6257 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6258 else if (should_turn_right && should_move_on)
6259 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6260 else if (should_turn_left)
6261 MovDir[x][y] = left_dir;
6262 else if (should_turn_right)
6263 MovDir[x][y] = right_dir;
6264 else if (should_move_on)
6265 MovDir[x][y] = old_move_dir;
6267 else if (can_move_on && rnd > rnd_value / 8)
6268 MovDir[x][y] = old_move_dir;
6269 else if (can_turn_left && can_turn_right)
6270 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6271 else if (can_turn_left && rnd > rnd_value / 8)
6272 MovDir[x][y] = left_dir;
6273 else if (can_turn_right && rnd > rnd_value/8)
6274 MovDir[x][y] = right_dir;
6276 MovDir[x][y] = back_dir;
6278 xx = x + move_xy[MovDir[x][y]].dx;
6279 yy = y + move_xy[MovDir[x][y]].dy;
6281 if (!IN_LEV_FIELD(xx, yy) ||
6282 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6283 MovDir[x][y] = old_move_dir;
6287 else if (element == EL_DRAGON)
6289 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6290 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6291 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6293 int rnd = RND(rnd_value);
6295 if (can_move_on && rnd > rnd_value / 8)
6296 MovDir[x][y] = old_move_dir;
6297 else if (can_turn_left && can_turn_right)
6298 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6299 else if (can_turn_left && rnd > rnd_value / 8)
6300 MovDir[x][y] = left_dir;
6301 else if (can_turn_right && rnd > rnd_value / 8)
6302 MovDir[x][y] = right_dir;
6304 MovDir[x][y] = back_dir;
6306 xx = x + move_xy[MovDir[x][y]].dx;
6307 yy = y + move_xy[MovDir[x][y]].dy;
6309 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6310 MovDir[x][y] = old_move_dir;
6314 else if (element == EL_MOLE)
6316 boolean can_move_on =
6317 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6318 IS_AMOEBOID(Feld[move_x][move_y]) ||
6319 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6322 boolean can_turn_left =
6323 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6324 IS_AMOEBOID(Feld[left_x][left_y])));
6326 boolean can_turn_right =
6327 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6328 IS_AMOEBOID(Feld[right_x][right_y])));
6330 if (can_turn_left && can_turn_right)
6331 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6332 else if (can_turn_left)
6333 MovDir[x][y] = left_dir;
6335 MovDir[x][y] = right_dir;
6338 if (MovDir[x][y] != old_move_dir)
6341 else if (element == EL_BALLOON)
6343 MovDir[x][y] = game.wind_direction;
6346 else if (element == EL_SPRING)
6348 #if USE_NEW_SPRING_BUMPER
6349 if (MovDir[x][y] & MV_HORIZONTAL)
6351 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6352 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6354 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6355 ResetGfxAnimation(move_x, move_y);
6356 DrawLevelField(move_x, move_y);
6358 MovDir[x][y] = back_dir;
6360 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6361 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6362 MovDir[x][y] = MV_NONE;
6365 if (MovDir[x][y] & MV_HORIZONTAL &&
6366 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6367 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6368 MovDir[x][y] = MV_NONE;
6373 else if (element == EL_ROBOT ||
6374 element == EL_SATELLITE ||
6375 element == EL_PENGUIN ||
6376 element == EL_EMC_ANDROID)
6378 int attr_x = -1, attr_y = -1;
6389 for (i = 0; i < MAX_PLAYERS; i++)
6391 struct PlayerInfo *player = &stored_player[i];
6392 int jx = player->jx, jy = player->jy;
6394 if (!player->active)
6398 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6406 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6407 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6408 game.engine_version < VERSION_IDENT(3,1,0,0)))
6414 if (element == EL_PENGUIN)
6417 static int xy[4][2] =
6425 for (i = 0; i < NUM_DIRECTIONS; i++)
6427 int ex = x + xy[i][0];
6428 int ey = y + xy[i][1];
6430 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6431 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6432 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6433 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6442 MovDir[x][y] = MV_NONE;
6444 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6445 else if (attr_x > x)
6446 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6448 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6449 else if (attr_y > y)
6450 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6452 if (element == EL_ROBOT)
6456 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6457 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6458 Moving2Blocked(x, y, &newx, &newy);
6460 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6461 MovDelay[x][y] = 8 + 8 * !RND(3);
6463 MovDelay[x][y] = 16;
6465 else if (element == EL_PENGUIN)
6471 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6473 boolean first_horiz = RND(2);
6474 int new_move_dir = MovDir[x][y];
6477 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6478 Moving2Blocked(x, y, &newx, &newy);
6480 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6484 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6485 Moving2Blocked(x, y, &newx, &newy);
6487 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6490 MovDir[x][y] = old_move_dir;
6494 else if (element == EL_SATELLITE)
6500 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6502 boolean first_horiz = RND(2);
6503 int new_move_dir = MovDir[x][y];
6506 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6507 Moving2Blocked(x, y, &newx, &newy);
6509 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6513 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6514 Moving2Blocked(x, y, &newx, &newy);
6516 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6519 MovDir[x][y] = old_move_dir;
6523 else if (element == EL_EMC_ANDROID)
6525 static int check_pos[16] =
6527 -1, /* 0 => (invalid) */
6528 7, /* 1 => MV_LEFT */
6529 3, /* 2 => MV_RIGHT */
6530 -1, /* 3 => (invalid) */
6532 0, /* 5 => MV_LEFT | MV_UP */
6533 2, /* 6 => MV_RIGHT | MV_UP */
6534 -1, /* 7 => (invalid) */
6535 5, /* 8 => MV_DOWN */
6536 6, /* 9 => MV_LEFT | MV_DOWN */
6537 4, /* 10 => MV_RIGHT | MV_DOWN */
6538 -1, /* 11 => (invalid) */
6539 -1, /* 12 => (invalid) */
6540 -1, /* 13 => (invalid) */
6541 -1, /* 14 => (invalid) */
6542 -1, /* 15 => (invalid) */
6550 { -1, -1, MV_LEFT | MV_UP },
6552 { +1, -1, MV_RIGHT | MV_UP },
6553 { +1, 0, MV_RIGHT },
6554 { +1, +1, MV_RIGHT | MV_DOWN },
6556 { -1, +1, MV_LEFT | MV_DOWN },
6559 int start_pos, check_order;
6560 boolean can_clone = FALSE;
6563 /* check if there is any free field around current position */
6564 for (i = 0; i < 8; i++)
6566 int newx = x + check_xy[i].dx;
6567 int newy = y + check_xy[i].dy;
6569 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6577 if (can_clone) /* randomly find an element to clone */
6581 start_pos = check_pos[RND(8)];
6582 check_order = (RND(2) ? -1 : +1);
6584 for (i = 0; i < 8; i++)
6586 int pos_raw = start_pos + i * check_order;
6587 int pos = (pos_raw + 8) % 8;
6588 int newx = x + check_xy[pos].dx;
6589 int newy = y + check_xy[pos].dy;
6591 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6593 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6594 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6596 Store[x][y] = Feld[newx][newy];
6605 if (can_clone) /* randomly find a direction to move */
6609 start_pos = check_pos[RND(8)];
6610 check_order = (RND(2) ? -1 : +1);
6612 for (i = 0; i < 8; i++)
6614 int pos_raw = start_pos + i * check_order;
6615 int pos = (pos_raw + 8) % 8;
6616 int newx = x + check_xy[pos].dx;
6617 int newy = y + check_xy[pos].dy;
6618 int new_move_dir = check_xy[pos].dir;
6620 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6622 MovDir[x][y] = new_move_dir;
6623 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6632 if (can_clone) /* cloning and moving successful */
6635 /* cannot clone -- try to move towards player */
6637 start_pos = check_pos[MovDir[x][y] & 0x0f];
6638 check_order = (RND(2) ? -1 : +1);
6640 for (i = 0; i < 3; i++)
6642 /* first check start_pos, then previous/next or (next/previous) pos */
6643 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6644 int pos = (pos_raw + 8) % 8;
6645 int newx = x + check_xy[pos].dx;
6646 int newy = y + check_xy[pos].dy;
6647 int new_move_dir = check_xy[pos].dir;
6649 if (IS_PLAYER(newx, newy))
6652 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6654 MovDir[x][y] = new_move_dir;
6655 MovDelay[x][y] = level.android_move_time * 8 + 1;
6662 else if (move_pattern == MV_TURNING_LEFT ||
6663 move_pattern == MV_TURNING_RIGHT ||
6664 move_pattern == MV_TURNING_LEFT_RIGHT ||
6665 move_pattern == MV_TURNING_RIGHT_LEFT ||
6666 move_pattern == MV_TURNING_RANDOM ||
6667 move_pattern == MV_ALL_DIRECTIONS)
6669 boolean can_turn_left =
6670 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6671 boolean can_turn_right =
6672 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6674 if (element_info[element].move_stepsize == 0) /* "not moving" */
6677 if (move_pattern == MV_TURNING_LEFT)
6678 MovDir[x][y] = left_dir;
6679 else if (move_pattern == MV_TURNING_RIGHT)
6680 MovDir[x][y] = right_dir;
6681 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6682 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6683 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6684 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6685 else if (move_pattern == MV_TURNING_RANDOM)
6686 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6687 can_turn_right && !can_turn_left ? right_dir :
6688 RND(2) ? left_dir : right_dir);
6689 else if (can_turn_left && can_turn_right)
6690 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6691 else if (can_turn_left)
6692 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6693 else if (can_turn_right)
6694 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6696 MovDir[x][y] = back_dir;
6698 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6700 else if (move_pattern == MV_HORIZONTAL ||
6701 move_pattern == MV_VERTICAL)
6703 if (move_pattern & old_move_dir)
6704 MovDir[x][y] = back_dir;
6705 else if (move_pattern == MV_HORIZONTAL)
6706 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6707 else if (move_pattern == MV_VERTICAL)
6708 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6710 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6712 else if (move_pattern & MV_ANY_DIRECTION)
6714 MovDir[x][y] = move_pattern;
6715 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6717 else if (move_pattern & MV_WIND_DIRECTION)
6719 MovDir[x][y] = game.wind_direction;
6720 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6722 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6724 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6725 MovDir[x][y] = left_dir;
6726 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6727 MovDir[x][y] = right_dir;
6729 if (MovDir[x][y] != old_move_dir)
6730 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6732 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6734 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6735 MovDir[x][y] = right_dir;
6736 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6737 MovDir[x][y] = left_dir;
6739 if (MovDir[x][y] != old_move_dir)
6740 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6742 else if (move_pattern == MV_TOWARDS_PLAYER ||
6743 move_pattern == MV_AWAY_FROM_PLAYER)
6745 int attr_x = -1, attr_y = -1;
6747 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6758 for (i = 0; i < MAX_PLAYERS; i++)
6760 struct PlayerInfo *player = &stored_player[i];
6761 int jx = player->jx, jy = player->jy;
6763 if (!player->active)
6767 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6775 MovDir[x][y] = MV_NONE;
6777 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6778 else if (attr_x > x)
6779 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6781 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6782 else if (attr_y > y)
6783 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6785 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6787 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6789 boolean first_horiz = RND(2);
6790 int new_move_dir = MovDir[x][y];
6792 if (element_info[element].move_stepsize == 0) /* "not moving" */
6794 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6795 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6801 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6802 Moving2Blocked(x, y, &newx, &newy);
6804 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6808 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6809 Moving2Blocked(x, y, &newx, &newy);
6811 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6814 MovDir[x][y] = old_move_dir;
6817 else if (move_pattern == MV_WHEN_PUSHED ||
6818 move_pattern == MV_WHEN_DROPPED)
6820 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6821 MovDir[x][y] = MV_NONE;
6825 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6827 static int test_xy[7][2] =
6837 static int test_dir[7] =
6847 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6848 int move_preference = -1000000; /* start with very low preference */
6849 int new_move_dir = MV_NONE;
6850 int start_test = RND(4);
6853 for (i = 0; i < NUM_DIRECTIONS; i++)
6855 int move_dir = test_dir[start_test + i];
6856 int move_dir_preference;
6858 xx = x + test_xy[start_test + i][0];
6859 yy = y + test_xy[start_test + i][1];
6861 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6862 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6864 new_move_dir = move_dir;
6869 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6872 move_dir_preference = -1 * RunnerVisit[xx][yy];
6873 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6874 move_dir_preference = PlayerVisit[xx][yy];
6876 if (move_dir_preference > move_preference)
6878 /* prefer field that has not been visited for the longest time */
6879 move_preference = move_dir_preference;
6880 new_move_dir = move_dir;
6882 else if (move_dir_preference == move_preference &&
6883 move_dir == old_move_dir)
6885 /* prefer last direction when all directions are preferred equally */
6886 move_preference = move_dir_preference;
6887 new_move_dir = move_dir;
6891 MovDir[x][y] = new_move_dir;
6892 if (old_move_dir != new_move_dir)
6893 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6897 static void TurnRound(int x, int y)
6899 int direction = MovDir[x][y];
6903 GfxDir[x][y] = MovDir[x][y];
6905 if (direction != MovDir[x][y])
6909 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6911 ResetGfxFrame(x, y, FALSE);
6914 static boolean JustBeingPushed(int x, int y)
6918 for (i = 0; i < MAX_PLAYERS; i++)
6920 struct PlayerInfo *player = &stored_player[i];
6922 if (player->active && player->is_pushing && player->MovPos)
6924 int next_jx = player->jx + (player->jx - player->last_jx);
6925 int next_jy = player->jy + (player->jy - player->last_jy);
6927 if (x == next_jx && y == next_jy)
6935 void StartMoving(int x, int y)
6937 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6938 int element = Feld[x][y];
6943 if (MovDelay[x][y] == 0)
6944 GfxAction[x][y] = ACTION_DEFAULT;
6946 if (CAN_FALL(element) && y < lev_fieldy - 1)
6948 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6949 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6950 if (JustBeingPushed(x, y))
6953 if (element == EL_QUICKSAND_FULL)
6955 if (IS_FREE(x, y + 1))
6957 InitMovingField(x, y, MV_DOWN);
6958 started_moving = TRUE;
6960 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6961 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6962 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6963 Store[x][y] = EL_ROCK;
6965 Store[x][y] = EL_ROCK;
6968 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6970 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6972 if (!MovDelay[x][y])
6973 MovDelay[x][y] = TILEY + 1;
6982 Feld[x][y] = EL_QUICKSAND_EMPTY;
6983 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6984 Store[x][y + 1] = Store[x][y];
6987 PlayLevelSoundAction(x, y, ACTION_FILLING);
6990 else if (element == EL_QUICKSAND_FAST_FULL)
6992 if (IS_FREE(x, y + 1))
6994 InitMovingField(x, y, MV_DOWN);
6995 started_moving = TRUE;
6997 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6998 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6999 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7000 Store[x][y] = EL_ROCK;
7002 Store[x][y] = EL_ROCK;
7005 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7007 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7009 if (!MovDelay[x][y])
7010 MovDelay[x][y] = TILEY + 1;
7019 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7020 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7021 Store[x][y + 1] = Store[x][y];
7024 PlayLevelSoundAction(x, y, ACTION_FILLING);
7027 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7028 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7030 InitMovingField(x, y, MV_DOWN);
7031 started_moving = TRUE;
7033 Feld[x][y] = EL_QUICKSAND_FILLING;
7034 Store[x][y] = element;
7036 PlayLevelSoundAction(x, y, ACTION_FILLING);
7038 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7039 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7041 InitMovingField(x, y, MV_DOWN);
7042 started_moving = TRUE;
7044 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7045 Store[x][y] = element;
7047 PlayLevelSoundAction(x, y, ACTION_FILLING);
7049 else if (element == EL_MAGIC_WALL_FULL)
7051 if (IS_FREE(x, y + 1))
7053 InitMovingField(x, y, MV_DOWN);
7054 started_moving = TRUE;
7056 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7057 Store[x][y] = EL_CHANGED(Store[x][y]);
7059 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7061 if (!MovDelay[x][y])
7062 MovDelay[x][y] = TILEY/4 + 1;
7071 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7072 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7073 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7077 else if (element == EL_BD_MAGIC_WALL_FULL)
7079 if (IS_FREE(x, y + 1))
7081 InitMovingField(x, y, MV_DOWN);
7082 started_moving = TRUE;
7084 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7085 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7087 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7089 if (!MovDelay[x][y])
7090 MovDelay[x][y] = TILEY/4 + 1;
7099 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7100 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7101 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7105 else if (element == EL_DC_MAGIC_WALL_FULL)
7107 if (IS_FREE(x, y + 1))
7109 InitMovingField(x, y, MV_DOWN);
7110 started_moving = TRUE;
7112 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7113 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7115 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7117 if (!MovDelay[x][y])
7118 MovDelay[x][y] = TILEY/4 + 1;
7127 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7128 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7129 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7133 else if ((CAN_PASS_MAGIC_WALL(element) &&
7134 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7135 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7136 (CAN_PASS_DC_MAGIC_WALL(element) &&
7137 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7140 InitMovingField(x, y, MV_DOWN);
7141 started_moving = TRUE;
7144 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7145 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7146 EL_DC_MAGIC_WALL_FILLING);
7147 Store[x][y] = element;
7149 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7151 SplashAcid(x, y + 1);
7153 InitMovingField(x, y, MV_DOWN);
7154 started_moving = TRUE;
7156 Store[x][y] = EL_ACID;
7159 #if USE_FIX_IMPACT_COLLISION
7160 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7161 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7163 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7164 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7166 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7167 CAN_FALL(element) && WasJustFalling[x][y] &&
7168 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7170 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7171 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7172 (Feld[x][y + 1] == EL_BLOCKED)))
7174 /* this is needed for a special case not covered by calling "Impact()"
7175 from "ContinueMoving()": if an element moves to a tile directly below
7176 another element which was just falling on that tile (which was empty
7177 in the previous frame), the falling element above would just stop
7178 instead of smashing the element below (in previous version, the above
7179 element was just checked for "moving" instead of "falling", resulting
7180 in incorrect smashes caused by horizontal movement of the above
7181 element; also, the case of the player being the element to smash was
7182 simply not covered here... :-/ ) */
7184 CheckCollision[x][y] = 0;
7185 CheckImpact[x][y] = 0;
7189 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7191 if (MovDir[x][y] == MV_NONE)
7193 InitMovingField(x, y, MV_DOWN);
7194 started_moving = TRUE;
7197 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7199 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7200 MovDir[x][y] = MV_DOWN;
7202 InitMovingField(x, y, MV_DOWN);
7203 started_moving = TRUE;
7205 else if (element == EL_AMOEBA_DROP)
7207 Feld[x][y] = EL_AMOEBA_GROWING;
7208 Store[x][y] = EL_AMOEBA_WET;
7210 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7211 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7212 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7213 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7215 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7216 (IS_FREE(x - 1, y + 1) ||
7217 Feld[x - 1][y + 1] == EL_ACID));
7218 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7219 (IS_FREE(x + 1, y + 1) ||
7220 Feld[x + 1][y + 1] == EL_ACID));
7221 boolean can_fall_any = (can_fall_left || can_fall_right);
7222 boolean can_fall_both = (can_fall_left && can_fall_right);
7223 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7225 #if USE_NEW_ALL_SLIPPERY
7226 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7228 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7229 can_fall_right = FALSE;
7230 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7231 can_fall_left = FALSE;
7232 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7233 can_fall_right = FALSE;
7234 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7235 can_fall_left = FALSE;
7237 can_fall_any = (can_fall_left || can_fall_right);
7238 can_fall_both = FALSE;
7241 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7243 if (slippery_type == SLIPPERY_ONLY_LEFT)
7244 can_fall_right = FALSE;
7245 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7246 can_fall_left = FALSE;
7247 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7248 can_fall_right = FALSE;
7249 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7250 can_fall_left = FALSE;
7252 can_fall_any = (can_fall_left || can_fall_right);
7253 can_fall_both = (can_fall_left && can_fall_right);
7257 #if USE_NEW_ALL_SLIPPERY
7259 #if USE_NEW_SP_SLIPPERY
7260 /* !!! better use the same properties as for custom elements here !!! */
7261 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7262 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7264 can_fall_right = FALSE; /* slip down on left side */
7265 can_fall_both = FALSE;
7270 #if USE_NEW_ALL_SLIPPERY
7273 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7274 can_fall_right = FALSE; /* slip down on left side */
7276 can_fall_left = !(can_fall_right = RND(2));
7278 can_fall_both = FALSE;
7283 if (game.emulation == EMU_BOULDERDASH ||
7284 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7285 can_fall_right = FALSE; /* slip down on left side */
7287 can_fall_left = !(can_fall_right = RND(2));
7289 can_fall_both = FALSE;
7295 /* if not determined otherwise, prefer left side for slipping down */
7296 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7297 started_moving = TRUE;
7301 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7303 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7306 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7307 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7308 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7309 int belt_dir = game.belt_dir[belt_nr];
7311 if ((belt_dir == MV_LEFT && left_is_free) ||
7312 (belt_dir == MV_RIGHT && right_is_free))
7314 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7316 InitMovingField(x, y, belt_dir);
7317 started_moving = TRUE;
7319 Pushed[x][y] = TRUE;
7320 Pushed[nextx][y] = TRUE;
7322 GfxAction[x][y] = ACTION_DEFAULT;
7326 MovDir[x][y] = 0; /* if element was moving, stop it */
7331 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7333 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7335 if (CAN_MOVE(element) && !started_moving)
7338 int move_pattern = element_info[element].move_pattern;
7343 if (MovDir[x][y] == MV_NONE)
7345 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7346 x, y, element, element_info[element].token_name);
7347 printf("StartMoving(): This should never happen!\n");
7352 Moving2Blocked(x, y, &newx, &newy);
7354 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7357 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7358 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7360 WasJustMoving[x][y] = 0;
7361 CheckCollision[x][y] = 0;
7363 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7365 if (Feld[x][y] != element) /* element has changed */
7369 if (!MovDelay[x][y]) /* start new movement phase */
7371 /* all objects that can change their move direction after each step
7372 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7374 if (element != EL_YAMYAM &&
7375 element != EL_DARK_YAMYAM &&
7376 element != EL_PACMAN &&
7377 !(move_pattern & MV_ANY_DIRECTION) &&
7378 move_pattern != MV_TURNING_LEFT &&
7379 move_pattern != MV_TURNING_RIGHT &&
7380 move_pattern != MV_TURNING_LEFT_RIGHT &&
7381 move_pattern != MV_TURNING_RIGHT_LEFT &&
7382 move_pattern != MV_TURNING_RANDOM)
7386 if (MovDelay[x][y] && (element == EL_BUG ||
7387 element == EL_SPACESHIP ||
7388 element == EL_SP_SNIKSNAK ||
7389 element == EL_SP_ELECTRON ||
7390 element == EL_MOLE))
7391 DrawLevelField(x, y);
7395 if (MovDelay[x][y]) /* wait some time before next movement */
7399 if (element == EL_ROBOT ||
7400 element == EL_YAMYAM ||
7401 element == EL_DARK_YAMYAM)
7403 DrawLevelElementAnimationIfNeeded(x, y, element);
7404 PlayLevelSoundAction(x, y, ACTION_WAITING);
7406 else if (element == EL_SP_ELECTRON)
7407 DrawLevelElementAnimationIfNeeded(x, y, element);
7408 else if (element == EL_DRAGON)
7411 int dir = MovDir[x][y];
7412 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7413 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7414 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7415 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7416 dir == MV_UP ? IMG_FLAMES_1_UP :
7417 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7418 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7420 GfxAction[x][y] = ACTION_ATTACKING;
7422 if (IS_PLAYER(x, y))
7423 DrawPlayerField(x, y);
7425 DrawLevelField(x, y);
7427 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7429 for (i = 1; i <= 3; i++)
7431 int xx = x + i * dx;
7432 int yy = y + i * dy;
7433 int sx = SCREENX(xx);
7434 int sy = SCREENY(yy);
7435 int flame_graphic = graphic + (i - 1);
7437 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7442 int flamed = MovingOrBlocked2Element(xx, yy);
7446 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7448 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7449 RemoveMovingField(xx, yy);
7451 RemoveField(xx, yy);
7453 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7456 RemoveMovingField(xx, yy);
7459 ChangeDelay[xx][yy] = 0;
7461 Feld[xx][yy] = EL_FLAMES;
7463 if (IN_SCR_FIELD(sx, sy))
7465 DrawLevelFieldCrumbledSand(xx, yy);
7466 DrawGraphic(sx, sy, flame_graphic, frame);
7471 if (Feld[xx][yy] == EL_FLAMES)
7472 Feld[xx][yy] = EL_EMPTY;
7473 DrawLevelField(xx, yy);
7478 if (MovDelay[x][y]) /* element still has to wait some time */
7480 PlayLevelSoundAction(x, y, ACTION_WAITING);
7486 /* now make next step */
7488 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7490 if (DONT_COLLIDE_WITH(element) &&
7491 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7492 !PLAYER_ENEMY_PROTECTED(newx, newy))
7494 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7499 else if (CAN_MOVE_INTO_ACID(element) &&
7500 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7501 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7502 (MovDir[x][y] == MV_DOWN ||
7503 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7505 SplashAcid(newx, newy);
7506 Store[x][y] = EL_ACID;
7508 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7510 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7511 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7512 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7513 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7516 DrawLevelField(x, y);
7518 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7519 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7520 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7522 local_player->friends_still_needed--;
7523 if (!local_player->friends_still_needed &&
7524 !local_player->GameOver && AllPlayersGone)
7525 PlayerWins(local_player);
7529 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7531 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7532 DrawLevelField(newx, newy);
7534 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7536 else if (!IS_FREE(newx, newy))
7538 GfxAction[x][y] = ACTION_WAITING;
7540 if (IS_PLAYER(x, y))
7541 DrawPlayerField(x, y);
7543 DrawLevelField(x, y);
7548 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7550 if (IS_FOOD_PIG(Feld[newx][newy]))
7552 if (IS_MOVING(newx, newy))
7553 RemoveMovingField(newx, newy);
7556 Feld[newx][newy] = EL_EMPTY;
7557 DrawLevelField(newx, newy);
7560 PlayLevelSound(x, y, SND_PIG_DIGGING);
7562 else if (!IS_FREE(newx, newy))
7564 if (IS_PLAYER(x, y))
7565 DrawPlayerField(x, y);
7567 DrawLevelField(x, y);
7572 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7574 if (Store[x][y] != EL_EMPTY)
7576 boolean can_clone = FALSE;
7579 /* check if element to clone is still there */
7580 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7582 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7590 /* cannot clone or target field not free anymore -- do not clone */
7591 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7592 Store[x][y] = EL_EMPTY;
7595 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7597 if (IS_MV_DIAGONAL(MovDir[x][y]))
7599 int diagonal_move_dir = MovDir[x][y];
7600 int stored = Store[x][y];
7601 int change_delay = 8;
7604 /* android is moving diagonally */
7606 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7608 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7609 GfxElement[x][y] = EL_EMC_ANDROID;
7610 GfxAction[x][y] = ACTION_SHRINKING;
7611 GfxDir[x][y] = diagonal_move_dir;
7612 ChangeDelay[x][y] = change_delay;
7614 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7617 DrawLevelGraphicAnimation(x, y, graphic);
7618 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7620 if (Feld[newx][newy] == EL_ACID)
7622 SplashAcid(newx, newy);
7627 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7629 Store[newx][newy] = EL_EMC_ANDROID;
7630 GfxElement[newx][newy] = EL_EMC_ANDROID;
7631 GfxAction[newx][newy] = ACTION_GROWING;
7632 GfxDir[newx][newy] = diagonal_move_dir;
7633 ChangeDelay[newx][newy] = change_delay;
7635 graphic = el_act_dir2img(GfxElement[newx][newy],
7636 GfxAction[newx][newy], GfxDir[newx][newy]);
7638 DrawLevelGraphicAnimation(newx, newy, graphic);
7639 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7645 Feld[newx][newy] = EL_EMPTY;
7646 DrawLevelField(newx, newy);
7648 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7651 else if (!IS_FREE(newx, newy))
7654 if (IS_PLAYER(x, y))
7655 DrawPlayerField(x, y);
7657 DrawLevelField(x, y);
7663 else if (IS_CUSTOM_ELEMENT(element) &&
7664 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7666 int new_element = Feld[newx][newy];
7668 if (!IS_FREE(newx, newy))
7670 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7671 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7674 /* no element can dig solid indestructible elements */
7675 if (IS_INDESTRUCTIBLE(new_element) &&
7676 !IS_DIGGABLE(new_element) &&
7677 !IS_COLLECTIBLE(new_element))
7680 if (AmoebaNr[newx][newy] &&
7681 (new_element == EL_AMOEBA_FULL ||
7682 new_element == EL_BD_AMOEBA ||
7683 new_element == EL_AMOEBA_GROWING))
7685 AmoebaCnt[AmoebaNr[newx][newy]]--;
7686 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7689 if (IS_MOVING(newx, newy))
7690 RemoveMovingField(newx, newy);
7693 RemoveField(newx, newy);
7694 DrawLevelField(newx, newy);
7697 /* if digged element was about to explode, prevent the explosion */
7698 ExplodeField[newx][newy] = EX_TYPE_NONE;
7700 PlayLevelSoundAction(x, y, action);
7703 Store[newx][newy] = EL_EMPTY;
7705 /* this makes it possible to leave the removed element again */
7706 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7707 Store[newx][newy] = new_element;
7709 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7711 int move_leave_element = element_info[element].move_leave_element;
7713 /* this makes it possible to leave the removed element again */
7714 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7715 new_element : move_leave_element);
7719 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7721 RunnerVisit[x][y] = FrameCounter;
7722 PlayerVisit[x][y] /= 8; /* expire player visit path */
7725 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7727 if (!IS_FREE(newx, newy))
7729 if (IS_PLAYER(x, y))
7730 DrawPlayerField(x, y);
7732 DrawLevelField(x, y);
7738 boolean wanna_flame = !RND(10);
7739 int dx = newx - x, dy = newy - y;
7740 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7741 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7742 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7743 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7744 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7745 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7748 IS_CLASSIC_ENEMY(element1) ||
7749 IS_CLASSIC_ENEMY(element2)) &&
7750 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7751 element1 != EL_FLAMES && element2 != EL_FLAMES)
7753 ResetGfxAnimation(x, y);
7754 GfxAction[x][y] = ACTION_ATTACKING;
7756 if (IS_PLAYER(x, y))
7757 DrawPlayerField(x, y);
7759 DrawLevelField(x, y);
7761 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7763 MovDelay[x][y] = 50;
7767 RemoveField(newx, newy);
7769 Feld[newx][newy] = EL_FLAMES;
7770 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7773 RemoveField(newx1, newy1);
7775 Feld[newx1][newy1] = EL_FLAMES;
7777 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7780 RemoveField(newx2, newy2);
7782 Feld[newx2][newy2] = EL_FLAMES;
7789 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7790 Feld[newx][newy] == EL_DIAMOND)
7792 if (IS_MOVING(newx, newy))
7793 RemoveMovingField(newx, newy);
7796 Feld[newx][newy] = EL_EMPTY;
7797 DrawLevelField(newx, newy);
7800 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7802 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7803 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7805 if (AmoebaNr[newx][newy])
7807 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7808 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7809 Feld[newx][newy] == EL_BD_AMOEBA)
7810 AmoebaCnt[AmoebaNr[newx][newy]]--;
7815 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7817 RemoveMovingField(newx, newy);
7820 if (IS_MOVING(newx, newy))
7822 RemoveMovingField(newx, newy);
7827 Feld[newx][newy] = EL_EMPTY;
7828 DrawLevelField(newx, newy);
7831 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7833 else if ((element == EL_PACMAN || element == EL_MOLE)
7834 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7836 if (AmoebaNr[newx][newy])
7838 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7839 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7840 Feld[newx][newy] == EL_BD_AMOEBA)
7841 AmoebaCnt[AmoebaNr[newx][newy]]--;
7844 if (element == EL_MOLE)
7846 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7847 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7849 ResetGfxAnimation(x, y);
7850 GfxAction[x][y] = ACTION_DIGGING;
7851 DrawLevelField(x, y);
7853 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7855 return; /* wait for shrinking amoeba */
7857 else /* element == EL_PACMAN */
7859 Feld[newx][newy] = EL_EMPTY;
7860 DrawLevelField(newx, newy);
7861 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7864 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7865 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7866 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7868 /* wait for shrinking amoeba to completely disappear */
7871 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7873 /* object was running against a wall */
7878 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7879 if (move_pattern & MV_ANY_DIRECTION &&
7880 move_pattern == MovDir[x][y])
7882 int blocking_element =
7883 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7885 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7888 element = Feld[x][y]; /* element might have changed */
7892 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7893 DrawLevelElementAnimation(x, y, element);
7895 if (DONT_TOUCH(element))
7896 TestIfBadThingTouchesPlayer(x, y);
7901 InitMovingField(x, y, MovDir[x][y]);
7903 PlayLevelSoundAction(x, y, ACTION_MOVING);
7907 ContinueMoving(x, y);
7910 void ContinueMoving(int x, int y)
7912 int element = Feld[x][y];
7913 struct ElementInfo *ei = &element_info[element];
7914 int direction = MovDir[x][y];
7915 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7916 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7917 int newx = x + dx, newy = y + dy;
7918 int stored = Store[x][y];
7919 int stored_new = Store[newx][newy];
7920 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7921 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7922 boolean last_line = (newy == lev_fieldy - 1);
7924 MovPos[x][y] += getElementMoveStepsize(x, y);
7926 if (pushed_by_player) /* special case: moving object pushed by player */
7927 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7929 if (ABS(MovPos[x][y]) < TILEX)
7932 int ee = Feld[x][y];
7933 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7934 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7936 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7937 x, y, ABS(MovPos[x][y]),
7939 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7942 DrawLevelField(x, y);
7944 return; /* element is still moving */
7947 /* element reached destination field */
7949 Feld[x][y] = EL_EMPTY;
7950 Feld[newx][newy] = element;
7951 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7953 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7955 element = Feld[newx][newy] = EL_ACID;
7957 else if (element == EL_MOLE)
7959 Feld[x][y] = EL_SAND;
7961 DrawLevelFieldCrumbledSandNeighbours(x, y);
7963 else if (element == EL_QUICKSAND_FILLING)
7965 element = Feld[newx][newy] = get_next_element(element);
7966 Store[newx][newy] = Store[x][y];
7968 else if (element == EL_QUICKSAND_EMPTYING)
7970 Feld[x][y] = get_next_element(element);
7971 element = Feld[newx][newy] = Store[x][y];
7973 else if (element == EL_QUICKSAND_FAST_FILLING)
7975 element = Feld[newx][newy] = get_next_element(element);
7976 Store[newx][newy] = Store[x][y];
7978 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7980 Feld[x][y] = get_next_element(element);
7981 element = Feld[newx][newy] = Store[x][y];
7983 else if (element == EL_MAGIC_WALL_FILLING)
7985 element = Feld[newx][newy] = get_next_element(element);
7986 if (!game.magic_wall_active)
7987 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7988 Store[newx][newy] = Store[x][y];
7990 else if (element == EL_MAGIC_WALL_EMPTYING)
7992 Feld[x][y] = get_next_element(element);
7993 if (!game.magic_wall_active)
7994 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7995 element = Feld[newx][newy] = Store[x][y];
7997 #if USE_NEW_CUSTOM_VALUE
7998 InitField(newx, newy, FALSE);
8001 else if (element == EL_BD_MAGIC_WALL_FILLING)
8003 element = Feld[newx][newy] = get_next_element(element);
8004 if (!game.magic_wall_active)
8005 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8006 Store[newx][newy] = Store[x][y];
8008 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8010 Feld[x][y] = get_next_element(element);
8011 if (!game.magic_wall_active)
8012 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8013 element = Feld[newx][newy] = Store[x][y];
8015 #if USE_NEW_CUSTOM_VALUE
8016 InitField(newx, newy, FALSE);
8019 else if (element == EL_DC_MAGIC_WALL_FILLING)
8021 element = Feld[newx][newy] = get_next_element(element);
8022 if (!game.magic_wall_active)
8023 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8024 Store[newx][newy] = Store[x][y];
8026 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8028 Feld[x][y] = get_next_element(element);
8029 if (!game.magic_wall_active)
8030 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8031 element = Feld[newx][newy] = Store[x][y];
8033 #if USE_NEW_CUSTOM_VALUE
8034 InitField(newx, newy, FALSE);
8037 else if (element == EL_AMOEBA_DROPPING)
8039 Feld[x][y] = get_next_element(element);
8040 element = Feld[newx][newy] = Store[x][y];
8042 else if (element == EL_SOKOBAN_OBJECT)
8045 Feld[x][y] = Back[x][y];
8047 if (Back[newx][newy])
8048 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8050 Back[x][y] = Back[newx][newy] = 0;
8053 Store[x][y] = EL_EMPTY;
8058 MovDelay[newx][newy] = 0;
8060 if (CAN_CHANGE_OR_HAS_ACTION(element))
8062 /* copy element change control values to new field */
8063 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8064 ChangePage[newx][newy] = ChangePage[x][y];
8065 ChangeCount[newx][newy] = ChangeCount[x][y];
8066 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8069 #if USE_NEW_CUSTOM_VALUE
8070 CustomValue[newx][newy] = CustomValue[x][y];
8073 ChangeDelay[x][y] = 0;
8074 ChangePage[x][y] = -1;
8075 ChangeCount[x][y] = 0;
8076 ChangeEvent[x][y] = -1;
8078 #if USE_NEW_CUSTOM_VALUE
8079 CustomValue[x][y] = 0;
8082 /* copy animation control values to new field */
8083 GfxFrame[newx][newy] = GfxFrame[x][y];
8084 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8085 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8086 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8088 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8090 /* some elements can leave other elements behind after moving */
8092 if (ei->move_leave_element != EL_EMPTY &&
8093 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8094 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8096 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8097 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8098 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8101 int move_leave_element = ei->move_leave_element;
8105 /* this makes it possible to leave the removed element again */
8106 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8107 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8109 /* this makes it possible to leave the removed element again */
8110 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8111 move_leave_element = stored;
8114 /* this makes it possible to leave the removed element again */
8115 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8116 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8117 move_leave_element = stored;
8120 Feld[x][y] = move_leave_element;
8122 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8123 MovDir[x][y] = direction;
8125 InitField(x, y, FALSE);
8127 if (GFX_CRUMBLED(Feld[x][y]))
8128 DrawLevelFieldCrumbledSandNeighbours(x, y);
8130 if (ELEM_IS_PLAYER(move_leave_element))
8131 RelocatePlayer(x, y, move_leave_element);
8134 /* do this after checking for left-behind element */
8135 ResetGfxAnimation(x, y); /* reset animation values for old field */
8137 if (!CAN_MOVE(element) ||
8138 (CAN_FALL(element) && direction == MV_DOWN &&
8139 (element == EL_SPRING ||
8140 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8141 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8142 GfxDir[x][y] = MovDir[newx][newy] = 0;
8144 DrawLevelField(x, y);
8145 DrawLevelField(newx, newy);
8147 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8149 /* prevent pushed element from moving on in pushed direction */
8150 if (pushed_by_player && CAN_MOVE(element) &&
8151 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8152 !(element_info[element].move_pattern & direction))
8153 TurnRound(newx, newy);
8155 /* prevent elements on conveyor belt from moving on in last direction */
8156 if (pushed_by_conveyor && CAN_FALL(element) &&
8157 direction & MV_HORIZONTAL)
8158 MovDir[newx][newy] = 0;
8160 if (!pushed_by_player)
8162 int nextx = newx + dx, nexty = newy + dy;
8163 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8165 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8167 if (CAN_FALL(element) && direction == MV_DOWN)
8168 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8170 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8171 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8173 #if USE_FIX_IMPACT_COLLISION
8174 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8175 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8179 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8181 TestIfBadThingTouchesPlayer(newx, newy);
8182 TestIfBadThingTouchesFriend(newx, newy);
8184 if (!IS_CUSTOM_ELEMENT(element))
8185 TestIfBadThingTouchesOtherBadThing(newx, newy);
8187 else if (element == EL_PENGUIN)
8188 TestIfFriendTouchesBadThing(newx, newy);
8190 /* give the player one last chance (one more frame) to move away */
8191 if (CAN_FALL(element) && direction == MV_DOWN &&
8192 (last_line || (!IS_FREE(x, newy + 1) &&
8193 (!IS_PLAYER(x, newy + 1) ||
8194 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8197 if (pushed_by_player && !game.use_change_when_pushing_bug)
8199 int push_side = MV_DIR_OPPOSITE(direction);
8200 struct PlayerInfo *player = PLAYERINFO(x, y);
8202 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8203 player->index_bit, push_side);
8204 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8205 player->index_bit, push_side);
8208 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8209 MovDelay[newx][newy] = 1;
8211 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8213 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8216 if (ChangePage[newx][newy] != -1) /* delayed change */
8218 int page = ChangePage[newx][newy];
8219 struct ElementChangeInfo *change = &ei->change_page[page];
8221 ChangePage[newx][newy] = -1;
8223 if (change->can_change)
8225 if (ChangeElement(newx, newy, element, page))
8227 if (change->post_change_function)
8228 change->post_change_function(newx, newy);
8232 if (change->has_action)
8233 ExecuteCustomElementAction(newx, newy, element, page);
8237 TestIfElementHitsCustomElement(newx, newy, direction);
8238 TestIfPlayerTouchesCustomElement(newx, newy);
8239 TestIfElementTouchesCustomElement(newx, newy);
8241 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8242 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8243 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8244 MV_DIR_OPPOSITE(direction));
8247 int AmoebeNachbarNr(int ax, int ay)
8250 int element = Feld[ax][ay];
8252 static int xy[4][2] =
8260 for (i = 0; i < NUM_DIRECTIONS; i++)
8262 int x = ax + xy[i][0];
8263 int y = ay + xy[i][1];
8265 if (!IN_LEV_FIELD(x, y))
8268 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8269 group_nr = AmoebaNr[x][y];
8275 void AmoebenVereinigen(int ax, int ay)
8277 int i, x, y, xx, yy;
8278 int new_group_nr = AmoebaNr[ax][ay];
8279 static int xy[4][2] =
8287 if (new_group_nr == 0)
8290 for (i = 0; i < NUM_DIRECTIONS; i++)
8295 if (!IN_LEV_FIELD(x, y))
8298 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8299 Feld[x][y] == EL_BD_AMOEBA ||
8300 Feld[x][y] == EL_AMOEBA_DEAD) &&
8301 AmoebaNr[x][y] != new_group_nr)
8303 int old_group_nr = AmoebaNr[x][y];
8305 if (old_group_nr == 0)
8308 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8309 AmoebaCnt[old_group_nr] = 0;
8310 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8311 AmoebaCnt2[old_group_nr] = 0;
8313 SCAN_PLAYFIELD(xx, yy)
8315 if (AmoebaNr[xx][yy] == old_group_nr)
8316 AmoebaNr[xx][yy] = new_group_nr;
8322 void AmoebeUmwandeln(int ax, int ay)
8326 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8328 int group_nr = AmoebaNr[ax][ay];
8333 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8334 printf("AmoebeUmwandeln(): This should never happen!\n");
8339 SCAN_PLAYFIELD(x, y)
8341 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8344 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8348 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8349 SND_AMOEBA_TURNING_TO_GEM :
8350 SND_AMOEBA_TURNING_TO_ROCK));
8355 static int xy[4][2] =
8363 for (i = 0; i < NUM_DIRECTIONS; i++)
8368 if (!IN_LEV_FIELD(x, y))
8371 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8373 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8374 SND_AMOEBA_TURNING_TO_GEM :
8375 SND_AMOEBA_TURNING_TO_ROCK));
8382 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8385 int group_nr = AmoebaNr[ax][ay];
8386 boolean done = FALSE;
8391 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8392 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8397 SCAN_PLAYFIELD(x, y)
8399 if (AmoebaNr[x][y] == group_nr &&
8400 (Feld[x][y] == EL_AMOEBA_DEAD ||
8401 Feld[x][y] == EL_BD_AMOEBA ||
8402 Feld[x][y] == EL_AMOEBA_GROWING))
8405 Feld[x][y] = new_element;
8406 InitField(x, y, FALSE);
8407 DrawLevelField(x, y);
8413 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8414 SND_BD_AMOEBA_TURNING_TO_ROCK :
8415 SND_BD_AMOEBA_TURNING_TO_GEM));
8418 void AmoebeWaechst(int x, int y)
8420 static unsigned long sound_delay = 0;
8421 static unsigned long sound_delay_value = 0;
8423 if (!MovDelay[x][y]) /* start new growing cycle */
8427 if (DelayReached(&sound_delay, sound_delay_value))
8429 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8430 sound_delay_value = 30;
8434 if (MovDelay[x][y]) /* wait some time before growing bigger */
8437 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8439 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8440 6 - MovDelay[x][y]);
8442 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8445 if (!MovDelay[x][y])
8447 Feld[x][y] = Store[x][y];
8449 DrawLevelField(x, y);
8454 void AmoebaDisappearing(int x, int y)
8456 static unsigned long sound_delay = 0;
8457 static unsigned long sound_delay_value = 0;
8459 if (!MovDelay[x][y]) /* start new shrinking cycle */
8463 if (DelayReached(&sound_delay, sound_delay_value))
8464 sound_delay_value = 30;
8467 if (MovDelay[x][y]) /* wait some time before shrinking */
8470 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8472 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8473 6 - MovDelay[x][y]);
8475 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8478 if (!MovDelay[x][y])
8480 Feld[x][y] = EL_EMPTY;
8481 DrawLevelField(x, y);
8483 /* don't let mole enter this field in this cycle;
8484 (give priority to objects falling to this field from above) */
8490 void AmoebeAbleger(int ax, int ay)
8493 int element = Feld[ax][ay];
8494 int graphic = el2img(element);
8495 int newax = ax, neway = ay;
8496 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8497 static int xy[4][2] =
8505 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8507 Feld[ax][ay] = EL_AMOEBA_DEAD;
8508 DrawLevelField(ax, ay);
8512 if (IS_ANIMATED(graphic))
8513 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8515 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8516 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8518 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8521 if (MovDelay[ax][ay])
8525 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8528 int x = ax + xy[start][0];
8529 int y = ay + xy[start][1];
8531 if (!IN_LEV_FIELD(x, y))
8534 if (IS_FREE(x, y) ||
8535 CAN_GROW_INTO(Feld[x][y]) ||
8536 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8537 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8543 if (newax == ax && neway == ay)
8546 else /* normal or "filled" (BD style) amoeba */
8549 boolean waiting_for_player = FALSE;
8551 for (i = 0; i < NUM_DIRECTIONS; i++)
8553 int j = (start + i) % 4;
8554 int x = ax + xy[j][0];
8555 int y = ay + xy[j][1];
8557 if (!IN_LEV_FIELD(x, y))
8560 if (IS_FREE(x, y) ||
8561 CAN_GROW_INTO(Feld[x][y]) ||
8562 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8563 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8569 else if (IS_PLAYER(x, y))
8570 waiting_for_player = TRUE;
8573 if (newax == ax && neway == ay) /* amoeba cannot grow */
8575 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8577 Feld[ax][ay] = EL_AMOEBA_DEAD;
8578 DrawLevelField(ax, ay);
8579 AmoebaCnt[AmoebaNr[ax][ay]]--;
8581 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8583 if (element == EL_AMOEBA_FULL)
8584 AmoebeUmwandeln(ax, ay);
8585 else if (element == EL_BD_AMOEBA)
8586 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8591 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8593 /* amoeba gets larger by growing in some direction */
8595 int new_group_nr = AmoebaNr[ax][ay];
8598 if (new_group_nr == 0)
8600 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8601 printf("AmoebeAbleger(): This should never happen!\n");
8606 AmoebaNr[newax][neway] = new_group_nr;
8607 AmoebaCnt[new_group_nr]++;
8608 AmoebaCnt2[new_group_nr]++;
8610 /* if amoeba touches other amoeba(s) after growing, unify them */
8611 AmoebenVereinigen(newax, neway);
8613 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8615 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8621 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8622 (neway == lev_fieldy - 1 && newax != ax))
8624 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8625 Store[newax][neway] = element;
8627 else if (neway == ay || element == EL_EMC_DRIPPER)
8629 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8631 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8635 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8636 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8637 Store[ax][ay] = EL_AMOEBA_DROP;
8638 ContinueMoving(ax, ay);
8642 DrawLevelField(newax, neway);
8645 void Life(int ax, int ay)
8649 int element = Feld[ax][ay];
8650 int graphic = el2img(element);
8651 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8653 boolean changed = FALSE;
8655 if (IS_ANIMATED(graphic))
8656 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8661 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8662 MovDelay[ax][ay] = life_time;
8664 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8667 if (MovDelay[ax][ay])
8671 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8673 int xx = ax+x1, yy = ay+y1;
8676 if (!IN_LEV_FIELD(xx, yy))
8679 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8681 int x = xx+x2, y = yy+y2;
8683 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8686 if (((Feld[x][y] == element ||
8687 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8689 (IS_FREE(x, y) && Stop[x][y]))
8693 if (xx == ax && yy == ay) /* field in the middle */
8695 if (nachbarn < life_parameter[0] ||
8696 nachbarn > life_parameter[1])
8698 Feld[xx][yy] = EL_EMPTY;
8700 DrawLevelField(xx, yy);
8701 Stop[xx][yy] = TRUE;
8705 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8706 { /* free border field */
8707 if (nachbarn >= life_parameter[2] &&
8708 nachbarn <= life_parameter[3])
8710 Feld[xx][yy] = element;
8711 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8713 DrawLevelField(xx, yy);
8714 Stop[xx][yy] = TRUE;
8721 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8722 SND_GAME_OF_LIFE_GROWING);
8725 static void InitRobotWheel(int x, int y)
8727 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8730 static void RunRobotWheel(int x, int y)
8732 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8735 static void StopRobotWheel(int x, int y)
8737 if (ZX == x && ZY == y)
8741 static void InitTimegateWheel(int x, int y)
8743 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8746 static void RunTimegateWheel(int x, int y)
8748 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8751 static void InitMagicBallDelay(int x, int y)
8754 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8756 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8760 static void ActivateMagicBall(int bx, int by)
8764 if (level.ball_random)
8766 int pos_border = RND(8); /* select one of the eight border elements */
8767 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8768 int xx = pos_content % 3;
8769 int yy = pos_content / 3;
8774 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8775 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8779 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8781 int xx = x - bx + 1;
8782 int yy = y - by + 1;
8784 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8785 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8789 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8792 void CheckExit(int x, int y)
8794 if (local_player->gems_still_needed > 0 ||
8795 local_player->sokobanfields_still_needed > 0 ||
8796 local_player->lights_still_needed > 0)
8798 int element = Feld[x][y];
8799 int graphic = el2img(element);
8801 if (IS_ANIMATED(graphic))
8802 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8807 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8810 Feld[x][y] = EL_EXIT_OPENING;
8812 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8815 void CheckExitEM(int x, int y)
8817 if (local_player->gems_still_needed > 0 ||
8818 local_player->sokobanfields_still_needed > 0 ||
8819 local_player->lights_still_needed > 0)
8821 int element = Feld[x][y];
8822 int graphic = el2img(element);
8824 if (IS_ANIMATED(graphic))
8825 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8830 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8833 Feld[x][y] = EL_EM_EXIT_OPENING;
8835 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8838 void CheckExitSteel(int x, int y)
8840 if (local_player->gems_still_needed > 0 ||
8841 local_player->sokobanfields_still_needed > 0 ||
8842 local_player->lights_still_needed > 0)
8844 int element = Feld[x][y];
8845 int graphic = el2img(element);
8847 if (IS_ANIMATED(graphic))
8848 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8853 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8856 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8858 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8861 void CheckExitSteelEM(int x, int y)
8863 if (local_player->gems_still_needed > 0 ||
8864 local_player->sokobanfields_still_needed > 0 ||
8865 local_player->lights_still_needed > 0)
8867 int element = Feld[x][y];
8868 int graphic = el2img(element);
8870 if (IS_ANIMATED(graphic))
8871 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8876 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8879 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8881 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8884 void CheckExitSP(int x, int y)
8886 if (local_player->gems_still_needed > 0)
8888 int element = Feld[x][y];
8889 int graphic = el2img(element);
8891 if (IS_ANIMATED(graphic))
8892 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8897 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8900 Feld[x][y] = EL_SP_EXIT_OPENING;
8902 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8905 static void CloseAllOpenTimegates()
8909 SCAN_PLAYFIELD(x, y)
8911 int element = Feld[x][y];
8913 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8915 Feld[x][y] = EL_TIMEGATE_CLOSING;
8917 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8922 void DrawTwinkleOnField(int x, int y)
8924 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8927 if (Feld[x][y] == EL_BD_DIAMOND)
8930 if (MovDelay[x][y] == 0) /* next animation frame */
8931 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8933 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8937 if (setup.direct_draw && MovDelay[x][y])
8938 SetDrawtoField(DRAW_BUFFERED);
8940 DrawLevelElementAnimation(x, y, Feld[x][y]);
8942 if (MovDelay[x][y] != 0)
8944 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8945 10 - MovDelay[x][y]);
8947 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8949 if (setup.direct_draw)
8953 dest_x = FX + SCREENX(x) * TILEX;
8954 dest_y = FY + SCREENY(y) * TILEY;
8956 BlitBitmap(drawto_field, window,
8957 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8958 SetDrawtoField(DRAW_DIRECT);
8964 void MauerWaechst(int x, int y)
8968 if (!MovDelay[x][y]) /* next animation frame */
8969 MovDelay[x][y] = 3 * delay;
8971 if (MovDelay[x][y]) /* wait some time before next frame */
8975 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8977 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8978 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8980 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8983 if (!MovDelay[x][y])
8985 if (MovDir[x][y] == MV_LEFT)
8987 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8988 DrawLevelField(x - 1, y);
8990 else if (MovDir[x][y] == MV_RIGHT)
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_UP)
8997 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8998 DrawLevelField(x, y - 1);
9002 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9003 DrawLevelField(x, y + 1);
9006 Feld[x][y] = Store[x][y];
9008 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9009 DrawLevelField(x, y);
9014 void MauerAbleger(int ax, int ay)
9016 int element = Feld[ax][ay];
9017 int graphic = el2img(element);
9018 boolean oben_frei = FALSE, unten_frei = FALSE;
9019 boolean links_frei = FALSE, rechts_frei = FALSE;
9020 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9021 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9022 boolean new_wall = FALSE;
9024 if (IS_ANIMATED(graphic))
9025 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9027 if (!MovDelay[ax][ay]) /* start building new wall */
9028 MovDelay[ax][ay] = 6;
9030 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9033 if (MovDelay[ax][ay])
9037 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9039 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9041 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9043 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9046 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9047 element == EL_EXPANDABLE_WALL_ANY)
9051 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9052 Store[ax][ay-1] = element;
9053 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9054 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9055 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9056 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9061 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9062 Store[ax][ay+1] = element;
9063 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9064 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9065 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9066 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9071 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9072 element == EL_EXPANDABLE_WALL_ANY ||
9073 element == EL_EXPANDABLE_WALL ||
9074 element == EL_BD_EXPANDABLE_WALL)
9078 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9079 Store[ax-1][ay] = element;
9080 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9081 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9082 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9083 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9089 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9090 Store[ax+1][ay] = element;
9091 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9092 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9093 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9094 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9099 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9100 DrawLevelField(ax, ay);
9102 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9104 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9105 unten_massiv = TRUE;
9106 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9107 links_massiv = TRUE;
9108 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9109 rechts_massiv = TRUE;
9111 if (((oben_massiv && unten_massiv) ||
9112 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9113 element == EL_EXPANDABLE_WALL) &&
9114 ((links_massiv && rechts_massiv) ||
9115 element == EL_EXPANDABLE_WALL_VERTICAL))
9116 Feld[ax][ay] = EL_WALL;
9119 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9122 void MauerAblegerStahl(int ax, int ay)
9124 int element = Feld[ax][ay];
9125 int graphic = el2img(element);
9126 boolean oben_frei = FALSE, unten_frei = FALSE;
9127 boolean links_frei = FALSE, rechts_frei = FALSE;
9128 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9129 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9130 boolean new_wall = FALSE;
9132 if (IS_ANIMATED(graphic))
9133 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9135 if (!MovDelay[ax][ay]) /* start building new wall */
9136 MovDelay[ax][ay] = 6;
9138 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9141 if (MovDelay[ax][ay])
9145 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9147 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9149 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9151 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9154 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9155 element == EL_EXPANDABLE_STEELWALL_ANY)
9159 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9160 Store[ax][ay-1] = element;
9161 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9162 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9163 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9164 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9169 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9170 Store[ax][ay+1] = element;
9171 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9172 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9173 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9174 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9179 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9180 element == EL_EXPANDABLE_STEELWALL_ANY)
9184 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9185 Store[ax-1][ay] = element;
9186 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9187 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9188 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9189 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9195 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9196 Store[ax+1][ay] = element;
9197 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9198 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9199 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9200 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9205 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9207 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9208 unten_massiv = TRUE;
9209 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9210 links_massiv = TRUE;
9211 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9212 rechts_massiv = TRUE;
9214 if (((oben_massiv && unten_massiv) ||
9215 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9216 ((links_massiv && rechts_massiv) ||
9217 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9218 Feld[ax][ay] = EL_WALL;
9221 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9224 void CheckForDragon(int x, int y)
9227 boolean dragon_found = FALSE;
9228 static int xy[4][2] =
9236 for (i = 0; i < NUM_DIRECTIONS; i++)
9238 for (j = 0; j < 4; j++)
9240 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9242 if (IN_LEV_FIELD(xx, yy) &&
9243 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9245 if (Feld[xx][yy] == EL_DRAGON)
9246 dragon_found = TRUE;
9255 for (i = 0; i < NUM_DIRECTIONS; i++)
9257 for (j = 0; j < 3; j++)
9259 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9261 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9263 Feld[xx][yy] = EL_EMPTY;
9264 DrawLevelField(xx, yy);
9273 static void InitBuggyBase(int x, int y)
9275 int element = Feld[x][y];
9276 int activating_delay = FRAMES_PER_SECOND / 4;
9279 (element == EL_SP_BUGGY_BASE ?
9280 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9281 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9283 element == EL_SP_BUGGY_BASE_ACTIVE ?
9284 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9287 static void WarnBuggyBase(int x, int y)
9290 static int xy[4][2] =
9298 for (i = 0; i < NUM_DIRECTIONS; i++)
9300 int xx = x + xy[i][0];
9301 int yy = y + xy[i][1];
9303 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9305 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9312 static void InitTrap(int x, int y)
9314 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9317 static void ActivateTrap(int x, int y)
9319 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9322 static void ChangeActiveTrap(int x, int y)
9324 int graphic = IMG_TRAP_ACTIVE;
9326 /* if new animation frame was drawn, correct crumbled sand border */
9327 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9328 DrawLevelFieldCrumbledSand(x, y);
9331 static int getSpecialActionElement(int element, int number, int base_element)
9333 return (element != EL_EMPTY ? element :
9334 number != -1 ? base_element + number - 1 :
9338 static int getModifiedActionNumber(int value_old, int operator, int operand,
9339 int value_min, int value_max)
9341 int value_new = (operator == CA_MODE_SET ? operand :
9342 operator == CA_MODE_ADD ? value_old + operand :
9343 operator == CA_MODE_SUBTRACT ? value_old - operand :
9344 operator == CA_MODE_MULTIPLY ? value_old * operand :
9345 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9346 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9349 return (value_new < value_min ? value_min :
9350 value_new > value_max ? value_max :
9354 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9356 struct ElementInfo *ei = &element_info[element];
9357 struct ElementChangeInfo *change = &ei->change_page[page];
9358 int target_element = change->target_element;
9359 int action_type = change->action_type;
9360 int action_mode = change->action_mode;
9361 int action_arg = change->action_arg;
9364 if (!change->has_action)
9367 /* ---------- determine action paramater values -------------------------- */
9369 int level_time_value =
9370 (level.time > 0 ? TimeLeft :
9373 int action_arg_element =
9374 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9375 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9376 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9379 int action_arg_direction =
9380 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9381 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9382 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9383 change->actual_trigger_side :
9384 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9385 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9388 int action_arg_number_min =
9389 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9392 int action_arg_number_max =
9393 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9394 action_type == CA_SET_LEVEL_GEMS ? 999 :
9395 action_type == CA_SET_LEVEL_TIME ? 9999 :
9396 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9397 action_type == CA_SET_CE_VALUE ? 9999 :
9398 action_type == CA_SET_CE_SCORE ? 9999 :
9401 int action_arg_number_reset =
9402 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9403 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9404 action_type == CA_SET_LEVEL_TIME ? level.time :
9405 action_type == CA_SET_LEVEL_SCORE ? 0 :
9406 #if USE_NEW_CUSTOM_VALUE
9407 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9409 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9411 action_type == CA_SET_CE_SCORE ? 0 :
9414 int action_arg_number =
9415 (action_arg <= CA_ARG_MAX ? action_arg :
9416 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9417 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9418 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9419 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9420 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9421 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9422 #if USE_NEW_CUSTOM_VALUE
9423 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9425 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9427 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9428 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9429 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9430 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9431 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9432 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9433 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9434 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9435 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9436 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9437 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9440 int action_arg_number_old =
9441 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9442 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9443 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9444 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9445 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9448 int action_arg_number_new =
9449 getModifiedActionNumber(action_arg_number_old,
9450 action_mode, action_arg_number,
9451 action_arg_number_min, action_arg_number_max);
9453 int trigger_player_bits =
9454 (change->actual_trigger_player >= EL_PLAYER_1 &&
9455 change->actual_trigger_player <= EL_PLAYER_4 ?
9456 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9459 int action_arg_player_bits =
9460 (action_arg >= CA_ARG_PLAYER_1 &&
9461 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9462 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9465 /* ---------- execute action -------------------------------------------- */
9467 switch (action_type)
9474 /* ---------- level actions ------------------------------------------- */
9476 case CA_RESTART_LEVEL:
9478 game.restart_level = TRUE;
9483 case CA_SHOW_ENVELOPE:
9485 int element = getSpecialActionElement(action_arg_element,
9486 action_arg_number, EL_ENVELOPE_1);
9488 if (IS_ENVELOPE(element))
9489 local_player->show_envelope = element;
9494 case CA_SET_LEVEL_TIME:
9496 if (level.time > 0) /* only modify limited time value */
9498 TimeLeft = action_arg_number_new;
9501 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9503 DisplayGameControlValues();
9505 DrawGameValue_Time(TimeLeft);
9508 if (!TimeLeft && setup.time_limit)
9509 for (i = 0; i < MAX_PLAYERS; i++)
9510 KillPlayer(&stored_player[i]);
9516 case CA_SET_LEVEL_SCORE:
9518 local_player->score = action_arg_number_new;
9521 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9523 DisplayGameControlValues();
9525 DrawGameValue_Score(local_player->score);
9531 case CA_SET_LEVEL_GEMS:
9533 local_player->gems_still_needed = action_arg_number_new;
9536 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9538 DisplayGameControlValues();
9540 DrawGameValue_Emeralds(local_player->gems_still_needed);
9546 #if !USE_PLAYER_GRAVITY
9547 case CA_SET_LEVEL_GRAVITY:
9549 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9550 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9551 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9557 case CA_SET_LEVEL_WIND:
9559 game.wind_direction = action_arg_direction;
9564 /* ---------- player actions ------------------------------------------ */
9566 case CA_MOVE_PLAYER:
9568 /* automatically move to the next field in specified direction */
9569 for (i = 0; i < MAX_PLAYERS; i++)
9570 if (trigger_player_bits & (1 << i))
9571 stored_player[i].programmed_action = action_arg_direction;
9576 case CA_EXIT_PLAYER:
9578 for (i = 0; i < MAX_PLAYERS; i++)
9579 if (action_arg_player_bits & (1 << i))
9580 PlayerWins(&stored_player[i]);
9585 case CA_KILL_PLAYER:
9587 for (i = 0; i < MAX_PLAYERS; i++)
9588 if (action_arg_player_bits & (1 << i))
9589 KillPlayer(&stored_player[i]);
9594 case CA_SET_PLAYER_KEYS:
9596 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9597 int element = getSpecialActionElement(action_arg_element,
9598 action_arg_number, EL_KEY_1);
9600 if (IS_KEY(element))
9602 for (i = 0; i < MAX_PLAYERS; i++)
9604 if (trigger_player_bits & (1 << i))
9606 stored_player[i].key[KEY_NR(element)] = key_state;
9608 DrawGameDoorValues();
9616 case CA_SET_PLAYER_SPEED:
9618 for (i = 0; i < MAX_PLAYERS; i++)
9620 if (trigger_player_bits & (1 << i))
9622 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9624 if (action_arg == CA_ARG_SPEED_FASTER &&
9625 stored_player[i].cannot_move)
9627 action_arg_number = STEPSIZE_VERY_SLOW;
9629 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9630 action_arg == CA_ARG_SPEED_FASTER)
9632 action_arg_number = 2;
9633 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9636 else if (action_arg == CA_ARG_NUMBER_RESET)
9638 action_arg_number = level.initial_player_stepsize[i];
9642 getModifiedActionNumber(move_stepsize,
9645 action_arg_number_min,
9646 action_arg_number_max);
9648 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9655 case CA_SET_PLAYER_SHIELD:
9657 for (i = 0; i < MAX_PLAYERS; i++)
9659 if (trigger_player_bits & (1 << i))
9661 if (action_arg == CA_ARG_SHIELD_OFF)
9663 stored_player[i].shield_normal_time_left = 0;
9664 stored_player[i].shield_deadly_time_left = 0;
9666 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9668 stored_player[i].shield_normal_time_left = 999999;
9670 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9672 stored_player[i].shield_normal_time_left = 999999;
9673 stored_player[i].shield_deadly_time_left = 999999;
9681 #if USE_PLAYER_GRAVITY
9682 case CA_SET_PLAYER_GRAVITY:
9684 for (i = 0; i < MAX_PLAYERS; i++)
9686 if (trigger_player_bits & (1 << i))
9688 stored_player[i].gravity =
9689 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9690 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9691 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9692 stored_player[i].gravity);
9700 case CA_SET_PLAYER_ARTWORK:
9702 for (i = 0; i < MAX_PLAYERS; i++)
9704 if (trigger_player_bits & (1 << i))
9706 int artwork_element = action_arg_element;
9708 if (action_arg == CA_ARG_ELEMENT_RESET)
9710 (level.use_artwork_element[i] ? level.artwork_element[i] :
9711 stored_player[i].element_nr);
9713 #if USE_GFX_RESET_PLAYER_ARTWORK
9714 if (stored_player[i].artwork_element != artwork_element)
9715 stored_player[i].Frame = 0;
9718 stored_player[i].artwork_element = artwork_element;
9720 SetPlayerWaiting(&stored_player[i], FALSE);
9722 /* set number of special actions for bored and sleeping animation */
9723 stored_player[i].num_special_action_bored =
9724 get_num_special_action(artwork_element,
9725 ACTION_BORING_1, ACTION_BORING_LAST);
9726 stored_player[i].num_special_action_sleeping =
9727 get_num_special_action(artwork_element,
9728 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9735 /* ---------- CE actions ---------------------------------------------- */
9737 case CA_SET_CE_VALUE:
9739 #if USE_NEW_CUSTOM_VALUE
9740 int last_ce_value = CustomValue[x][y];
9742 CustomValue[x][y] = action_arg_number_new;
9744 if (CustomValue[x][y] != last_ce_value)
9746 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9747 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9749 if (CustomValue[x][y] == 0)
9751 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9752 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9760 case CA_SET_CE_SCORE:
9762 #if USE_NEW_CUSTOM_VALUE
9763 int last_ce_score = ei->collect_score;
9765 ei->collect_score = action_arg_number_new;
9767 if (ei->collect_score != last_ce_score)
9769 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9770 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9772 if (ei->collect_score == 0)
9776 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9777 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9780 This is a very special case that seems to be a mixture between
9781 CheckElementChange() and CheckTriggeredElementChange(): while
9782 the first one only affects single elements that are triggered
9783 directly, the second one affects multiple elements in the playfield
9784 that are triggered indirectly by another element. This is a third
9785 case: Changing the CE score always affects multiple identical CEs,
9786 so every affected CE must be checked, not only the single CE for
9787 which the CE score was changed in the first place (as every instance
9788 of that CE shares the same CE score, and therefore also can change)!
9790 SCAN_PLAYFIELD(xx, yy)
9792 if (Feld[xx][yy] == element)
9793 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9794 CE_SCORE_GETS_ZERO);
9803 /* ---------- engine actions ------------------------------------------ */
9805 case CA_SET_ENGINE_SCAN_MODE:
9807 InitPlayfieldScanMode(action_arg);
9817 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9819 int old_element = Feld[x][y];
9820 int new_element = GetElementFromGroupElement(element);
9821 int previous_move_direction = MovDir[x][y];
9822 #if USE_NEW_CUSTOM_VALUE
9823 int last_ce_value = CustomValue[x][y];
9825 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9826 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9827 boolean add_player_onto_element = (new_element_is_player &&
9828 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9829 /* this breaks SnakeBite when a snake is
9830 halfway through a door that closes */
9831 /* NOW FIXED AT LEVEL INIT IN files.c */
9832 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9834 IS_WALKABLE(old_element));
9837 /* check if element under the player changes from accessible to unaccessible
9838 (needed for special case of dropping element which then changes) */
9839 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9840 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9848 if (!add_player_onto_element)
9850 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9851 RemoveMovingField(x, y);
9855 Feld[x][y] = new_element;
9857 #if !USE_GFX_RESET_GFX_ANIMATION
9858 ResetGfxAnimation(x, y);
9859 ResetRandomAnimationValue(x, y);
9862 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9863 MovDir[x][y] = previous_move_direction;
9865 #if USE_NEW_CUSTOM_VALUE
9866 if (element_info[new_element].use_last_ce_value)
9867 CustomValue[x][y] = last_ce_value;
9870 InitField_WithBug1(x, y, FALSE);
9872 new_element = Feld[x][y]; /* element may have changed */
9874 #if USE_GFX_RESET_GFX_ANIMATION
9875 ResetGfxAnimation(x, y);
9876 ResetRandomAnimationValue(x, y);
9879 DrawLevelField(x, y);
9881 if (GFX_CRUMBLED(new_element))
9882 DrawLevelFieldCrumbledSandNeighbours(x, y);
9886 /* check if element under the player changes from accessible to unaccessible
9887 (needed for special case of dropping element which then changes) */
9888 /* (must be checked after creating new element for walkable group elements) */
9889 #if USE_FIX_KILLED_BY_NON_WALKABLE
9890 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9891 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9898 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9899 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9908 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9909 if (new_element_is_player)
9910 RelocatePlayer(x, y, new_element);
9913 ChangeCount[x][y]++; /* count number of changes in the same frame */
9915 TestIfBadThingTouchesPlayer(x, y);
9916 TestIfPlayerTouchesCustomElement(x, y);
9917 TestIfElementTouchesCustomElement(x, y);
9920 static void CreateField(int x, int y, int element)
9922 CreateFieldExt(x, y, element, FALSE);
9925 static void CreateElementFromChange(int x, int y, int element)
9927 element = GET_VALID_RUNTIME_ELEMENT(element);
9929 #if USE_STOP_CHANGED_ELEMENTS
9930 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9932 int old_element = Feld[x][y];
9934 /* prevent changed element from moving in same engine frame
9935 unless both old and new element can either fall or move */
9936 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9937 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9942 CreateFieldExt(x, y, element, TRUE);
9945 static boolean ChangeElement(int x, int y, int element, int page)
9947 struct ElementInfo *ei = &element_info[element];
9948 struct ElementChangeInfo *change = &ei->change_page[page];
9949 int ce_value = CustomValue[x][y];
9950 int ce_score = ei->collect_score;
9952 int old_element = Feld[x][y];
9954 /* always use default change event to prevent running into a loop */
9955 if (ChangeEvent[x][y] == -1)
9956 ChangeEvent[x][y] = CE_DELAY;
9958 if (ChangeEvent[x][y] == CE_DELAY)
9960 /* reset actual trigger element, trigger player and action element */
9961 change->actual_trigger_element = EL_EMPTY;
9962 change->actual_trigger_player = EL_PLAYER_1;
9963 change->actual_trigger_side = CH_SIDE_NONE;
9964 change->actual_trigger_ce_value = 0;
9965 change->actual_trigger_ce_score = 0;
9968 /* do not change elements more than a specified maximum number of changes */
9969 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9972 ChangeCount[x][y]++; /* count number of changes in the same frame */
9974 if (change->explode)
9981 if (change->use_target_content)
9983 boolean complete_replace = TRUE;
9984 boolean can_replace[3][3];
9987 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9990 boolean is_walkable;
9991 boolean is_diggable;
9992 boolean is_collectible;
9993 boolean is_removable;
9994 boolean is_destructible;
9995 int ex = x + xx - 1;
9996 int ey = y + yy - 1;
9997 int content_element = change->target_content.e[xx][yy];
10000 can_replace[xx][yy] = TRUE;
10002 if (ex == x && ey == y) /* do not check changing element itself */
10005 if (content_element == EL_EMPTY_SPACE)
10007 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10012 if (!IN_LEV_FIELD(ex, ey))
10014 can_replace[xx][yy] = FALSE;
10015 complete_replace = FALSE;
10022 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10023 e = MovingOrBlocked2Element(ex, ey);
10025 is_empty = (IS_FREE(ex, ey) ||
10026 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10028 is_walkable = (is_empty || IS_WALKABLE(e));
10029 is_diggable = (is_empty || IS_DIGGABLE(e));
10030 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10031 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10032 is_removable = (is_diggable || is_collectible);
10034 can_replace[xx][yy] =
10035 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10036 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10037 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10038 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10039 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10040 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10041 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10043 if (!can_replace[xx][yy])
10044 complete_replace = FALSE;
10047 if (!change->only_if_complete || complete_replace)
10049 boolean something_has_changed = FALSE;
10051 if (change->only_if_complete && change->use_random_replace &&
10052 RND(100) < change->random_percentage)
10055 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10057 int ex = x + xx - 1;
10058 int ey = y + yy - 1;
10059 int content_element;
10061 if (can_replace[xx][yy] && (!change->use_random_replace ||
10062 RND(100) < change->random_percentage))
10064 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10065 RemoveMovingField(ex, ey);
10067 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10069 content_element = change->target_content.e[xx][yy];
10070 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10071 ce_value, ce_score);
10073 CreateElementFromChange(ex, ey, target_element);
10075 something_has_changed = TRUE;
10077 /* for symmetry reasons, freeze newly created border elements */
10078 if (ex != x || ey != y)
10079 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10083 if (something_has_changed)
10085 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10086 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10092 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10093 ce_value, ce_score);
10095 if (element == EL_DIAGONAL_GROWING ||
10096 element == EL_DIAGONAL_SHRINKING)
10098 target_element = Store[x][y];
10100 Store[x][y] = EL_EMPTY;
10103 CreateElementFromChange(x, y, target_element);
10105 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10106 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10109 /* this uses direct change before indirect change */
10110 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10115 #if USE_NEW_DELAYED_ACTION
10117 static void HandleElementChange(int x, int y, int page)
10119 int element = MovingOrBlocked2Element(x, y);
10120 struct ElementInfo *ei = &element_info[element];
10121 struct ElementChangeInfo *change = &ei->change_page[page];
10124 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10125 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10128 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10129 x, y, element, element_info[element].token_name);
10130 printf("HandleElementChange(): This should never happen!\n");
10135 /* this can happen with classic bombs on walkable, changing elements */
10136 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10139 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10140 ChangeDelay[x][y] = 0;
10146 if (ChangeDelay[x][y] == 0) /* initialize element change */
10148 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10150 if (change->can_change)
10153 /* !!! not clear why graphic animation should be reset at all here !!! */
10154 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10155 #if USE_GFX_RESET_WHEN_NOT_MOVING
10156 /* when a custom element is about to change (for example by change delay),
10157 do not reset graphic animation when the custom element is moving */
10158 if (!IS_MOVING(x, y))
10161 ResetGfxAnimation(x, y);
10162 ResetRandomAnimationValue(x, y);
10166 if (change->pre_change_function)
10167 change->pre_change_function(x, y);
10171 ChangeDelay[x][y]--;
10173 if (ChangeDelay[x][y] != 0) /* continue element change */
10175 if (change->can_change)
10177 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10179 if (IS_ANIMATED(graphic))
10180 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10182 if (change->change_function)
10183 change->change_function(x, y);
10186 else /* finish element change */
10188 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10190 page = ChangePage[x][y];
10191 ChangePage[x][y] = -1;
10193 change = &ei->change_page[page];
10196 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10198 ChangeDelay[x][y] = 1; /* try change after next move step */
10199 ChangePage[x][y] = page; /* remember page to use for change */
10204 if (change->can_change)
10206 if (ChangeElement(x, y, element, page))
10208 if (change->post_change_function)
10209 change->post_change_function(x, y);
10213 if (change->has_action)
10214 ExecuteCustomElementAction(x, y, element, page);
10220 static void HandleElementChange(int x, int y, int page)
10222 int element = MovingOrBlocked2Element(x, y);
10223 struct ElementInfo *ei = &element_info[element];
10224 struct ElementChangeInfo *change = &ei->change_page[page];
10227 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10230 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10231 x, y, element, element_info[element].token_name);
10232 printf("HandleElementChange(): This should never happen!\n");
10237 /* this can happen with classic bombs on walkable, changing elements */
10238 if (!CAN_CHANGE(element))
10241 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10242 ChangeDelay[x][y] = 0;
10248 if (ChangeDelay[x][y] == 0) /* initialize element change */
10250 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10252 ResetGfxAnimation(x, y);
10253 ResetRandomAnimationValue(x, y);
10255 if (change->pre_change_function)
10256 change->pre_change_function(x, y);
10259 ChangeDelay[x][y]--;
10261 if (ChangeDelay[x][y] != 0) /* continue element change */
10263 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10265 if (IS_ANIMATED(graphic))
10266 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10268 if (change->change_function)
10269 change->change_function(x, y);
10271 else /* finish element change */
10273 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10275 page = ChangePage[x][y];
10276 ChangePage[x][y] = -1;
10278 change = &ei->change_page[page];
10281 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10283 ChangeDelay[x][y] = 1; /* try change after next move step */
10284 ChangePage[x][y] = page; /* remember page to use for change */
10289 if (ChangeElement(x, y, element, page))
10291 if (change->post_change_function)
10292 change->post_change_function(x, y);
10299 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10300 int trigger_element,
10302 int trigger_player,
10306 boolean change_done_any = FALSE;
10307 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10310 if (!(trigger_events[trigger_element][trigger_event]))
10314 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10315 trigger_event, recursion_loop_depth, recursion_loop_detected,
10316 recursion_loop_element, EL_NAME(recursion_loop_element));
10319 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10321 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10323 int element = EL_CUSTOM_START + i;
10324 boolean change_done = FALSE;
10327 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10328 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10331 for (p = 0; p < element_info[element].num_change_pages; p++)
10333 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10335 if (change->can_change_or_has_action &&
10336 change->has_event[trigger_event] &&
10337 change->trigger_side & trigger_side &&
10338 change->trigger_player & trigger_player &&
10339 change->trigger_page & trigger_page_bits &&
10340 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10342 change->actual_trigger_element = trigger_element;
10343 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10344 change->actual_trigger_side = trigger_side;
10345 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10346 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10348 if ((change->can_change && !change_done) || change->has_action)
10352 SCAN_PLAYFIELD(x, y)
10354 if (Feld[x][y] == element)
10356 if (change->can_change && !change_done)
10358 ChangeDelay[x][y] = 1;
10359 ChangeEvent[x][y] = trigger_event;
10361 HandleElementChange(x, y, p);
10363 #if USE_NEW_DELAYED_ACTION
10364 else if (change->has_action)
10366 ExecuteCustomElementAction(x, y, element, p);
10367 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10370 if (change->has_action)
10372 ExecuteCustomElementAction(x, y, element, p);
10373 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10379 if (change->can_change)
10381 change_done = TRUE;
10382 change_done_any = TRUE;
10389 RECURSION_LOOP_DETECTION_END();
10391 return change_done_any;
10394 static boolean CheckElementChangeExt(int x, int y,
10396 int trigger_element,
10398 int trigger_player,
10401 boolean change_done = FALSE;
10404 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10405 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10408 if (Feld[x][y] == EL_BLOCKED)
10410 Blocked2Moving(x, y, &x, &y);
10411 element = Feld[x][y];
10415 /* check if element has already changed */
10416 if (Feld[x][y] != element)
10419 /* check if element has already changed or is about to change after moving */
10420 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10421 Feld[x][y] != element) ||
10423 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10424 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10425 ChangePage[x][y] != -1)))
10430 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10431 trigger_event, recursion_loop_depth, recursion_loop_detected,
10432 recursion_loop_element, EL_NAME(recursion_loop_element));
10435 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10437 for (p = 0; p < element_info[element].num_change_pages; p++)
10439 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10441 /* check trigger element for all events where the element that is checked
10442 for changing interacts with a directly adjacent element -- this is
10443 different to element changes that affect other elements to change on the
10444 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10445 boolean check_trigger_element =
10446 (trigger_event == CE_TOUCHING_X ||
10447 trigger_event == CE_HITTING_X ||
10448 trigger_event == CE_HIT_BY_X ||
10450 /* this one was forgotten until 3.2.3 */
10451 trigger_event == CE_DIGGING_X);
10454 if (change->can_change_or_has_action &&
10455 change->has_event[trigger_event] &&
10456 change->trigger_side & trigger_side &&
10457 change->trigger_player & trigger_player &&
10458 (!check_trigger_element ||
10459 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10461 change->actual_trigger_element = trigger_element;
10462 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10463 change->actual_trigger_side = trigger_side;
10464 change->actual_trigger_ce_value = CustomValue[x][y];
10465 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10467 /* special case: trigger element not at (x,y) position for some events */
10468 if (check_trigger_element)
10480 { 0, 0 }, { 0, 0 }, { 0, 0 },
10484 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10485 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10487 change->actual_trigger_ce_value = CustomValue[xx][yy];
10488 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10491 if (change->can_change && !change_done)
10493 ChangeDelay[x][y] = 1;
10494 ChangeEvent[x][y] = trigger_event;
10496 HandleElementChange(x, y, p);
10498 change_done = TRUE;
10500 #if USE_NEW_DELAYED_ACTION
10501 else if (change->has_action)
10503 ExecuteCustomElementAction(x, y, element, p);
10504 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10507 if (change->has_action)
10509 ExecuteCustomElementAction(x, y, element, p);
10510 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10516 RECURSION_LOOP_DETECTION_END();
10518 return change_done;
10521 static void PlayPlayerSound(struct PlayerInfo *player)
10523 int jx = player->jx, jy = player->jy;
10524 int sound_element = player->artwork_element;
10525 int last_action = player->last_action_waiting;
10526 int action = player->action_waiting;
10528 if (player->is_waiting)
10530 if (action != last_action)
10531 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10533 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10537 if (action != last_action)
10538 StopSound(element_info[sound_element].sound[last_action]);
10540 if (last_action == ACTION_SLEEPING)
10541 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10545 static void PlayAllPlayersSound()
10549 for (i = 0; i < MAX_PLAYERS; i++)
10550 if (stored_player[i].active)
10551 PlayPlayerSound(&stored_player[i]);
10554 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10556 boolean last_waiting = player->is_waiting;
10557 int move_dir = player->MovDir;
10559 player->dir_waiting = move_dir;
10560 player->last_action_waiting = player->action_waiting;
10564 if (!last_waiting) /* not waiting -> waiting */
10566 player->is_waiting = TRUE;
10568 player->frame_counter_bored =
10570 game.player_boring_delay_fixed +
10571 GetSimpleRandom(game.player_boring_delay_random);
10572 player->frame_counter_sleeping =
10574 game.player_sleeping_delay_fixed +
10575 GetSimpleRandom(game.player_sleeping_delay_random);
10577 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10580 if (game.player_sleeping_delay_fixed +
10581 game.player_sleeping_delay_random > 0 &&
10582 player->anim_delay_counter == 0 &&
10583 player->post_delay_counter == 0 &&
10584 FrameCounter >= player->frame_counter_sleeping)
10585 player->is_sleeping = TRUE;
10586 else if (game.player_boring_delay_fixed +
10587 game.player_boring_delay_random > 0 &&
10588 FrameCounter >= player->frame_counter_bored)
10589 player->is_bored = TRUE;
10591 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10592 player->is_bored ? ACTION_BORING :
10595 if (player->is_sleeping && player->use_murphy)
10597 /* special case for sleeping Murphy when leaning against non-free tile */
10599 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10600 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10601 !IS_MOVING(player->jx - 1, player->jy)))
10602 move_dir = MV_LEFT;
10603 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10604 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10605 !IS_MOVING(player->jx + 1, player->jy)))
10606 move_dir = MV_RIGHT;
10608 player->is_sleeping = FALSE;
10610 player->dir_waiting = move_dir;
10613 if (player->is_sleeping)
10615 if (player->num_special_action_sleeping > 0)
10617 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10619 int last_special_action = player->special_action_sleeping;
10620 int num_special_action = player->num_special_action_sleeping;
10621 int special_action =
10622 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10623 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10624 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10625 last_special_action + 1 : ACTION_SLEEPING);
10626 int special_graphic =
10627 el_act_dir2img(player->artwork_element, special_action, move_dir);
10629 player->anim_delay_counter =
10630 graphic_info[special_graphic].anim_delay_fixed +
10631 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10632 player->post_delay_counter =
10633 graphic_info[special_graphic].post_delay_fixed +
10634 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10636 player->special_action_sleeping = special_action;
10639 if (player->anim_delay_counter > 0)
10641 player->action_waiting = player->special_action_sleeping;
10642 player->anim_delay_counter--;
10644 else if (player->post_delay_counter > 0)
10646 player->post_delay_counter--;
10650 else if (player->is_bored)
10652 if (player->num_special_action_bored > 0)
10654 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10656 int special_action =
10657 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10658 int special_graphic =
10659 el_act_dir2img(player->artwork_element, special_action, move_dir);
10661 player->anim_delay_counter =
10662 graphic_info[special_graphic].anim_delay_fixed +
10663 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10664 player->post_delay_counter =
10665 graphic_info[special_graphic].post_delay_fixed +
10666 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10668 player->special_action_bored = special_action;
10671 if (player->anim_delay_counter > 0)
10673 player->action_waiting = player->special_action_bored;
10674 player->anim_delay_counter--;
10676 else if (player->post_delay_counter > 0)
10678 player->post_delay_counter--;
10683 else if (last_waiting) /* waiting -> not waiting */
10685 player->is_waiting = FALSE;
10686 player->is_bored = FALSE;
10687 player->is_sleeping = FALSE;
10689 player->frame_counter_bored = -1;
10690 player->frame_counter_sleeping = -1;
10692 player->anim_delay_counter = 0;
10693 player->post_delay_counter = 0;
10695 player->dir_waiting = player->MovDir;
10696 player->action_waiting = ACTION_DEFAULT;
10698 player->special_action_bored = ACTION_DEFAULT;
10699 player->special_action_sleeping = ACTION_DEFAULT;
10703 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10705 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10706 int left = player_action & JOY_LEFT;
10707 int right = player_action & JOY_RIGHT;
10708 int up = player_action & JOY_UP;
10709 int down = player_action & JOY_DOWN;
10710 int button1 = player_action & JOY_BUTTON_1;
10711 int button2 = player_action & JOY_BUTTON_2;
10712 int dx = (left ? -1 : right ? 1 : 0);
10713 int dy = (up ? -1 : down ? 1 : 0);
10715 if (!player->active || tape.pausing)
10721 snapped = SnapField(player, dx, dy);
10725 dropped = DropElement(player);
10727 moved = MovePlayer(player, dx, dy);
10730 if (tape.single_step && tape.recording && !tape.pausing)
10732 if (button1 || (dropped && !moved))
10734 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10735 SnapField(player, 0, 0); /* stop snapping */
10739 SetPlayerWaiting(player, FALSE);
10741 return player_action;
10745 /* no actions for this player (no input at player's configured device) */
10747 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10748 SnapField(player, 0, 0);
10749 CheckGravityMovementWhenNotMoving(player);
10751 if (player->MovPos == 0)
10752 SetPlayerWaiting(player, TRUE);
10754 if (player->MovPos == 0) /* needed for tape.playing */
10755 player->is_moving = FALSE;
10757 player->is_dropping = FALSE;
10758 player->is_dropping_pressed = FALSE;
10759 player->drop_pressed_delay = 0;
10765 static void CheckLevelTime()
10769 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10771 if (level.native_em_level->lev->home == 0) /* all players at home */
10773 PlayerWins(local_player);
10775 AllPlayersGone = TRUE;
10777 level.native_em_level->lev->home = -1;
10780 if (level.native_em_level->ply[0]->alive == 0 &&
10781 level.native_em_level->ply[1]->alive == 0 &&
10782 level.native_em_level->ply[2]->alive == 0 &&
10783 level.native_em_level->ply[3]->alive == 0) /* all dead */
10784 AllPlayersGone = TRUE;
10787 if (TimeFrames >= FRAMES_PER_SECOND)
10792 for (i = 0; i < MAX_PLAYERS; i++)
10794 struct PlayerInfo *player = &stored_player[i];
10796 if (SHIELD_ON(player))
10798 player->shield_normal_time_left--;
10800 if (player->shield_deadly_time_left > 0)
10801 player->shield_deadly_time_left--;
10805 if (!local_player->LevelSolved && !level.use_step_counter)
10813 if (TimeLeft <= 10 && setup.time_limit)
10814 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10817 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10819 DisplayGameControlValues();
10821 DrawGameValue_Time(TimeLeft);
10824 if (!TimeLeft && setup.time_limit)
10826 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10827 level.native_em_level->lev->killed_out_of_time = TRUE;
10829 for (i = 0; i < MAX_PLAYERS; i++)
10830 KillPlayer(&stored_player[i]);
10834 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10836 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10838 DisplayGameControlValues();
10841 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10842 DrawGameValue_Time(TimePlayed);
10845 level.native_em_level->lev->time =
10846 (level.time == 0 ? TimePlayed : TimeLeft);
10849 if (tape.recording || tape.playing)
10850 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10853 DrawGameDoorValues();
10856 void AdvanceFrameAndPlayerCounters(int player_nr)
10860 /* advance frame counters (global frame counter and time frame counter) */
10864 /* advance player counters (counters for move delay, move animation etc.) */
10865 for (i = 0; i < MAX_PLAYERS; i++)
10867 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10868 int move_delay_value = stored_player[i].move_delay_value;
10869 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10871 if (!advance_player_counters) /* not all players may be affected */
10874 #if USE_NEW_PLAYER_ANIM
10875 if (move_frames == 0) /* less than one move per game frame */
10877 int stepsize = TILEX / move_delay_value;
10878 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10879 int count = (stored_player[i].is_moving ?
10880 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10882 if (count % delay == 0)
10887 stored_player[i].Frame += move_frames;
10889 if (stored_player[i].MovPos != 0)
10890 stored_player[i].StepFrame += move_frames;
10892 if (stored_player[i].move_delay > 0)
10893 stored_player[i].move_delay--;
10895 /* due to bugs in previous versions, counter must count up, not down */
10896 if (stored_player[i].push_delay != -1)
10897 stored_player[i].push_delay++;
10899 if (stored_player[i].drop_delay > 0)
10900 stored_player[i].drop_delay--;
10902 if (stored_player[i].is_dropping_pressed)
10903 stored_player[i].drop_pressed_delay++;
10907 void StartGameActions(boolean init_network_game, boolean record_tape,
10910 unsigned long new_random_seed = InitRND(random_seed);
10913 TapeStartRecording(new_random_seed);
10915 #if defined(NETWORK_AVALIABLE)
10916 if (init_network_game)
10918 SendToServer_StartPlaying();
10929 static unsigned long game_frame_delay = 0;
10930 unsigned long game_frame_delay_value;
10931 byte *recorded_player_action;
10932 byte summarized_player_action = 0;
10933 byte tape_action[MAX_PLAYERS];
10936 /* detect endless loops, caused by custom element programming */
10937 if (recursion_loop_detected && recursion_loop_depth == 0)
10939 char *message = getStringCat3("Internal Error ! Element ",
10940 EL_NAME(recursion_loop_element),
10941 " caused endless loop ! Quit the game ?");
10943 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10944 EL_NAME(recursion_loop_element));
10946 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10948 recursion_loop_detected = FALSE; /* if game should be continued */
10955 if (game.restart_level)
10956 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10958 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10960 if (level.native_em_level->lev->home == 0) /* all players at home */
10962 PlayerWins(local_player);
10964 AllPlayersGone = TRUE;
10966 level.native_em_level->lev->home = -1;
10969 if (level.native_em_level->ply[0]->alive == 0 &&
10970 level.native_em_level->ply[1]->alive == 0 &&
10971 level.native_em_level->ply[2]->alive == 0 &&
10972 level.native_em_level->ply[3]->alive == 0) /* all dead */
10973 AllPlayersGone = TRUE;
10976 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10979 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10982 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10985 game_frame_delay_value =
10986 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10988 if (tape.playing && tape.warp_forward && !tape.pausing)
10989 game_frame_delay_value = 0;
10991 /* ---------- main game synchronization point ---------- */
10993 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10995 if (network_playing && !network_player_action_received)
10997 /* try to get network player actions in time */
10999 #if defined(NETWORK_AVALIABLE)
11000 /* last chance to get network player actions without main loop delay */
11001 HandleNetworking();
11004 /* game was quit by network peer */
11005 if (game_status != GAME_MODE_PLAYING)
11008 if (!network_player_action_received)
11009 return; /* failed to get network player actions in time */
11011 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11017 /* at this point we know that we really continue executing the game */
11019 network_player_action_received = FALSE;
11021 /* when playing tape, read previously recorded player input from tape data */
11022 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11025 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11030 if (tape.set_centered_player)
11032 game.centered_player_nr_next = tape.centered_player_nr_next;
11033 game.set_centered_player = TRUE;
11036 for (i = 0; i < MAX_PLAYERS; i++)
11038 summarized_player_action |= stored_player[i].action;
11040 if (!network_playing)
11041 stored_player[i].effective_action = stored_player[i].action;
11044 #if defined(NETWORK_AVALIABLE)
11045 if (network_playing)
11046 SendToServer_MovePlayer(summarized_player_action);
11049 if (!options.network && !setup.team_mode)
11050 local_player->effective_action = summarized_player_action;
11052 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11054 for (i = 0; i < MAX_PLAYERS; i++)
11055 stored_player[i].effective_action =
11056 (i == game.centered_player_nr ? summarized_player_action : 0);
11059 if (recorded_player_action != NULL)
11060 for (i = 0; i < MAX_PLAYERS; i++)
11061 stored_player[i].effective_action = recorded_player_action[i];
11063 for (i = 0; i < MAX_PLAYERS; i++)
11065 tape_action[i] = stored_player[i].effective_action;
11067 /* (this can only happen in the R'n'D game engine) */
11068 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11069 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11072 /* only record actions from input devices, but not programmed actions */
11073 if (tape.recording)
11074 TapeRecordAction(tape_action);
11076 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11078 GameActions_EM_Main();
11086 void GameActions_EM_Main()
11088 byte effective_action[MAX_PLAYERS];
11089 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11092 for (i = 0; i < MAX_PLAYERS; i++)
11093 effective_action[i] = stored_player[i].effective_action;
11095 GameActions_EM(effective_action, warp_mode);
11099 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11102 void GameActions_RND()
11104 int magic_wall_x = 0, magic_wall_y = 0;
11105 int i, x, y, element, graphic;
11107 InitPlayfieldScanModeVars();
11109 #if USE_ONE_MORE_CHANGE_PER_FRAME
11110 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11112 SCAN_PLAYFIELD(x, y)
11114 ChangeCount[x][y] = 0;
11115 ChangeEvent[x][y] = -1;
11120 if (game.set_centered_player)
11122 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11124 /* switching to "all players" only possible if all players fit to screen */
11125 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11127 game.centered_player_nr_next = game.centered_player_nr;
11128 game.set_centered_player = FALSE;
11131 /* do not switch focus to non-existing (or non-active) player */
11132 if (game.centered_player_nr_next >= 0 &&
11133 !stored_player[game.centered_player_nr_next].active)
11135 game.centered_player_nr_next = game.centered_player_nr;
11136 game.set_centered_player = FALSE;
11140 if (game.set_centered_player &&
11141 ScreenMovPos == 0) /* screen currently aligned at tile position */
11145 if (game.centered_player_nr_next == -1)
11147 setScreenCenteredToAllPlayers(&sx, &sy);
11151 sx = stored_player[game.centered_player_nr_next].jx;
11152 sy = stored_player[game.centered_player_nr_next].jy;
11155 game.centered_player_nr = game.centered_player_nr_next;
11156 game.set_centered_player = FALSE;
11158 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11159 DrawGameDoorValues();
11162 for (i = 0; i < MAX_PLAYERS; i++)
11164 int actual_player_action = stored_player[i].effective_action;
11167 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11168 - rnd_equinox_tetrachloride 048
11169 - rnd_equinox_tetrachloride_ii 096
11170 - rnd_emanuel_schmieg 002
11171 - doctor_sloan_ww 001, 020
11173 if (stored_player[i].MovPos == 0)
11174 CheckGravityMovement(&stored_player[i]);
11177 /* overwrite programmed action with tape action */
11178 if (stored_player[i].programmed_action)
11179 actual_player_action = stored_player[i].programmed_action;
11181 PlayerActions(&stored_player[i], actual_player_action);
11183 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11186 ScrollScreen(NULL, SCROLL_GO_ON);
11188 /* for backwards compatibility, the following code emulates a fixed bug that
11189 occured when pushing elements (causing elements that just made their last
11190 pushing step to already (if possible) make their first falling step in the
11191 same game frame, which is bad); this code is also needed to use the famous
11192 "spring push bug" which is used in older levels and might be wanted to be
11193 used also in newer levels, but in this case the buggy pushing code is only
11194 affecting the "spring" element and no other elements */
11196 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11198 for (i = 0; i < MAX_PLAYERS; i++)
11200 struct PlayerInfo *player = &stored_player[i];
11201 int x = player->jx;
11202 int y = player->jy;
11204 if (player->active && player->is_pushing && player->is_moving &&
11206 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11207 Feld[x][y] == EL_SPRING))
11209 ContinueMoving(x, y);
11211 /* continue moving after pushing (this is actually a bug) */
11212 if (!IS_MOVING(x, y))
11213 Stop[x][y] = FALSE;
11219 debug_print_timestamp(0, "start main loop profiling");
11222 SCAN_PLAYFIELD(x, y)
11224 ChangeCount[x][y] = 0;
11225 ChangeEvent[x][y] = -1;
11227 /* this must be handled before main playfield loop */
11228 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11231 if (MovDelay[x][y] <= 0)
11235 #if USE_NEW_SNAP_DELAY
11236 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11239 if (MovDelay[x][y] <= 0)
11242 DrawLevelField(x, y);
11244 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11250 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11252 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11253 printf("GameActions(): This should never happen!\n");
11255 ChangePage[x][y] = -1;
11259 Stop[x][y] = FALSE;
11260 if (WasJustMoving[x][y] > 0)
11261 WasJustMoving[x][y]--;
11262 if (WasJustFalling[x][y] > 0)
11263 WasJustFalling[x][y]--;
11264 if (CheckCollision[x][y] > 0)
11265 CheckCollision[x][y]--;
11266 if (CheckImpact[x][y] > 0)
11267 CheckImpact[x][y]--;
11271 /* reset finished pushing action (not done in ContinueMoving() to allow
11272 continuous pushing animation for elements with zero push delay) */
11273 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11275 ResetGfxAnimation(x, y);
11276 DrawLevelField(x, y);
11280 if (IS_BLOCKED(x, y))
11284 Blocked2Moving(x, y, &oldx, &oldy);
11285 if (!IS_MOVING(oldx, oldy))
11287 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11288 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11289 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11290 printf("GameActions(): This should never happen!\n");
11297 debug_print_timestamp(0, "- time for pre-main loop:");
11300 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11301 SCAN_PLAYFIELD(x, y)
11303 element = Feld[x][y];
11304 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11309 int element2 = element;
11310 int graphic2 = graphic;
11312 int element2 = Feld[x][y];
11313 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11315 int last_gfx_frame = GfxFrame[x][y];
11317 if (graphic_info[graphic2].anim_global_sync)
11318 GfxFrame[x][y] = FrameCounter;
11319 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11320 GfxFrame[x][y] = CustomValue[x][y];
11321 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11322 GfxFrame[x][y] = element_info[element2].collect_score;
11323 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11324 GfxFrame[x][y] = ChangeDelay[x][y];
11326 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11327 DrawLevelGraphicAnimation(x, y, graphic2);
11330 ResetGfxFrame(x, y, TRUE);
11334 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11335 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11336 ResetRandomAnimationValue(x, y);
11340 SetRandomAnimationValue(x, y);
11344 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11347 #endif // -------------------- !!! TEST ONLY !!! --------------------
11350 debug_print_timestamp(0, "- time for TEST loop: -->");
11353 SCAN_PLAYFIELD(x, y)
11355 element = Feld[x][y];
11356 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11358 ResetGfxFrame(x, y, TRUE);
11360 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11361 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11362 ResetRandomAnimationValue(x, y);
11364 SetRandomAnimationValue(x, y);
11366 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11368 if (IS_INACTIVE(element))
11370 if (IS_ANIMATED(graphic))
11371 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11376 /* this may take place after moving, so 'element' may have changed */
11377 if (IS_CHANGING(x, y) &&
11378 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11380 int page = element_info[element].event_page_nr[CE_DELAY];
11383 HandleElementChange(x, y, page);
11385 if (CAN_CHANGE(element))
11386 HandleElementChange(x, y, page);
11388 if (HAS_ACTION(element))
11389 ExecuteCustomElementAction(x, y, element, page);
11392 element = Feld[x][y];
11393 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11396 #if 0 // ---------------------------------------------------------------------
11398 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11402 element = Feld[x][y];
11403 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11405 if (IS_ANIMATED(graphic) &&
11406 !IS_MOVING(x, y) &&
11408 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11410 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11411 DrawTwinkleOnField(x, y);
11413 else if (IS_MOVING(x, y))
11414 ContinueMoving(x, y);
11421 case EL_EM_EXIT_OPEN:
11422 case EL_SP_EXIT_OPEN:
11423 case EL_STEEL_EXIT_OPEN:
11424 case EL_EM_STEEL_EXIT_OPEN:
11425 case EL_SP_TERMINAL:
11426 case EL_SP_TERMINAL_ACTIVE:
11427 case EL_EXTRA_TIME:
11428 case EL_SHIELD_NORMAL:
11429 case EL_SHIELD_DEADLY:
11430 if (IS_ANIMATED(graphic))
11431 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11434 case EL_DYNAMITE_ACTIVE:
11435 case EL_EM_DYNAMITE_ACTIVE:
11436 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11437 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11438 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11439 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11440 case EL_SP_DISK_RED_ACTIVE:
11441 CheckDynamite(x, y);
11444 case EL_AMOEBA_GROWING:
11445 AmoebeWaechst(x, y);
11448 case EL_AMOEBA_SHRINKING:
11449 AmoebaDisappearing(x, y);
11452 #if !USE_NEW_AMOEBA_CODE
11453 case EL_AMOEBA_WET:
11454 case EL_AMOEBA_DRY:
11455 case EL_AMOEBA_FULL:
11457 case EL_EMC_DRIPPER:
11458 AmoebeAbleger(x, y);
11462 case EL_GAME_OF_LIFE:
11467 case EL_EXIT_CLOSED:
11471 case EL_EM_EXIT_CLOSED:
11475 case EL_STEEL_EXIT_CLOSED:
11476 CheckExitSteel(x, y);
11479 case EL_EM_STEEL_EXIT_CLOSED:
11480 CheckExitSteelEM(x, y);
11483 case EL_SP_EXIT_CLOSED:
11487 case EL_EXPANDABLE_WALL_GROWING:
11488 case EL_EXPANDABLE_STEELWALL_GROWING:
11489 MauerWaechst(x, y);
11492 case EL_EXPANDABLE_WALL:
11493 case EL_EXPANDABLE_WALL_HORIZONTAL:
11494 case EL_EXPANDABLE_WALL_VERTICAL:
11495 case EL_EXPANDABLE_WALL_ANY:
11496 case EL_BD_EXPANDABLE_WALL:
11497 MauerAbleger(x, y);
11500 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11501 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11502 case EL_EXPANDABLE_STEELWALL_ANY:
11503 MauerAblegerStahl(x, y);
11507 CheckForDragon(x, y);
11513 case EL_ELEMENT_SNAPPING:
11514 case EL_DIAGONAL_SHRINKING:
11515 case EL_DIAGONAL_GROWING:
11518 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11525 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11526 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11531 #else // ---------------------------------------------------------------------
11533 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11537 element = Feld[x][y];
11538 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11540 if (IS_ANIMATED(graphic) &&
11541 !IS_MOVING(x, y) &&
11543 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11545 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11546 DrawTwinkleOnField(x, y);
11548 else if ((element == EL_ACID ||
11549 element == EL_EXIT_OPEN ||
11550 element == EL_EM_EXIT_OPEN ||
11551 element == EL_SP_EXIT_OPEN ||
11552 element == EL_STEEL_EXIT_OPEN ||
11553 element == EL_EM_STEEL_EXIT_OPEN ||
11554 element == EL_SP_TERMINAL ||
11555 element == EL_SP_TERMINAL_ACTIVE ||
11556 element == EL_EXTRA_TIME ||
11557 element == EL_SHIELD_NORMAL ||
11558 element == EL_SHIELD_DEADLY) &&
11559 IS_ANIMATED(graphic))
11560 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11561 else if (IS_MOVING(x, y))
11562 ContinueMoving(x, y);
11563 else if (IS_ACTIVE_BOMB(element))
11564 CheckDynamite(x, y);
11565 else if (element == EL_AMOEBA_GROWING)
11566 AmoebeWaechst(x, y);
11567 else if (element == EL_AMOEBA_SHRINKING)
11568 AmoebaDisappearing(x, y);
11570 #if !USE_NEW_AMOEBA_CODE
11571 else if (IS_AMOEBALIVE(element))
11572 AmoebeAbleger(x, y);
11575 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11577 else if (element == EL_EXIT_CLOSED)
11579 else if (element == EL_EM_EXIT_CLOSED)
11581 else if (element == EL_STEEL_EXIT_CLOSED)
11582 CheckExitSteel(x, y);
11583 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11584 CheckExitSteelEM(x, y);
11585 else if (element == EL_SP_EXIT_CLOSED)
11587 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11588 element == EL_EXPANDABLE_STEELWALL_GROWING)
11589 MauerWaechst(x, y);
11590 else if (element == EL_EXPANDABLE_WALL ||
11591 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11592 element == EL_EXPANDABLE_WALL_VERTICAL ||
11593 element == EL_EXPANDABLE_WALL_ANY ||
11594 element == EL_BD_EXPANDABLE_WALL)
11595 MauerAbleger(x, y);
11596 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11597 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11598 element == EL_EXPANDABLE_STEELWALL_ANY)
11599 MauerAblegerStahl(x, y);
11600 else if (element == EL_FLAMES)
11601 CheckForDragon(x, y);
11602 else if (element == EL_EXPLOSION)
11603 ; /* drawing of correct explosion animation is handled separately */
11604 else if (element == EL_ELEMENT_SNAPPING ||
11605 element == EL_DIAGONAL_SHRINKING ||
11606 element == EL_DIAGONAL_GROWING)
11608 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11610 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11612 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11613 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11615 #endif // ---------------------------------------------------------------------
11617 if (IS_BELT_ACTIVE(element))
11618 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11620 if (game.magic_wall_active)
11622 int jx = local_player->jx, jy = local_player->jy;
11624 /* play the element sound at the position nearest to the player */
11625 if ((element == EL_MAGIC_WALL_FULL ||
11626 element == EL_MAGIC_WALL_ACTIVE ||
11627 element == EL_MAGIC_WALL_EMPTYING ||
11628 element == EL_BD_MAGIC_WALL_FULL ||
11629 element == EL_BD_MAGIC_WALL_ACTIVE ||
11630 element == EL_BD_MAGIC_WALL_EMPTYING ||
11631 element == EL_DC_MAGIC_WALL_FULL ||
11632 element == EL_DC_MAGIC_WALL_ACTIVE ||
11633 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11634 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11643 debug_print_timestamp(0, "- time for MAIN loop: -->");
11646 #if USE_NEW_AMOEBA_CODE
11647 /* new experimental amoeba growth stuff */
11648 if (!(FrameCounter % 8))
11650 static unsigned long random = 1684108901;
11652 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11654 x = RND(lev_fieldx);
11655 y = RND(lev_fieldy);
11656 element = Feld[x][y];
11658 if (!IS_PLAYER(x,y) &&
11659 (element == EL_EMPTY ||
11660 CAN_GROW_INTO(element) ||
11661 element == EL_QUICKSAND_EMPTY ||
11662 element == EL_QUICKSAND_FAST_EMPTY ||
11663 element == EL_ACID_SPLASH_LEFT ||
11664 element == EL_ACID_SPLASH_RIGHT))
11666 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11667 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11668 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11669 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11670 Feld[x][y] = EL_AMOEBA_DROP;
11673 random = random * 129 + 1;
11679 if (game.explosions_delayed)
11682 game.explosions_delayed = FALSE;
11684 SCAN_PLAYFIELD(x, y)
11686 element = Feld[x][y];
11688 if (ExplodeField[x][y])
11689 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11690 else if (element == EL_EXPLOSION)
11691 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11693 ExplodeField[x][y] = EX_TYPE_NONE;
11696 game.explosions_delayed = TRUE;
11699 if (game.magic_wall_active)
11701 if (!(game.magic_wall_time_left % 4))
11703 int element = Feld[magic_wall_x][magic_wall_y];
11705 if (element == EL_BD_MAGIC_WALL_FULL ||
11706 element == EL_BD_MAGIC_WALL_ACTIVE ||
11707 element == EL_BD_MAGIC_WALL_EMPTYING)
11708 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11709 else if (element == EL_DC_MAGIC_WALL_FULL ||
11710 element == EL_DC_MAGIC_WALL_ACTIVE ||
11711 element == EL_DC_MAGIC_WALL_EMPTYING)
11712 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11714 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11717 if (game.magic_wall_time_left > 0)
11719 game.magic_wall_time_left--;
11720 if (!game.magic_wall_time_left)
11722 SCAN_PLAYFIELD(x, y)
11724 element = Feld[x][y];
11726 if (element == EL_MAGIC_WALL_ACTIVE ||
11727 element == EL_MAGIC_WALL_FULL)
11729 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11730 DrawLevelField(x, y);
11732 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11733 element == EL_BD_MAGIC_WALL_FULL)
11735 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11736 DrawLevelField(x, y);
11738 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11739 element == EL_DC_MAGIC_WALL_FULL)
11741 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11742 DrawLevelField(x, y);
11746 game.magic_wall_active = FALSE;
11751 if (game.light_time_left > 0)
11753 game.light_time_left--;
11755 if (game.light_time_left == 0)
11756 RedrawAllLightSwitchesAndInvisibleElements();
11759 if (game.timegate_time_left > 0)
11761 game.timegate_time_left--;
11763 if (game.timegate_time_left == 0)
11764 CloseAllOpenTimegates();
11767 if (game.lenses_time_left > 0)
11769 game.lenses_time_left--;
11771 if (game.lenses_time_left == 0)
11772 RedrawAllInvisibleElementsForLenses();
11775 if (game.magnify_time_left > 0)
11777 game.magnify_time_left--;
11779 if (game.magnify_time_left == 0)
11780 RedrawAllInvisibleElementsForMagnifier();
11783 for (i = 0; i < MAX_PLAYERS; i++)
11785 struct PlayerInfo *player = &stored_player[i];
11787 if (SHIELD_ON(player))
11789 if (player->shield_deadly_time_left)
11790 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11791 else if (player->shield_normal_time_left)
11792 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11799 PlayAllPlayersSound();
11801 if (options.debug) /* calculate frames per second */
11803 static unsigned long fps_counter = 0;
11804 static int fps_frames = 0;
11805 unsigned long fps_delay_ms = Counter() - fps_counter;
11809 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11811 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11814 fps_counter = Counter();
11817 redraw_mask |= REDRAW_FPS;
11820 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11822 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11824 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11826 local_player->show_envelope = 0;
11830 debug_print_timestamp(0, "stop main loop profiling ");
11831 printf("----------------------------------------------------------\n");
11834 /* use random number generator in every frame to make it less predictable */
11835 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11839 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11841 int min_x = x, min_y = y, max_x = x, max_y = y;
11844 for (i = 0; i < MAX_PLAYERS; i++)
11846 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11848 if (!stored_player[i].active || &stored_player[i] == player)
11851 min_x = MIN(min_x, jx);
11852 min_y = MIN(min_y, jy);
11853 max_x = MAX(max_x, jx);
11854 max_y = MAX(max_y, jy);
11857 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11860 static boolean AllPlayersInVisibleScreen()
11864 for (i = 0; i < MAX_PLAYERS; i++)
11866 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11868 if (!stored_player[i].active)
11871 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11878 void ScrollLevel(int dx, int dy)
11881 static Bitmap *bitmap_db_field2 = NULL;
11882 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11889 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11890 /* only horizontal XOR vertical scroll direction allowed */
11891 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11896 if (bitmap_db_field2 == NULL)
11897 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11899 /* needed when blitting directly to same bitmap -- should not be needed with
11900 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11901 BlitBitmap(drawto_field, bitmap_db_field2,
11902 FX + TILEX * (dx == -1) - softscroll_offset,
11903 FY + TILEY * (dy == -1) - softscroll_offset,
11904 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11905 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11906 FX + TILEX * (dx == 1) - softscroll_offset,
11907 FY + TILEY * (dy == 1) - softscroll_offset);
11908 BlitBitmap(bitmap_db_field2, drawto_field,
11909 FX + TILEX * (dx == 1) - softscroll_offset,
11910 FY + TILEY * (dy == 1) - softscroll_offset,
11911 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11912 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11913 FX + TILEX * (dx == 1) - softscroll_offset,
11914 FY + TILEY * (dy == 1) - softscroll_offset);
11919 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11920 int xsize = (BX2 - BX1 + 1);
11921 int ysize = (BY2 - BY1 + 1);
11922 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11923 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11924 int step = (start < end ? +1 : -1);
11926 for (i = start; i != end; i += step)
11928 BlitBitmap(drawto_field, drawto_field,
11929 FX + TILEX * (dx != 0 ? i + step : 0),
11930 FY + TILEY * (dy != 0 ? i + step : 0),
11931 TILEX * (dx != 0 ? 1 : xsize),
11932 TILEY * (dy != 0 ? 1 : ysize),
11933 FX + TILEX * (dx != 0 ? i : 0),
11934 FY + TILEY * (dy != 0 ? i : 0));
11939 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11941 BlitBitmap(drawto_field, drawto_field,
11942 FX + TILEX * (dx == -1) - softscroll_offset,
11943 FY + TILEY * (dy == -1) - softscroll_offset,
11944 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11945 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11946 FX + TILEX * (dx == 1) - softscroll_offset,
11947 FY + TILEY * (dy == 1) - softscroll_offset);
11953 x = (dx == 1 ? BX1 : BX2);
11954 for (y = BY1; y <= BY2; y++)
11955 DrawScreenField(x, y);
11960 y = (dy == 1 ? BY1 : BY2);
11961 for (x = BX1; x <= BX2; x++)
11962 DrawScreenField(x, y);
11965 redraw_mask |= REDRAW_FIELD;
11968 static boolean canFallDown(struct PlayerInfo *player)
11970 int jx = player->jx, jy = player->jy;
11972 return (IN_LEV_FIELD(jx, jy + 1) &&
11973 (IS_FREE(jx, jy + 1) ||
11974 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11975 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11976 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11979 static boolean canPassField(int x, int y, int move_dir)
11981 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11982 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11983 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11984 int nextx = x + dx;
11985 int nexty = y + dy;
11986 int element = Feld[x][y];
11988 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11989 !CAN_MOVE(element) &&
11990 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11991 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11992 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11995 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11997 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11998 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11999 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12003 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12004 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12005 (IS_DIGGABLE(Feld[newx][newy]) ||
12006 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12007 canPassField(newx, newy, move_dir)));
12010 static void CheckGravityMovement(struct PlayerInfo *player)
12012 #if USE_PLAYER_GRAVITY
12013 if (player->gravity && !player->programmed_action)
12015 if (game.gravity && !player->programmed_action)
12018 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12019 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12020 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12021 int jx = player->jx, jy = player->jy;
12022 boolean player_is_moving_to_valid_field =
12023 (!player_is_snapping &&
12024 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12025 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12026 boolean player_can_fall_down = canFallDown(player);
12028 if (player_can_fall_down &&
12029 !player_is_moving_to_valid_field)
12030 player->programmed_action = MV_DOWN;
12034 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12036 return CheckGravityMovement(player);
12038 #if USE_PLAYER_GRAVITY
12039 if (player->gravity && !player->programmed_action)
12041 if (game.gravity && !player->programmed_action)
12044 int jx = player->jx, jy = player->jy;
12045 boolean field_under_player_is_free =
12046 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12047 boolean player_is_standing_on_valid_field =
12048 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12049 (IS_WALKABLE(Feld[jx][jy]) &&
12050 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12052 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12053 player->programmed_action = MV_DOWN;
12058 MovePlayerOneStep()
12059 -----------------------------------------------------------------------------
12060 dx, dy: direction (non-diagonal) to try to move the player to
12061 real_dx, real_dy: direction as read from input device (can be diagonal)
12064 boolean MovePlayerOneStep(struct PlayerInfo *player,
12065 int dx, int dy, int real_dx, int real_dy)
12067 int jx = player->jx, jy = player->jy;
12068 int new_jx = jx + dx, new_jy = jy + dy;
12069 #if !USE_FIXED_DONT_RUN_INTO
12073 boolean player_can_move = !player->cannot_move;
12075 if (!player->active || (!dx && !dy))
12076 return MP_NO_ACTION;
12078 player->MovDir = (dx < 0 ? MV_LEFT :
12079 dx > 0 ? MV_RIGHT :
12081 dy > 0 ? MV_DOWN : MV_NONE);
12083 if (!IN_LEV_FIELD(new_jx, new_jy))
12084 return MP_NO_ACTION;
12086 if (!player_can_move)
12088 if (player->MovPos == 0)
12090 player->is_moving = FALSE;
12091 player->is_digging = FALSE;
12092 player->is_collecting = FALSE;
12093 player->is_snapping = FALSE;
12094 player->is_pushing = FALSE;
12099 if (!options.network && game.centered_player_nr == -1 &&
12100 !AllPlayersInSight(player, new_jx, new_jy))
12101 return MP_NO_ACTION;
12103 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12104 return MP_NO_ACTION;
12107 #if !USE_FIXED_DONT_RUN_INTO
12108 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12110 /* (moved to DigField()) */
12111 if (player_can_move && DONT_RUN_INTO(element))
12113 if (element == EL_ACID && dx == 0 && dy == 1)
12115 SplashAcid(new_jx, new_jy);
12116 Feld[jx][jy] = EL_PLAYER_1;
12117 InitMovingField(jx, jy, MV_DOWN);
12118 Store[jx][jy] = EL_ACID;
12119 ContinueMoving(jx, jy);
12120 BuryPlayer(player);
12123 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12129 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12130 if (can_move != MP_MOVING)
12133 /* check if DigField() has caused relocation of the player */
12134 if (player->jx != jx || player->jy != jy)
12135 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12137 StorePlayer[jx][jy] = 0;
12138 player->last_jx = jx;
12139 player->last_jy = jy;
12140 player->jx = new_jx;
12141 player->jy = new_jy;
12142 StorePlayer[new_jx][new_jy] = player->element_nr;
12144 if (player->move_delay_value_next != -1)
12146 player->move_delay_value = player->move_delay_value_next;
12147 player->move_delay_value_next = -1;
12151 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12153 player->step_counter++;
12155 PlayerVisit[jx][jy] = FrameCounter;
12157 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12158 player->is_moving = TRUE;
12162 /* should better be called in MovePlayer(), but this breaks some tapes */
12163 ScrollPlayer(player, SCROLL_INIT);
12169 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12171 int jx = player->jx, jy = player->jy;
12172 int old_jx = jx, old_jy = jy;
12173 int moved = MP_NO_ACTION;
12175 if (!player->active)
12180 if (player->MovPos == 0)
12182 player->is_moving = FALSE;
12183 player->is_digging = FALSE;
12184 player->is_collecting = FALSE;
12185 player->is_snapping = FALSE;
12186 player->is_pushing = FALSE;
12192 if (player->move_delay > 0)
12195 player->move_delay = -1; /* set to "uninitialized" value */
12197 /* store if player is automatically moved to next field */
12198 player->is_auto_moving = (player->programmed_action != MV_NONE);
12200 /* remove the last programmed player action */
12201 player->programmed_action = 0;
12203 if (player->MovPos)
12205 /* should only happen if pre-1.2 tape recordings are played */
12206 /* this is only for backward compatibility */
12208 int original_move_delay_value = player->move_delay_value;
12211 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12215 /* scroll remaining steps with finest movement resolution */
12216 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12218 while (player->MovPos)
12220 ScrollPlayer(player, SCROLL_GO_ON);
12221 ScrollScreen(NULL, SCROLL_GO_ON);
12223 AdvanceFrameAndPlayerCounters(player->index_nr);
12229 player->move_delay_value = original_move_delay_value;
12232 player->is_active = FALSE;
12234 if (player->last_move_dir & MV_HORIZONTAL)
12236 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12237 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12241 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12242 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12245 #if USE_FIXED_BORDER_RUNNING_GFX
12246 if (!moved && !player->is_active)
12248 player->is_moving = FALSE;
12249 player->is_digging = FALSE;
12250 player->is_collecting = FALSE;
12251 player->is_snapping = FALSE;
12252 player->is_pushing = FALSE;
12260 if (moved & MP_MOVING && !ScreenMovPos &&
12261 (player->index_nr == game.centered_player_nr ||
12262 game.centered_player_nr == -1))
12264 if (moved & MP_MOVING && !ScreenMovPos &&
12265 (player == local_player || !options.network))
12268 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12269 int offset = game.scroll_delay_value;
12271 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12273 /* actual player has left the screen -- scroll in that direction */
12274 if (jx != old_jx) /* player has moved horizontally */
12275 scroll_x += (jx - old_jx);
12276 else /* player has moved vertically */
12277 scroll_y += (jy - old_jy);
12281 if (jx != old_jx) /* player has moved horizontally */
12283 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12284 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12285 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12287 /* don't scroll over playfield boundaries */
12288 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12289 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12291 /* don't scroll more than one field at a time */
12292 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12294 /* don't scroll against the player's moving direction */
12295 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12296 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12297 scroll_x = old_scroll_x;
12299 else /* player has moved vertically */
12301 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12302 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12303 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12305 /* don't scroll over playfield boundaries */
12306 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12307 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12309 /* don't scroll more than one field at a time */
12310 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12312 /* don't scroll against the player's moving direction */
12313 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12314 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12315 scroll_y = old_scroll_y;
12319 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12322 if (!options.network && game.centered_player_nr == -1 &&
12323 !AllPlayersInVisibleScreen())
12325 scroll_x = old_scroll_x;
12326 scroll_y = old_scroll_y;
12330 if (!options.network && !AllPlayersInVisibleScreen())
12332 scroll_x = old_scroll_x;
12333 scroll_y = old_scroll_y;
12338 ScrollScreen(player, SCROLL_INIT);
12339 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12344 player->StepFrame = 0;
12346 if (moved & MP_MOVING)
12348 if (old_jx != jx && old_jy == jy)
12349 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12350 else if (old_jx == jx && old_jy != jy)
12351 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12353 DrawLevelField(jx, jy); /* for "crumbled sand" */
12355 player->last_move_dir = player->MovDir;
12356 player->is_moving = TRUE;
12357 player->is_snapping = FALSE;
12358 player->is_switching = FALSE;
12359 player->is_dropping = FALSE;
12360 player->is_dropping_pressed = FALSE;
12361 player->drop_pressed_delay = 0;
12364 /* should better be called here than above, but this breaks some tapes */
12365 ScrollPlayer(player, SCROLL_INIT);
12370 CheckGravityMovementWhenNotMoving(player);
12372 player->is_moving = FALSE;
12374 /* at this point, the player is allowed to move, but cannot move right now
12375 (e.g. because of something blocking the way) -- ensure that the player
12376 is also allowed to move in the next frame (in old versions before 3.1.1,
12377 the player was forced to wait again for eight frames before next try) */
12379 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12380 player->move_delay = 0; /* allow direct movement in the next frame */
12383 if (player->move_delay == -1) /* not yet initialized by DigField() */
12384 player->move_delay = player->move_delay_value;
12386 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12388 TestIfPlayerTouchesBadThing(jx, jy);
12389 TestIfPlayerTouchesCustomElement(jx, jy);
12392 if (!player->active)
12393 RemovePlayer(player);
12398 void ScrollPlayer(struct PlayerInfo *player, int mode)
12400 int jx = player->jx, jy = player->jy;
12401 int last_jx = player->last_jx, last_jy = player->last_jy;
12402 int move_stepsize = TILEX / player->move_delay_value;
12404 #if USE_NEW_PLAYER_SPEED
12405 if (!player->active)
12408 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12411 if (!player->active || player->MovPos == 0)
12415 if (mode == SCROLL_INIT)
12417 player->actual_frame_counter = FrameCounter;
12418 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12420 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12421 Feld[last_jx][last_jy] == EL_EMPTY)
12423 int last_field_block_delay = 0; /* start with no blocking at all */
12424 int block_delay_adjustment = player->block_delay_adjustment;
12426 /* if player blocks last field, add delay for exactly one move */
12427 if (player->block_last_field)
12429 last_field_block_delay += player->move_delay_value;
12431 /* when blocking enabled, prevent moving up despite gravity */
12432 #if USE_PLAYER_GRAVITY
12433 if (player->gravity && player->MovDir == MV_UP)
12434 block_delay_adjustment = -1;
12436 if (game.gravity && player->MovDir == MV_UP)
12437 block_delay_adjustment = -1;
12441 /* add block delay adjustment (also possible when not blocking) */
12442 last_field_block_delay += block_delay_adjustment;
12444 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12445 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12448 #if USE_NEW_PLAYER_SPEED
12449 if (player->MovPos != 0) /* player has not yet reached destination */
12455 else if (!FrameReached(&player->actual_frame_counter, 1))
12458 #if USE_NEW_PLAYER_SPEED
12459 if (player->MovPos != 0)
12461 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12462 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12464 /* before DrawPlayer() to draw correct player graphic for this case */
12465 if (player->MovPos == 0)
12466 CheckGravityMovement(player);
12469 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12470 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12472 /* before DrawPlayer() to draw correct player graphic for this case */
12473 if (player->MovPos == 0)
12474 CheckGravityMovement(player);
12477 if (player->MovPos == 0) /* player reached destination field */
12479 if (player->move_delay_reset_counter > 0)
12481 player->move_delay_reset_counter--;
12483 if (player->move_delay_reset_counter == 0)
12485 /* continue with normal speed after quickly moving through gate */
12486 HALVE_PLAYER_SPEED(player);
12488 /* be able to make the next move without delay */
12489 player->move_delay = 0;
12493 player->last_jx = jx;
12494 player->last_jy = jy;
12496 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12497 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12498 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12499 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12500 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12501 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12503 DrawPlayer(player); /* needed here only to cleanup last field */
12504 RemovePlayer(player);
12506 if (local_player->friends_still_needed == 0 ||
12507 IS_SP_ELEMENT(Feld[jx][jy]))
12508 PlayerWins(player);
12511 /* this breaks one level: "machine", level 000 */
12513 int move_direction = player->MovDir;
12514 int enter_side = MV_DIR_OPPOSITE(move_direction);
12515 int leave_side = move_direction;
12516 int old_jx = last_jx;
12517 int old_jy = last_jy;
12518 int old_element = Feld[old_jx][old_jy];
12519 int new_element = Feld[jx][jy];
12521 if (IS_CUSTOM_ELEMENT(old_element))
12522 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12524 player->index_bit, leave_side);
12526 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12527 CE_PLAYER_LEAVES_X,
12528 player->index_bit, leave_side);
12530 if (IS_CUSTOM_ELEMENT(new_element))
12531 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12532 player->index_bit, enter_side);
12534 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12535 CE_PLAYER_ENTERS_X,
12536 player->index_bit, enter_side);
12538 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12539 CE_MOVE_OF_X, move_direction);
12542 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12544 TestIfPlayerTouchesBadThing(jx, jy);
12545 TestIfPlayerTouchesCustomElement(jx, jy);
12547 /* needed because pushed element has not yet reached its destination,
12548 so it would trigger a change event at its previous field location */
12549 if (!player->is_pushing)
12550 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12552 if (!player->active)
12553 RemovePlayer(player);
12556 if (!local_player->LevelSolved && level.use_step_counter)
12566 if (TimeLeft <= 10 && setup.time_limit)
12567 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12570 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12572 DisplayGameControlValues();
12574 DrawGameValue_Time(TimeLeft);
12577 if (!TimeLeft && setup.time_limit)
12578 for (i = 0; i < MAX_PLAYERS; i++)
12579 KillPlayer(&stored_player[i]);
12582 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12584 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12586 DisplayGameControlValues();
12589 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12590 DrawGameValue_Time(TimePlayed);
12594 if (tape.single_step && tape.recording && !tape.pausing &&
12595 !player->programmed_action)
12596 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12600 void ScrollScreen(struct PlayerInfo *player, int mode)
12602 static unsigned long screen_frame_counter = 0;
12604 if (mode == SCROLL_INIT)
12606 /* set scrolling step size according to actual player's moving speed */
12607 ScrollStepSize = TILEX / player->move_delay_value;
12609 screen_frame_counter = FrameCounter;
12610 ScreenMovDir = player->MovDir;
12611 ScreenMovPos = player->MovPos;
12612 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12615 else if (!FrameReached(&screen_frame_counter, 1))
12620 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12621 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12622 redraw_mask |= REDRAW_FIELD;
12625 ScreenMovDir = MV_NONE;
12628 void TestIfPlayerTouchesCustomElement(int x, int y)
12630 static int xy[4][2] =
12637 static int trigger_sides[4][2] =
12639 /* center side border side */
12640 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12641 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12642 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12643 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12645 static int touch_dir[4] =
12647 MV_LEFT | MV_RIGHT,
12652 int center_element = Feld[x][y]; /* should always be non-moving! */
12655 for (i = 0; i < NUM_DIRECTIONS; i++)
12657 int xx = x + xy[i][0];
12658 int yy = y + xy[i][1];
12659 int center_side = trigger_sides[i][0];
12660 int border_side = trigger_sides[i][1];
12661 int border_element;
12663 if (!IN_LEV_FIELD(xx, yy))
12666 if (IS_PLAYER(x, y))
12668 struct PlayerInfo *player = PLAYERINFO(x, y);
12670 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12671 border_element = Feld[xx][yy]; /* may be moving! */
12672 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12673 border_element = Feld[xx][yy];
12674 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12675 border_element = MovingOrBlocked2Element(xx, yy);
12677 continue; /* center and border element do not touch */
12679 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12680 player->index_bit, border_side);
12681 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12682 CE_PLAYER_TOUCHES_X,
12683 player->index_bit, border_side);
12685 else if (IS_PLAYER(xx, yy))
12687 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12689 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12691 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12692 continue; /* center and border element do not touch */
12695 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12696 player->index_bit, center_side);
12697 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12698 CE_PLAYER_TOUCHES_X,
12699 player->index_bit, center_side);
12705 #if USE_ELEMENT_TOUCHING_BUGFIX
12707 void TestIfElementTouchesCustomElement(int x, int y)
12709 static int xy[4][2] =
12716 static int trigger_sides[4][2] =
12718 /* center side border side */
12719 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12720 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12721 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12722 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12724 static int touch_dir[4] =
12726 MV_LEFT | MV_RIGHT,
12731 boolean change_center_element = FALSE;
12732 int center_element = Feld[x][y]; /* should always be non-moving! */
12733 int border_element_old[NUM_DIRECTIONS];
12736 for (i = 0; i < NUM_DIRECTIONS; i++)
12738 int xx = x + xy[i][0];
12739 int yy = y + xy[i][1];
12740 int border_element;
12742 border_element_old[i] = -1;
12744 if (!IN_LEV_FIELD(xx, yy))
12747 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12748 border_element = Feld[xx][yy]; /* may be moving! */
12749 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12750 border_element = Feld[xx][yy];
12751 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12752 border_element = MovingOrBlocked2Element(xx, yy);
12754 continue; /* center and border element do not touch */
12756 border_element_old[i] = border_element;
12759 for (i = 0; i < NUM_DIRECTIONS; i++)
12761 int xx = x + xy[i][0];
12762 int yy = y + xy[i][1];
12763 int center_side = trigger_sides[i][0];
12764 int border_element = border_element_old[i];
12766 if (border_element == -1)
12769 /* check for change of border element */
12770 CheckElementChangeBySide(xx, yy, border_element, center_element,
12771 CE_TOUCHING_X, center_side);
12774 for (i = 0; i < NUM_DIRECTIONS; i++)
12776 int border_side = trigger_sides[i][1];
12777 int border_element = border_element_old[i];
12779 if (border_element == -1)
12782 /* check for change of center element (but change it only once) */
12783 if (!change_center_element)
12784 change_center_element =
12785 CheckElementChangeBySide(x, y, center_element, border_element,
12786 CE_TOUCHING_X, border_side);
12792 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12794 static int xy[4][2] =
12801 static int trigger_sides[4][2] =
12803 /* center side border side */
12804 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12805 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12806 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12807 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12809 static int touch_dir[4] =
12811 MV_LEFT | MV_RIGHT,
12816 boolean change_center_element = FALSE;
12817 int center_element = Feld[x][y]; /* should always be non-moving! */
12820 for (i = 0; i < NUM_DIRECTIONS; i++)
12822 int xx = x + xy[i][0];
12823 int yy = y + xy[i][1];
12824 int center_side = trigger_sides[i][0];
12825 int border_side = trigger_sides[i][1];
12826 int border_element;
12828 if (!IN_LEV_FIELD(xx, yy))
12831 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12832 border_element = Feld[xx][yy]; /* may be moving! */
12833 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12834 border_element = Feld[xx][yy];
12835 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12836 border_element = MovingOrBlocked2Element(xx, yy);
12838 continue; /* center and border element do not touch */
12840 /* check for change of center element (but change it only once) */
12841 if (!change_center_element)
12842 change_center_element =
12843 CheckElementChangeBySide(x, y, center_element, border_element,
12844 CE_TOUCHING_X, border_side);
12846 /* check for change of border element */
12847 CheckElementChangeBySide(xx, yy, border_element, center_element,
12848 CE_TOUCHING_X, center_side);
12854 void TestIfElementHitsCustomElement(int x, int y, int direction)
12856 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12857 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12858 int hitx = x + dx, hity = y + dy;
12859 int hitting_element = Feld[x][y];
12860 int touched_element;
12862 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12865 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12866 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12868 if (IN_LEV_FIELD(hitx, hity))
12870 int opposite_direction = MV_DIR_OPPOSITE(direction);
12871 int hitting_side = direction;
12872 int touched_side = opposite_direction;
12873 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12874 MovDir[hitx][hity] != direction ||
12875 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12881 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12882 CE_HITTING_X, touched_side);
12884 CheckElementChangeBySide(hitx, hity, touched_element,
12885 hitting_element, CE_HIT_BY_X, hitting_side);
12887 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12888 CE_HIT_BY_SOMETHING, opposite_direction);
12892 /* "hitting something" is also true when hitting the playfield border */
12893 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12894 CE_HITTING_SOMETHING, direction);
12898 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12900 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12901 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12902 int hitx = x + dx, hity = y + dy;
12903 int hitting_element = Feld[x][y];
12904 int touched_element;
12906 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12907 !IS_FREE(hitx, hity) &&
12908 (!IS_MOVING(hitx, hity) ||
12909 MovDir[hitx][hity] != direction ||
12910 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12913 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12917 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12921 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12922 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12924 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12925 EP_CAN_SMASH_EVERYTHING, direction);
12927 if (IN_LEV_FIELD(hitx, hity))
12929 int opposite_direction = MV_DIR_OPPOSITE(direction);
12930 int hitting_side = direction;
12931 int touched_side = opposite_direction;
12933 int touched_element = MovingOrBlocked2Element(hitx, hity);
12936 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12937 MovDir[hitx][hity] != direction ||
12938 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12947 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12948 CE_SMASHED_BY_SOMETHING, opposite_direction);
12950 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12951 CE_OTHER_IS_SMASHING, touched_side);
12953 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12954 CE_OTHER_GETS_SMASHED, hitting_side);
12960 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12962 int i, kill_x = -1, kill_y = -1;
12964 int bad_element = -1;
12965 static int test_xy[4][2] =
12972 static int test_dir[4] =
12980 for (i = 0; i < NUM_DIRECTIONS; i++)
12982 int test_x, test_y, test_move_dir, test_element;
12984 test_x = good_x + test_xy[i][0];
12985 test_y = good_y + test_xy[i][1];
12987 if (!IN_LEV_FIELD(test_x, test_y))
12991 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12993 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12995 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12996 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12998 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12999 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13003 bad_element = test_element;
13009 if (kill_x != -1 || kill_y != -1)
13011 if (IS_PLAYER(good_x, good_y))
13013 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13015 if (player->shield_deadly_time_left > 0 &&
13016 !IS_INDESTRUCTIBLE(bad_element))
13017 Bang(kill_x, kill_y);
13018 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13019 KillPlayer(player);
13022 Bang(good_x, good_y);
13026 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13028 int i, kill_x = -1, kill_y = -1;
13029 int bad_element = Feld[bad_x][bad_y];
13030 static int test_xy[4][2] =
13037 static int touch_dir[4] =
13039 MV_LEFT | MV_RIGHT,
13044 static int test_dir[4] =
13052 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13055 for (i = 0; i < NUM_DIRECTIONS; i++)
13057 int test_x, test_y, test_move_dir, test_element;
13059 test_x = bad_x + test_xy[i][0];
13060 test_y = bad_y + test_xy[i][1];
13061 if (!IN_LEV_FIELD(test_x, test_y))
13065 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13067 test_element = Feld[test_x][test_y];
13069 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13070 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13072 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13073 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13075 /* good thing is player or penguin that does not move away */
13076 if (IS_PLAYER(test_x, test_y))
13078 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13080 if (bad_element == EL_ROBOT && player->is_moving)
13081 continue; /* robot does not kill player if he is moving */
13083 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13085 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13086 continue; /* center and border element do not touch */
13093 else if (test_element == EL_PENGUIN)
13102 if (kill_x != -1 || kill_y != -1)
13104 if (IS_PLAYER(kill_x, kill_y))
13106 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13108 if (player->shield_deadly_time_left > 0 &&
13109 !IS_INDESTRUCTIBLE(bad_element))
13110 Bang(bad_x, bad_y);
13111 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13112 KillPlayer(player);
13115 Bang(kill_x, kill_y);
13119 void TestIfPlayerTouchesBadThing(int x, int y)
13121 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13124 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13126 TestIfGoodThingHitsBadThing(x, y, move_dir);
13129 void TestIfBadThingTouchesPlayer(int x, int y)
13131 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13134 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13136 TestIfBadThingHitsGoodThing(x, y, move_dir);
13139 void TestIfFriendTouchesBadThing(int x, int y)
13141 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13144 void TestIfBadThingTouchesFriend(int x, int y)
13146 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13149 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13151 int i, kill_x = bad_x, kill_y = bad_y;
13152 static int xy[4][2] =
13160 for (i = 0; i < NUM_DIRECTIONS; i++)
13164 x = bad_x + xy[i][0];
13165 y = bad_y + xy[i][1];
13166 if (!IN_LEV_FIELD(x, y))
13169 element = Feld[x][y];
13170 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13171 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13179 if (kill_x != bad_x || kill_y != bad_y)
13180 Bang(bad_x, bad_y);
13183 void KillPlayer(struct PlayerInfo *player)
13185 int jx = player->jx, jy = player->jy;
13187 if (!player->active)
13190 /* the following code was introduced to prevent an infinite loop when calling
13192 -> CheckTriggeredElementChangeExt()
13193 -> ExecuteCustomElementAction()
13195 -> (infinitely repeating the above sequence of function calls)
13196 which occurs when killing the player while having a CE with the setting
13197 "kill player X when explosion of <player X>"; the solution using a new
13198 field "player->killed" was chosen for backwards compatibility, although
13199 clever use of the fields "player->active" etc. would probably also work */
13201 if (player->killed)
13205 player->killed = TRUE;
13207 /* remove accessible field at the player's position */
13208 Feld[jx][jy] = EL_EMPTY;
13210 /* deactivate shield (else Bang()/Explode() would not work right) */
13211 player->shield_normal_time_left = 0;
13212 player->shield_deadly_time_left = 0;
13215 BuryPlayer(player);
13218 static void KillPlayerUnlessEnemyProtected(int x, int y)
13220 if (!PLAYER_ENEMY_PROTECTED(x, y))
13221 KillPlayer(PLAYERINFO(x, y));
13224 static void KillPlayerUnlessExplosionProtected(int x, int y)
13226 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13227 KillPlayer(PLAYERINFO(x, y));
13230 void BuryPlayer(struct PlayerInfo *player)
13232 int jx = player->jx, jy = player->jy;
13234 if (!player->active)
13237 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13238 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13240 player->GameOver = TRUE;
13241 RemovePlayer(player);
13244 void RemovePlayer(struct PlayerInfo *player)
13246 int jx = player->jx, jy = player->jy;
13247 int i, found = FALSE;
13249 player->present = FALSE;
13250 player->active = FALSE;
13252 if (!ExplodeField[jx][jy])
13253 StorePlayer[jx][jy] = 0;
13255 if (player->is_moving)
13256 DrawLevelField(player->last_jx, player->last_jy);
13258 for (i = 0; i < MAX_PLAYERS; i++)
13259 if (stored_player[i].active)
13263 AllPlayersGone = TRUE;
13269 #if USE_NEW_SNAP_DELAY
13270 static void setFieldForSnapping(int x, int y, int element, int direction)
13272 struct ElementInfo *ei = &element_info[element];
13273 int direction_bit = MV_DIR_TO_BIT(direction);
13274 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13275 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13276 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13278 Feld[x][y] = EL_ELEMENT_SNAPPING;
13279 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13281 ResetGfxAnimation(x, y);
13283 GfxElement[x][y] = element;
13284 GfxAction[x][y] = action;
13285 GfxDir[x][y] = direction;
13286 GfxFrame[x][y] = -1;
13291 =============================================================================
13292 checkDiagonalPushing()
13293 -----------------------------------------------------------------------------
13294 check if diagonal input device direction results in pushing of object
13295 (by checking if the alternative direction is walkable, diggable, ...)
13296 =============================================================================
13299 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13300 int x, int y, int real_dx, int real_dy)
13302 int jx, jy, dx, dy, xx, yy;
13304 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13307 /* diagonal direction: check alternative direction */
13312 xx = jx + (dx == 0 ? real_dx : 0);
13313 yy = jy + (dy == 0 ? real_dy : 0);
13315 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13319 =============================================================================
13321 -----------------------------------------------------------------------------
13322 x, y: field next to player (non-diagonal) to try to dig to
13323 real_dx, real_dy: direction as read from input device (can be diagonal)
13324 =============================================================================
13327 int DigField(struct PlayerInfo *player,
13328 int oldx, int oldy, int x, int y,
13329 int real_dx, int real_dy, int mode)
13331 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13332 boolean player_was_pushing = player->is_pushing;
13333 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13334 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13335 int jx = oldx, jy = oldy;
13336 int dx = x - jx, dy = y - jy;
13337 int nextx = x + dx, nexty = y + dy;
13338 int move_direction = (dx == -1 ? MV_LEFT :
13339 dx == +1 ? MV_RIGHT :
13341 dy == +1 ? MV_DOWN : MV_NONE);
13342 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13343 int dig_side = MV_DIR_OPPOSITE(move_direction);
13344 int old_element = Feld[jx][jy];
13345 #if USE_FIXED_DONT_RUN_INTO
13346 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13352 if (is_player) /* function can also be called by EL_PENGUIN */
13354 if (player->MovPos == 0)
13356 player->is_digging = FALSE;
13357 player->is_collecting = FALSE;
13360 if (player->MovPos == 0) /* last pushing move finished */
13361 player->is_pushing = FALSE;
13363 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13365 player->is_switching = FALSE;
13366 player->push_delay = -1;
13368 return MP_NO_ACTION;
13372 #if !USE_FIXED_DONT_RUN_INTO
13373 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13374 return MP_NO_ACTION;
13377 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13378 old_element = Back[jx][jy];
13380 /* in case of element dropped at player position, check background */
13381 else if (Back[jx][jy] != EL_EMPTY &&
13382 game.engine_version >= VERSION_IDENT(2,2,0,0))
13383 old_element = Back[jx][jy];
13385 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13386 return MP_NO_ACTION; /* field has no opening in this direction */
13388 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13389 return MP_NO_ACTION; /* field has no opening in this direction */
13391 #if USE_FIXED_DONT_RUN_INTO
13392 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13396 Feld[jx][jy] = player->artwork_element;
13397 InitMovingField(jx, jy, MV_DOWN);
13398 Store[jx][jy] = EL_ACID;
13399 ContinueMoving(jx, jy);
13400 BuryPlayer(player);
13402 return MP_DONT_RUN_INTO;
13406 #if USE_FIXED_DONT_RUN_INTO
13407 if (player_can_move && DONT_RUN_INTO(element))
13409 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13411 return MP_DONT_RUN_INTO;
13415 #if USE_FIXED_DONT_RUN_INTO
13416 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13417 return MP_NO_ACTION;
13420 #if !USE_FIXED_DONT_RUN_INTO
13421 element = Feld[x][y];
13424 collect_count = element_info[element].collect_count_initial;
13426 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13427 return MP_NO_ACTION;
13429 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13430 player_can_move = player_can_move_or_snap;
13432 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13433 game.engine_version >= VERSION_IDENT(2,2,0,0))
13435 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13436 player->index_bit, dig_side);
13437 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13438 player->index_bit, dig_side);
13440 if (element == EL_DC_LANDMINE)
13443 if (Feld[x][y] != element) /* field changed by snapping */
13446 return MP_NO_ACTION;
13449 #if USE_PLAYER_GRAVITY
13450 if (player->gravity && is_player && !player->is_auto_moving &&
13451 canFallDown(player) && move_direction != MV_DOWN &&
13452 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13453 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13455 if (game.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 */
13461 if (player_can_move &&
13462 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13464 int sound_element = SND_ELEMENT(element);
13465 int sound_action = ACTION_WALKING;
13467 if (IS_RND_GATE(element))
13469 if (!player->key[RND_GATE_NR(element)])
13470 return MP_NO_ACTION;
13472 else if (IS_RND_GATE_GRAY(element))
13474 if (!player->key[RND_GATE_GRAY_NR(element)])
13475 return MP_NO_ACTION;
13477 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13479 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13480 return MP_NO_ACTION;
13482 else if (element == EL_EXIT_OPEN ||
13483 element == EL_EM_EXIT_OPEN ||
13484 element == EL_STEEL_EXIT_OPEN ||
13485 element == EL_EM_STEEL_EXIT_OPEN ||
13486 element == EL_SP_EXIT_OPEN ||
13487 element == EL_SP_EXIT_OPENING)
13489 sound_action = ACTION_PASSING; /* player is passing exit */
13491 else if (element == EL_EMPTY)
13493 sound_action = ACTION_MOVING; /* nothing to walk on */
13496 /* play sound from background or player, whatever is available */
13497 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13498 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13500 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13502 else if (player_can_move &&
13503 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13505 if (!ACCESS_FROM(element, opposite_direction))
13506 return MP_NO_ACTION; /* field not accessible from this direction */
13508 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13509 return MP_NO_ACTION;
13511 if (IS_EM_GATE(element))
13513 if (!player->key[EM_GATE_NR(element)])
13514 return MP_NO_ACTION;
13516 else if (IS_EM_GATE_GRAY(element))
13518 if (!player->key[EM_GATE_GRAY_NR(element)])
13519 return MP_NO_ACTION;
13521 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13523 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13524 return MP_NO_ACTION;
13526 else if (IS_EMC_GATE(element))
13528 if (!player->key[EMC_GATE_NR(element)])
13529 return MP_NO_ACTION;
13531 else if (IS_EMC_GATE_GRAY(element))
13533 if (!player->key[EMC_GATE_GRAY_NR(element)])
13534 return MP_NO_ACTION;
13536 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13538 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13539 return MP_NO_ACTION;
13541 else if (element == EL_DC_GATE_WHITE ||
13542 element == EL_DC_GATE_WHITE_GRAY ||
13543 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13545 if (player->num_white_keys == 0)
13546 return MP_NO_ACTION;
13548 player->num_white_keys--;
13550 else if (IS_SP_PORT(element))
13552 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13553 element == EL_SP_GRAVITY_PORT_RIGHT ||
13554 element == EL_SP_GRAVITY_PORT_UP ||
13555 element == EL_SP_GRAVITY_PORT_DOWN)
13556 #if USE_PLAYER_GRAVITY
13557 player->gravity = !player->gravity;
13559 game.gravity = !game.gravity;
13561 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13562 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13563 element == EL_SP_GRAVITY_ON_PORT_UP ||
13564 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13565 #if USE_PLAYER_GRAVITY
13566 player->gravity = TRUE;
13568 game.gravity = TRUE;
13570 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13571 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13572 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13573 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13574 #if USE_PLAYER_GRAVITY
13575 player->gravity = FALSE;
13577 game.gravity = FALSE;
13581 /* automatically move to the next field with double speed */
13582 player->programmed_action = move_direction;
13584 if (player->move_delay_reset_counter == 0)
13586 player->move_delay_reset_counter = 2; /* two double speed steps */
13588 DOUBLE_PLAYER_SPEED(player);
13591 PlayLevelSoundAction(x, y, ACTION_PASSING);
13593 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13597 if (mode != DF_SNAP)
13599 GfxElement[x][y] = GFX_ELEMENT(element);
13600 player->is_digging = TRUE;
13603 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13605 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13606 player->index_bit, dig_side);
13608 if (mode == DF_SNAP)
13610 #if USE_NEW_SNAP_DELAY
13611 if (level.block_snap_field)
13612 setFieldForSnapping(x, y, element, move_direction);
13614 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13616 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13619 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13620 player->index_bit, dig_side);
13623 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13627 if (is_player && mode != DF_SNAP)
13629 GfxElement[x][y] = element;
13630 player->is_collecting = TRUE;
13633 if (element == EL_SPEED_PILL)
13635 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13637 else if (element == EL_EXTRA_TIME && level.time > 0)
13639 TimeLeft += level.extra_time;
13642 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13644 DisplayGameControlValues();
13646 DrawGameValue_Time(TimeLeft);
13649 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13651 player->shield_normal_time_left += level.shield_normal_time;
13652 if (element == EL_SHIELD_DEADLY)
13653 player->shield_deadly_time_left += level.shield_deadly_time;
13655 else if (element == EL_DYNAMITE ||
13656 element == EL_EM_DYNAMITE ||
13657 element == EL_SP_DISK_RED)
13659 if (player->inventory_size < MAX_INVENTORY_SIZE)
13660 player->inventory_element[player->inventory_size++] = element;
13662 DrawGameDoorValues();
13664 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13666 player->dynabomb_count++;
13667 player->dynabombs_left++;
13669 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13671 player->dynabomb_size++;
13673 else if (element == EL_DYNABOMB_INCREASE_POWER)
13675 player->dynabomb_xl = TRUE;
13677 else if (IS_KEY(element))
13679 player->key[KEY_NR(element)] = TRUE;
13681 DrawGameDoorValues();
13683 else if (element == EL_DC_KEY_WHITE)
13685 player->num_white_keys++;
13687 /* display white keys? */
13688 /* DrawGameDoorValues(); */
13690 else if (IS_ENVELOPE(element))
13692 player->show_envelope = element;
13694 else if (element == EL_EMC_LENSES)
13696 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13698 RedrawAllInvisibleElementsForLenses();
13700 else if (element == EL_EMC_MAGNIFIER)
13702 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13704 RedrawAllInvisibleElementsForMagnifier();
13706 else if (IS_DROPPABLE(element) ||
13707 IS_THROWABLE(element)) /* can be collected and dropped */
13711 if (collect_count == 0)
13712 player->inventory_infinite_element = element;
13714 for (i = 0; i < collect_count; i++)
13715 if (player->inventory_size < MAX_INVENTORY_SIZE)
13716 player->inventory_element[player->inventory_size++] = element;
13718 DrawGameDoorValues();
13720 else if (collect_count > 0)
13722 local_player->gems_still_needed -= collect_count;
13723 if (local_player->gems_still_needed < 0)
13724 local_player->gems_still_needed = 0;
13727 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13729 DisplayGameControlValues();
13731 DrawGameValue_Emeralds(local_player->gems_still_needed);
13735 RaiseScoreElement(element);
13736 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13739 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13740 player->index_bit, dig_side);
13742 if (mode == DF_SNAP)
13744 #if USE_NEW_SNAP_DELAY
13745 if (level.block_snap_field)
13746 setFieldForSnapping(x, y, element, move_direction);
13748 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13750 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13753 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13754 player->index_bit, dig_side);
13757 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13759 if (mode == DF_SNAP && element != EL_BD_ROCK)
13760 return MP_NO_ACTION;
13762 if (CAN_FALL(element) && dy)
13763 return MP_NO_ACTION;
13765 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13766 !(element == EL_SPRING && level.use_spring_bug))
13767 return MP_NO_ACTION;
13769 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13770 ((move_direction & MV_VERTICAL &&
13771 ((element_info[element].move_pattern & MV_LEFT &&
13772 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13773 (element_info[element].move_pattern & MV_RIGHT &&
13774 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13775 (move_direction & MV_HORIZONTAL &&
13776 ((element_info[element].move_pattern & MV_UP &&
13777 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13778 (element_info[element].move_pattern & MV_DOWN &&
13779 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13780 return MP_NO_ACTION;
13782 /* do not push elements already moving away faster than player */
13783 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13784 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13785 return MP_NO_ACTION;
13787 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13789 if (player->push_delay_value == -1 || !player_was_pushing)
13790 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13792 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13794 if (player->push_delay_value == -1)
13795 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13797 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13799 if (!player->is_pushing)
13800 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13803 player->is_pushing = TRUE;
13804 player->is_active = TRUE;
13806 if (!(IN_LEV_FIELD(nextx, nexty) &&
13807 (IS_FREE(nextx, nexty) ||
13808 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13809 IS_SB_ELEMENT(element)))))
13810 return MP_NO_ACTION;
13812 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13813 return MP_NO_ACTION;
13815 if (player->push_delay == -1) /* new pushing; restart delay */
13816 player->push_delay = 0;
13818 if (player->push_delay < player->push_delay_value &&
13819 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13820 element != EL_SPRING && element != EL_BALLOON)
13822 /* make sure that there is no move delay before next try to push */
13823 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13824 player->move_delay = 0;
13826 return MP_NO_ACTION;
13829 if (IS_SB_ELEMENT(element))
13831 if (element == EL_SOKOBAN_FIELD_FULL)
13833 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13834 local_player->sokobanfields_still_needed++;
13837 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13839 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13840 local_player->sokobanfields_still_needed--;
13843 Feld[x][y] = EL_SOKOBAN_OBJECT;
13845 if (Back[x][y] == Back[nextx][nexty])
13846 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13847 else if (Back[x][y] != 0)
13848 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13851 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13854 if (local_player->sokobanfields_still_needed == 0 &&
13855 game.emulation == EMU_SOKOBAN)
13857 PlayerWins(player);
13859 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13863 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13865 InitMovingField(x, y, move_direction);
13866 GfxAction[x][y] = ACTION_PUSHING;
13868 if (mode == DF_SNAP)
13869 ContinueMoving(x, y);
13871 MovPos[x][y] = (dx != 0 ? dx : dy);
13873 Pushed[x][y] = TRUE;
13874 Pushed[nextx][nexty] = TRUE;
13876 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13877 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13879 player->push_delay_value = -1; /* get new value later */
13881 /* check for element change _after_ element has been pushed */
13882 if (game.use_change_when_pushing_bug)
13884 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13885 player->index_bit, dig_side);
13886 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13887 player->index_bit, dig_side);
13890 else if (IS_SWITCHABLE(element))
13892 if (PLAYER_SWITCHING(player, x, y))
13894 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13895 player->index_bit, dig_side);
13900 player->is_switching = TRUE;
13901 player->switch_x = x;
13902 player->switch_y = y;
13904 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13906 if (element == EL_ROBOT_WHEEL)
13908 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13912 DrawLevelField(x, y);
13914 else if (element == EL_SP_TERMINAL)
13918 SCAN_PLAYFIELD(xx, yy)
13920 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13922 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13923 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13926 else if (IS_BELT_SWITCH(element))
13928 ToggleBeltSwitch(x, y);
13930 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13931 element == EL_SWITCHGATE_SWITCH_DOWN ||
13932 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13933 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13935 ToggleSwitchgateSwitch(x, y);
13937 else if (element == EL_LIGHT_SWITCH ||
13938 element == EL_LIGHT_SWITCH_ACTIVE)
13940 ToggleLightSwitch(x, y);
13942 else if (element == EL_TIMEGATE_SWITCH ||
13943 element == EL_DC_TIMEGATE_SWITCH)
13945 ActivateTimegateSwitch(x, y);
13947 else if (element == EL_BALLOON_SWITCH_LEFT ||
13948 element == EL_BALLOON_SWITCH_RIGHT ||
13949 element == EL_BALLOON_SWITCH_UP ||
13950 element == EL_BALLOON_SWITCH_DOWN ||
13951 element == EL_BALLOON_SWITCH_NONE ||
13952 element == EL_BALLOON_SWITCH_ANY)
13954 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13955 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13956 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13957 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13958 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13961 else if (element == EL_LAMP)
13963 Feld[x][y] = EL_LAMP_ACTIVE;
13964 local_player->lights_still_needed--;
13966 ResetGfxAnimation(x, y);
13967 DrawLevelField(x, y);
13969 else if (element == EL_TIME_ORB_FULL)
13971 Feld[x][y] = EL_TIME_ORB_EMPTY;
13973 if (level.time > 0 || level.use_time_orb_bug)
13975 TimeLeft += level.time_orb_time;
13978 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13980 DisplayGameControlValues();
13982 DrawGameValue_Time(TimeLeft);
13986 ResetGfxAnimation(x, y);
13987 DrawLevelField(x, y);
13989 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13990 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13994 game.ball_state = !game.ball_state;
13996 SCAN_PLAYFIELD(xx, yy)
13998 int e = Feld[xx][yy];
14000 if (game.ball_state)
14002 if (e == EL_EMC_MAGIC_BALL)
14003 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14004 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14005 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14009 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14010 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14011 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14012 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14017 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14018 player->index_bit, dig_side);
14020 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14021 player->index_bit, dig_side);
14023 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14024 player->index_bit, dig_side);
14030 if (!PLAYER_SWITCHING(player, x, y))
14032 player->is_switching = TRUE;
14033 player->switch_x = x;
14034 player->switch_y = y;
14036 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14037 player->index_bit, dig_side);
14038 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14039 player->index_bit, dig_side);
14041 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14042 player->index_bit, dig_side);
14043 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14044 player->index_bit, dig_side);
14047 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14048 player->index_bit, dig_side);
14049 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14050 player->index_bit, dig_side);
14052 return MP_NO_ACTION;
14055 player->push_delay = -1;
14057 if (is_player) /* function can also be called by EL_PENGUIN */
14059 if (Feld[x][y] != element) /* really digged/collected something */
14061 player->is_collecting = !player->is_digging;
14062 player->is_active = TRUE;
14069 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14071 int jx = player->jx, jy = player->jy;
14072 int x = jx + dx, y = jy + dy;
14073 int snap_direction = (dx == -1 ? MV_LEFT :
14074 dx == +1 ? MV_RIGHT :
14076 dy == +1 ? MV_DOWN : MV_NONE);
14077 boolean can_continue_snapping = (level.continuous_snapping &&
14078 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14080 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14083 if (!player->active || !IN_LEV_FIELD(x, y))
14091 if (player->MovPos == 0)
14092 player->is_pushing = FALSE;
14094 player->is_snapping = FALSE;
14096 if (player->MovPos == 0)
14098 player->is_moving = FALSE;
14099 player->is_digging = FALSE;
14100 player->is_collecting = FALSE;
14106 #if USE_NEW_CONTINUOUS_SNAPPING
14107 /* prevent snapping with already pressed snap key when not allowed */
14108 if (player->is_snapping && !can_continue_snapping)
14111 if (player->is_snapping)
14115 player->MovDir = snap_direction;
14117 if (player->MovPos == 0)
14119 player->is_moving = FALSE;
14120 player->is_digging = FALSE;
14121 player->is_collecting = FALSE;
14124 player->is_dropping = FALSE;
14125 player->is_dropping_pressed = FALSE;
14126 player->drop_pressed_delay = 0;
14128 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14131 player->is_snapping = TRUE;
14132 player->is_active = TRUE;
14134 if (player->MovPos == 0)
14136 player->is_moving = FALSE;
14137 player->is_digging = FALSE;
14138 player->is_collecting = FALSE;
14141 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14142 DrawLevelField(player->last_jx, player->last_jy);
14144 DrawLevelField(x, y);
14149 boolean DropElement(struct PlayerInfo *player)
14151 int old_element, new_element;
14152 int dropx = player->jx, dropy = player->jy;
14153 int drop_direction = player->MovDir;
14154 int drop_side = drop_direction;
14156 int drop_element = get_next_drop_element(player);
14158 int drop_element = (player->inventory_size > 0 ?
14159 player->inventory_element[player->inventory_size - 1] :
14160 player->inventory_infinite_element != EL_UNDEFINED ?
14161 player->inventory_infinite_element :
14162 player->dynabombs_left > 0 ?
14163 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14167 player->is_dropping_pressed = TRUE;
14169 /* do not drop an element on top of another element; when holding drop key
14170 pressed without moving, dropped element must move away before the next
14171 element can be dropped (this is especially important if the next element
14172 is dynamite, which can be placed on background for historical reasons) */
14173 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14176 if (IS_THROWABLE(drop_element))
14178 dropx += GET_DX_FROM_DIR(drop_direction);
14179 dropy += GET_DY_FROM_DIR(drop_direction);
14181 if (!IN_LEV_FIELD(dropx, dropy))
14185 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14186 new_element = drop_element; /* default: no change when dropping */
14188 /* check if player is active, not moving and ready to drop */
14189 if (!player->active || player->MovPos || player->drop_delay > 0)
14192 /* check if player has anything that can be dropped */
14193 if (new_element == EL_UNDEFINED)
14196 /* check if drop key was pressed long enough for EM style dynamite */
14197 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14200 /* check if anything can be dropped at the current position */
14201 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14204 /* collected custom elements can only be dropped on empty fields */
14205 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14208 if (old_element != EL_EMPTY)
14209 Back[dropx][dropy] = old_element; /* store old element on this field */
14211 ResetGfxAnimation(dropx, dropy);
14212 ResetRandomAnimationValue(dropx, dropy);
14214 if (player->inventory_size > 0 ||
14215 player->inventory_infinite_element != EL_UNDEFINED)
14217 if (player->inventory_size > 0)
14219 player->inventory_size--;
14221 DrawGameDoorValues();
14223 if (new_element == EL_DYNAMITE)
14224 new_element = EL_DYNAMITE_ACTIVE;
14225 else if (new_element == EL_EM_DYNAMITE)
14226 new_element = EL_EM_DYNAMITE_ACTIVE;
14227 else if (new_element == EL_SP_DISK_RED)
14228 new_element = EL_SP_DISK_RED_ACTIVE;
14231 Feld[dropx][dropy] = new_element;
14233 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14234 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14235 el2img(Feld[dropx][dropy]), 0);
14237 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14239 /* needed if previous element just changed to "empty" in the last frame */
14240 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14242 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14243 player->index_bit, drop_side);
14244 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14246 player->index_bit, drop_side);
14248 TestIfElementTouchesCustomElement(dropx, dropy);
14250 else /* player is dropping a dyna bomb */
14252 player->dynabombs_left--;
14254 Feld[dropx][dropy] = new_element;
14256 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14257 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14258 el2img(Feld[dropx][dropy]), 0);
14260 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14263 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14264 InitField_WithBug1(dropx, dropy, FALSE);
14266 new_element = Feld[dropx][dropy]; /* element might have changed */
14268 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14269 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14271 int move_direction, nextx, nexty;
14273 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14274 MovDir[dropx][dropy] = drop_direction;
14276 move_direction = MovDir[dropx][dropy];
14277 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14278 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14280 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14282 #if USE_FIX_IMPACT_COLLISION
14283 /* do not cause impact style collision by dropping elements that can fall */
14284 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14286 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14290 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14291 player->is_dropping = TRUE;
14293 player->drop_pressed_delay = 0;
14294 player->is_dropping_pressed = FALSE;
14296 player->drop_x = dropx;
14297 player->drop_y = dropy;
14302 /* ------------------------------------------------------------------------- */
14303 /* game sound playing functions */
14304 /* ------------------------------------------------------------------------- */
14306 static int *loop_sound_frame = NULL;
14307 static int *loop_sound_volume = NULL;
14309 void InitPlayLevelSound()
14311 int num_sounds = getSoundListSize();
14313 checked_free(loop_sound_frame);
14314 checked_free(loop_sound_volume);
14316 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14317 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14320 static void PlayLevelSound(int x, int y, int nr)
14322 int sx = SCREENX(x), sy = SCREENY(y);
14323 int volume, stereo_position;
14324 int max_distance = 8;
14325 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14327 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14328 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14331 if (!IN_LEV_FIELD(x, y) ||
14332 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14333 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14336 volume = SOUND_MAX_VOLUME;
14338 if (!IN_SCR_FIELD(sx, sy))
14340 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14341 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14343 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14346 stereo_position = (SOUND_MAX_LEFT +
14347 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14348 (SCR_FIELDX + 2 * max_distance));
14350 if (IS_LOOP_SOUND(nr))
14352 /* This assures that quieter loop sounds do not overwrite louder ones,
14353 while restarting sound volume comparison with each new game frame. */
14355 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14358 loop_sound_volume[nr] = volume;
14359 loop_sound_frame[nr] = FrameCounter;
14362 PlaySoundExt(nr, volume, stereo_position, type);
14365 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14367 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14368 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14369 y < LEVELY(BY1) ? LEVELY(BY1) :
14370 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14374 static void PlayLevelSoundAction(int x, int y, int action)
14376 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14379 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14381 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14383 if (sound_effect != SND_UNDEFINED)
14384 PlayLevelSound(x, y, sound_effect);
14387 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14390 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14392 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14393 PlayLevelSound(x, y, sound_effect);
14396 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14398 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14400 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14401 PlayLevelSound(x, y, sound_effect);
14404 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14406 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14408 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14409 StopSound(sound_effect);
14412 static void PlayLevelMusic()
14414 if (levelset.music[level_nr] != MUS_UNDEFINED)
14415 PlayMusic(levelset.music[level_nr]); /* from config file */
14417 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14420 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14422 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14423 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14424 int x = xx - 1 - offset;
14425 int y = yy - 1 - offset;
14430 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14434 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14438 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14442 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14446 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14450 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14454 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14457 case SAMPLE_android_clone:
14458 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14461 case SAMPLE_android_move:
14462 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14465 case SAMPLE_spring:
14466 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14470 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14474 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14477 case SAMPLE_eater_eat:
14478 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14482 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14485 case SAMPLE_collect:
14486 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14489 case SAMPLE_diamond:
14490 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14493 case SAMPLE_squash:
14494 /* !!! CHECK THIS !!! */
14496 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14498 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14502 case SAMPLE_wonderfall:
14503 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14507 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14511 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14515 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14519 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14523 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14527 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14530 case SAMPLE_wonder:
14531 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14535 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14538 case SAMPLE_exit_open:
14539 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14542 case SAMPLE_exit_leave:
14543 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14546 case SAMPLE_dynamite:
14547 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14551 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14555 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14559 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14563 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14567 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14571 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14575 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14581 void ChangeTime(int value)
14583 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14587 /* EMC game engine uses value from time counter of RND game engine */
14588 level.native_em_level->lev->time = *time;
14590 DrawGameValue_Time(*time);
14593 void RaiseScore(int value)
14595 /* EMC game engine and RND game engine have separate score counters */
14596 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14597 &level.native_em_level->lev->score : &local_player->score);
14601 DrawGameValue_Score(*score);
14605 void RaiseScore(int value)
14607 local_player->score += value;
14610 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14612 DisplayGameControlValues();
14614 DrawGameValue_Score(local_player->score);
14618 void RaiseScoreElement(int element)
14623 case EL_BD_DIAMOND:
14624 case EL_EMERALD_YELLOW:
14625 case EL_EMERALD_RED:
14626 case EL_EMERALD_PURPLE:
14627 case EL_SP_INFOTRON:
14628 RaiseScore(level.score[SC_EMERALD]);
14631 RaiseScore(level.score[SC_DIAMOND]);
14634 RaiseScore(level.score[SC_CRYSTAL]);
14637 RaiseScore(level.score[SC_PEARL]);
14640 case EL_BD_BUTTERFLY:
14641 case EL_SP_ELECTRON:
14642 RaiseScore(level.score[SC_BUG]);
14645 case EL_BD_FIREFLY:
14646 case EL_SP_SNIKSNAK:
14647 RaiseScore(level.score[SC_SPACESHIP]);
14650 case EL_DARK_YAMYAM:
14651 RaiseScore(level.score[SC_YAMYAM]);
14654 RaiseScore(level.score[SC_ROBOT]);
14657 RaiseScore(level.score[SC_PACMAN]);
14660 RaiseScore(level.score[SC_NUT]);
14663 case EL_EM_DYNAMITE:
14664 case EL_SP_DISK_RED:
14665 case EL_DYNABOMB_INCREASE_NUMBER:
14666 case EL_DYNABOMB_INCREASE_SIZE:
14667 case EL_DYNABOMB_INCREASE_POWER:
14668 RaiseScore(level.score[SC_DYNAMITE]);
14670 case EL_SHIELD_NORMAL:
14671 case EL_SHIELD_DEADLY:
14672 RaiseScore(level.score[SC_SHIELD]);
14674 case EL_EXTRA_TIME:
14675 RaiseScore(level.extra_time_score);
14689 case EL_DC_KEY_WHITE:
14690 RaiseScore(level.score[SC_KEY]);
14693 RaiseScore(element_info[element].collect_score);
14698 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14700 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14702 #if defined(NETWORK_AVALIABLE)
14703 if (options.network)
14704 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14713 FadeSkipNextFadeIn();
14715 fading = fading_none;
14719 OpenDoor(DOOR_CLOSE_1);
14722 game_status = GAME_MODE_MAIN;
14725 DrawAndFadeInMainMenu(REDRAW_FIELD);
14733 FadeOut(REDRAW_FIELD);
14736 game_status = GAME_MODE_MAIN;
14738 DrawAndFadeInMainMenu(REDRAW_FIELD);
14742 else /* continue playing the game */
14744 if (tape.playing && tape.deactivate_display)
14745 TapeDeactivateDisplayOff(TRUE);
14747 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14749 if (tape.playing && tape.deactivate_display)
14750 TapeDeactivateDisplayOn();
14754 void RequestQuitGame(boolean ask_if_really_quit)
14756 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14757 boolean skip_request = AllPlayersGone || quick_quit;
14759 RequestQuitGameExt(skip_request, quick_quit,
14760 "Do you really want to quit the game ?");
14764 /* ------------------------------------------------------------------------- */
14765 /* random generator functions */
14766 /* ------------------------------------------------------------------------- */
14768 unsigned int InitEngineRandom_RND(long seed)
14770 game.num_random_calls = 0;
14773 unsigned int rnd_seed = InitEngineRandom(seed);
14775 printf("::: START RND: %d\n", rnd_seed);
14780 return InitEngineRandom(seed);
14786 unsigned int RND(int max)
14790 game.num_random_calls++;
14792 return GetEngineRandom(max);
14799 /* ------------------------------------------------------------------------- */
14800 /* game engine snapshot handling functions */
14801 /* ------------------------------------------------------------------------- */
14803 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14805 struct EngineSnapshotInfo
14807 /* runtime values for custom element collect score */
14808 int collect_score[NUM_CUSTOM_ELEMENTS];
14810 /* runtime values for group element choice position */
14811 int choice_pos[NUM_GROUP_ELEMENTS];
14813 /* runtime values for belt position animations */
14814 int belt_graphic[4 * NUM_BELT_PARTS];
14815 int belt_anim_mode[4 * NUM_BELT_PARTS];
14818 struct EngineSnapshotNodeInfo
14825 static struct EngineSnapshotInfo engine_snapshot_rnd;
14826 static ListNode *engine_snapshot_list = NULL;
14827 static char *snapshot_level_identifier = NULL;
14828 static int snapshot_level_nr = -1;
14830 void FreeEngineSnapshot()
14832 while (engine_snapshot_list != NULL)
14833 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14836 setString(&snapshot_level_identifier, NULL);
14837 snapshot_level_nr = -1;
14840 static void SaveEngineSnapshotValues_RND()
14842 static int belt_base_active_element[4] =
14844 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14845 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14846 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14847 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14851 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14853 int element = EL_CUSTOM_START + i;
14855 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14858 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14860 int element = EL_GROUP_START + i;
14862 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14865 for (i = 0; i < 4; i++)
14867 for (j = 0; j < NUM_BELT_PARTS; j++)
14869 int element = belt_base_active_element[i] + j;
14870 int graphic = el2img(element);
14871 int anim_mode = graphic_info[graphic].anim_mode;
14873 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14874 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14879 static void LoadEngineSnapshotValues_RND()
14881 unsigned long num_random_calls = game.num_random_calls;
14884 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14886 int element = EL_CUSTOM_START + i;
14888 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14891 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14893 int element = EL_GROUP_START + i;
14895 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14898 for (i = 0; i < 4; i++)
14900 for (j = 0; j < NUM_BELT_PARTS; j++)
14902 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14903 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14905 graphic_info[graphic].anim_mode = anim_mode;
14909 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14911 InitRND(tape.random_seed);
14912 for (i = 0; i < num_random_calls; i++)
14916 if (game.num_random_calls != num_random_calls)
14918 Error(ERR_INFO, "number of random calls out of sync");
14919 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14920 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14921 Error(ERR_EXIT, "this should not happen -- please debug");
14925 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14927 struct EngineSnapshotNodeInfo *bi =
14928 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14930 bi->buffer_orig = buffer;
14931 bi->buffer_copy = checked_malloc(size);
14934 memcpy(bi->buffer_copy, buffer, size);
14936 addNodeToList(&engine_snapshot_list, NULL, bi);
14939 void SaveEngineSnapshot()
14941 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14943 if (level_editor_test_game) /* do not save snapshots from editor */
14946 /* copy some special values to a structure better suited for the snapshot */
14948 SaveEngineSnapshotValues_RND();
14949 SaveEngineSnapshotValues_EM();
14951 /* save values stored in special snapshot structure */
14953 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14954 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14956 /* save further RND engine values */
14958 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14959 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14960 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14962 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14963 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14964 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14965 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14967 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14968 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14969 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14970 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14971 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14973 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14974 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14975 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14977 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14979 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14981 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14982 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14984 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14985 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14986 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14987 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14988 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14989 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14990 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14991 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14992 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14993 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14994 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14995 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14996 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14997 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14998 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14999 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15000 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15001 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15003 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15004 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15006 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15007 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15008 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15010 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15011 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15013 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15014 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15015 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15016 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15017 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15019 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15020 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15022 /* save level identification information */
15024 setString(&snapshot_level_identifier, leveldir_current->identifier);
15025 snapshot_level_nr = level_nr;
15028 ListNode *node = engine_snapshot_list;
15031 while (node != NULL)
15033 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15038 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15042 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15044 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15047 void LoadEngineSnapshot()
15049 ListNode *node = engine_snapshot_list;
15051 if (engine_snapshot_list == NULL)
15054 while (node != NULL)
15056 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15061 /* restore special values from snapshot structure */
15063 LoadEngineSnapshotValues_RND();
15064 LoadEngineSnapshotValues_EM();
15067 boolean CheckEngineSnapshot()
15069 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15070 snapshot_level_nr == level_nr);
15074 /* ---------- new game button stuff ---------------------------------------- */
15076 /* graphic position values for game buttons */
15077 #define GAME_BUTTON_XSIZE 30
15078 #define GAME_BUTTON_YSIZE 30
15079 #define GAME_BUTTON_XPOS 5
15080 #define GAME_BUTTON_YPOS 215
15081 #define SOUND_BUTTON_XPOS 5
15082 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15084 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15085 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15086 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15087 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15088 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15089 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15097 } gamebutton_info[NUM_GAME_BUTTONS] =
15101 &game.button.stop.x, &game.button.stop.y,
15102 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15107 &game.button.pause.x, &game.button.pause.y,
15108 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15109 GAME_CTRL_ID_PAUSE,
15113 &game.button.play.x, &game.button.play.y,
15114 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15119 &game.button.sound_music.x, &game.button.sound_music.y,
15120 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15121 SOUND_CTRL_ID_MUSIC,
15122 "background music on/off"
15125 &game.button.sound_loops.x, &game.button.sound_loops.y,
15126 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15127 SOUND_CTRL_ID_LOOPS,
15128 "sound loops on/off"
15131 &game.button.sound_simple.x,&game.button.sound_simple.y,
15132 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15133 SOUND_CTRL_ID_SIMPLE,
15134 "normal sounds on/off"
15138 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15143 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15144 GAME_CTRL_ID_PAUSE,
15148 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15153 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15154 SOUND_CTRL_ID_MUSIC,
15155 "background music on/off"
15158 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15159 SOUND_CTRL_ID_LOOPS,
15160 "sound loops on/off"
15163 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15164 SOUND_CTRL_ID_SIMPLE,
15165 "normal sounds on/off"
15170 void CreateGameButtons()
15174 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15176 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15177 struct GadgetInfo *gi;
15180 unsigned long event_mask;
15182 int gd_xoffset, gd_yoffset;
15183 int gd_x1, gd_x2, gd_y1, gd_y2;
15186 x = DX + *gamebutton_info[i].x;
15187 y = DY + *gamebutton_info[i].y;
15188 gd_xoffset = gamebutton_info[i].gd_x;
15189 gd_yoffset = gamebutton_info[i].gd_y;
15190 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15191 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15193 if (id == GAME_CTRL_ID_STOP ||
15194 id == GAME_CTRL_ID_PAUSE ||
15195 id == GAME_CTRL_ID_PLAY)
15197 button_type = GD_TYPE_NORMAL_BUTTON;
15199 event_mask = GD_EVENT_RELEASED;
15200 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15201 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15205 button_type = GD_TYPE_CHECK_BUTTON;
15207 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15208 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15209 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15210 event_mask = GD_EVENT_PRESSED;
15211 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15212 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15215 gi = CreateGadget(GDI_CUSTOM_ID, id,
15216 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15221 GDI_X, DX + gd_xoffset,
15222 GDI_Y, DY + gd_yoffset,
15224 GDI_WIDTH, GAME_BUTTON_XSIZE,
15225 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15226 GDI_TYPE, button_type,
15227 GDI_STATE, GD_BUTTON_UNPRESSED,
15228 GDI_CHECKED, checked,
15229 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15230 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15231 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15232 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15233 GDI_EVENT_MASK, event_mask,
15234 GDI_CALLBACK_ACTION, HandleGameButtons,
15238 Error(ERR_EXIT, "cannot create gadget");
15240 game_gadget[id] = gi;
15244 void FreeGameButtons()
15248 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15249 FreeGadget(game_gadget[i]);
15252 static void MapGameButtons()
15256 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15257 MapGadget(game_gadget[i]);
15260 void UnmapGameButtons()
15264 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15265 UnmapGadget(game_gadget[i]);
15268 static void HandleGameButtons(struct GadgetInfo *gi)
15270 int id = gi->custom_id;
15272 if (game_status != GAME_MODE_PLAYING)
15277 case GAME_CTRL_ID_STOP:
15281 RequestQuitGame(TRUE);
15284 case GAME_CTRL_ID_PAUSE:
15285 if (options.network)
15287 #if defined(NETWORK_AVALIABLE)
15289 SendToServer_ContinuePlaying();
15291 SendToServer_PausePlaying();
15295 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15298 case GAME_CTRL_ID_PLAY:
15301 #if defined(NETWORK_AVALIABLE)
15302 if (options.network)
15303 SendToServer_ContinuePlaying();
15307 tape.pausing = FALSE;
15308 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15313 case SOUND_CTRL_ID_MUSIC:
15314 if (setup.sound_music)
15316 setup.sound_music = FALSE;
15319 else if (audio.music_available)
15321 setup.sound = setup.sound_music = TRUE;
15323 SetAudioMode(setup.sound);
15329 case SOUND_CTRL_ID_LOOPS:
15330 if (setup.sound_loops)
15331 setup.sound_loops = FALSE;
15332 else if (audio.loops_available)
15334 setup.sound = setup.sound_loops = TRUE;
15335 SetAudioMode(setup.sound);
15339 case SOUND_CTRL_ID_SIMPLE:
15340 if (setup.sound_simple)
15341 setup.sound_simple = FALSE;
15342 else if (audio.sound_available)
15344 setup.sound = setup.sound_simple = TRUE;
15345 SetAudioMode(setup.sound);