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_PANEL_LEVEL_NUMBER 0
135 #define GAME_PANEL_GEMS 1
136 #define GAME_PANEL_INVENTORY_COUNT 2
137 #define GAME_PANEL_INVENTORY_FIRST_1 3
138 #define GAME_PANEL_INVENTORY_FIRST_2 4
139 #define GAME_PANEL_INVENTORY_FIRST_3 5
140 #define GAME_PANEL_INVENTORY_FIRST_4 6
141 #define GAME_PANEL_INVENTORY_FIRST_5 7
142 #define GAME_PANEL_INVENTORY_FIRST_6 8
143 #define GAME_PANEL_INVENTORY_FIRST_7 9
144 #define GAME_PANEL_INVENTORY_FIRST_8 10
145 #define GAME_PANEL_INVENTORY_LAST_1 11
146 #define GAME_PANEL_INVENTORY_LAST_2 12
147 #define GAME_PANEL_INVENTORY_LAST_3 13
148 #define GAME_PANEL_INVENTORY_LAST_4 14
149 #define GAME_PANEL_INVENTORY_LAST_5 15
150 #define GAME_PANEL_INVENTORY_LAST_6 16
151 #define GAME_PANEL_INVENTORY_LAST_7 17
152 #define GAME_PANEL_INVENTORY_LAST_8 18
153 #define GAME_PANEL_KEY_1 19
154 #define GAME_PANEL_KEY_2 20
155 #define GAME_PANEL_KEY_3 21
156 #define GAME_PANEL_KEY_4 22
157 #define GAME_PANEL_KEY_5 23
158 #define GAME_PANEL_KEY_6 24
159 #define GAME_PANEL_KEY_7 25
160 #define GAME_PANEL_KEY_8 26
161 #define GAME_PANEL_KEY_WHITE 27
162 #define GAME_PANEL_KEY_WHITE_COUNT 28
163 #define GAME_PANEL_SCORE 29
164 #define GAME_PANEL_TIME 30
165 #define GAME_PANEL_TIME_HH 31
166 #define GAME_PANEL_TIME_MM 32
167 #define GAME_PANEL_TIME_SS 33
168 #define GAME_PANEL_SHIELD_NORMAL 34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME 35
170 #define GAME_PANEL_SHIELD_DEADLY 36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME 37
172 #define GAME_PANEL_EXIT 38
173 #define GAME_PANEL_EMC_MAGIC_BALL 39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 40
175 #define GAME_PANEL_LIGHT_SWITCH 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME 42
177 #define GAME_PANEL_TIMEGATE_SWITCH 43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 44
179 #define GAME_PANEL_SWITCHGATE_SWITCH 45
180 #define GAME_PANEL_EMC_LENSES 46
181 #define GAME_PANEL_EMC_LENSES_TIME 47
182 #define GAME_PANEL_EMC_MAGNIFIER 48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME 49
184 #define GAME_PANEL_BALLOON_SWITCH 50
185 #define GAME_PANEL_DYNABOMB_NUMBER 51
186 #define GAME_PANEL_DYNABOMB_SIZE 52
187 #define GAME_PANEL_DYNABOMB_POWER 53
188 #define GAME_PANEL_PENGUINS 54
189 #define GAME_PANEL_SOKOBAN_OBJECTS 55
190 #define GAME_PANEL_SOKOBAN_FIELDS 56
191 #define GAME_PANEL_ROBOT_WHEEL 57
192 #define GAME_PANEL_CONVEYOR_BELT_1 58
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 59
194 #define GAME_PANEL_CONVEYOR_BELT_2 60
195 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 61
196 #define GAME_PANEL_CONVEYOR_BELT_3 62
197 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 63
198 #define GAME_PANEL_CONVEYOR_BELT_4 64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 65
200 #define GAME_PANEL_MAGIC_WALL 66
201 #define GAME_PANEL_MAGIC_WALL_TIME 67
202 #define GAME_PANEL_GRAVITY_STATE 68
203 #define GAME_PANEL_PLAYER_NAME 69
204 #define GAME_PANEL_LEVEL_NAME 70
205 #define GAME_PANEL_LEVEL_AUTHOR 71
207 #define NUM_GAME_PANEL_CONTROLS 72
209 struct GamePanelControlInfo
213 struct TextPosInfo *pos;
216 int value, last_value;
217 int frame, last_frame;
221 static struct GamePanelControlInfo game_panel_controls[] =
224 GAME_PANEL_LEVEL_NUMBER,
225 &game.panel.level_number,
234 GAME_PANEL_INVENTORY_COUNT,
235 &game.panel.inventory_count,
239 GAME_PANEL_INVENTORY_FIRST_1,
240 &game.panel.inventory_first_1,
244 GAME_PANEL_INVENTORY_FIRST_2,
245 &game.panel.inventory_first_2,
249 GAME_PANEL_INVENTORY_FIRST_3,
250 &game.panel.inventory_first_3,
254 GAME_PANEL_INVENTORY_FIRST_4,
255 &game.panel.inventory_first_4,
259 GAME_PANEL_INVENTORY_FIRST_5,
260 &game.panel.inventory_first_5,
264 GAME_PANEL_INVENTORY_FIRST_6,
265 &game.panel.inventory_first_6,
269 GAME_PANEL_INVENTORY_FIRST_7,
270 &game.panel.inventory_first_7,
274 GAME_PANEL_INVENTORY_FIRST_8,
275 &game.panel.inventory_first_8,
279 GAME_PANEL_INVENTORY_LAST_1,
280 &game.panel.inventory_last_1,
284 GAME_PANEL_INVENTORY_LAST_2,
285 &game.panel.inventory_last_2,
289 GAME_PANEL_INVENTORY_LAST_3,
290 &game.panel.inventory_last_3,
294 GAME_PANEL_INVENTORY_LAST_4,
295 &game.panel.inventory_last_4,
299 GAME_PANEL_INVENTORY_LAST_5,
300 &game.panel.inventory_last_5,
304 GAME_PANEL_INVENTORY_LAST_6,
305 &game.panel.inventory_last_6,
309 GAME_PANEL_INVENTORY_LAST_7,
310 &game.panel.inventory_last_7,
314 GAME_PANEL_INVENTORY_LAST_8,
315 &game.panel.inventory_last_8,
359 GAME_PANEL_KEY_WHITE,
360 &game.panel.key_white,
364 GAME_PANEL_KEY_WHITE_COUNT,
365 &game.panel.key_white_count,
394 GAME_PANEL_SHIELD_NORMAL,
395 &game.panel.shield_normal,
399 GAME_PANEL_SHIELD_NORMAL_TIME,
400 &game.panel.shield_normal_time,
404 GAME_PANEL_SHIELD_DEADLY,
405 &game.panel.shield_deadly,
409 GAME_PANEL_SHIELD_DEADLY_TIME,
410 &game.panel.shield_deadly_time,
419 GAME_PANEL_EMC_MAGIC_BALL,
420 &game.panel.emc_magic_ball,
424 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
425 &game.panel.emc_magic_ball_switch,
429 GAME_PANEL_LIGHT_SWITCH,
430 &game.panel.light_switch,
434 GAME_PANEL_LIGHT_SWITCH_TIME,
435 &game.panel.light_switch_time,
439 GAME_PANEL_TIMEGATE_SWITCH,
440 &game.panel.timegate_switch,
444 GAME_PANEL_TIMEGATE_SWITCH_TIME,
445 &game.panel.timegate_switch_time,
449 GAME_PANEL_SWITCHGATE_SWITCH,
450 &game.panel.switchgate_switch,
454 GAME_PANEL_EMC_LENSES,
455 &game.panel.emc_lenses,
459 GAME_PANEL_EMC_LENSES_TIME,
460 &game.panel.emc_lenses_time,
464 GAME_PANEL_EMC_MAGNIFIER,
465 &game.panel.emc_magnifier,
469 GAME_PANEL_EMC_MAGNIFIER_TIME,
470 &game.panel.emc_magnifier_time,
474 GAME_PANEL_BALLOON_SWITCH,
475 &game.panel.balloon_switch,
479 GAME_PANEL_DYNABOMB_NUMBER,
480 &game.panel.dynabomb_number,
484 GAME_PANEL_DYNABOMB_SIZE,
485 &game.panel.dynabomb_size,
489 GAME_PANEL_DYNABOMB_POWER,
490 &game.panel.dynabomb_power,
495 &game.panel.penguins,
499 GAME_PANEL_SOKOBAN_OBJECTS,
500 &game.panel.sokoban_objects,
504 GAME_PANEL_SOKOBAN_FIELDS,
505 &game.panel.sokoban_fields,
509 GAME_PANEL_ROBOT_WHEEL,
510 &game.panel.robot_wheel,
514 GAME_PANEL_CONVEYOR_BELT_1,
515 &game.panel.conveyor_belt_1,
519 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
520 &game.panel.conveyor_belt_1_switch,
524 GAME_PANEL_CONVEYOR_BELT_2,
525 &game.panel.conveyor_belt_2,
529 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
530 &game.panel.conveyor_belt_2_switch,
534 GAME_PANEL_CONVEYOR_BELT_3,
535 &game.panel.conveyor_belt_3,
539 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
540 &game.panel.conveyor_belt_3_switch,
544 GAME_PANEL_CONVEYOR_BELT_4,
545 &game.panel.conveyor_belt_4,
549 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
550 &game.panel.conveyor_belt_4_switch,
554 GAME_PANEL_MAGIC_WALL,
555 &game.panel.magic_wall,
559 GAME_PANEL_MAGIC_WALL_TIME,
560 &game.panel.magic_wall_time,
564 GAME_PANEL_GRAVITY_STATE,
565 &game.panel.gravity_state,
569 GAME_PANEL_PLAYER_NAME,
570 &game.panel.player_name,
574 GAME_PANEL_LEVEL_NAME,
575 &game.panel.level_name,
579 GAME_PANEL_LEVEL_AUTHOR,
580 &game.panel.level_author,
593 /* values for delayed check of falling and moving elements and for collision */
594 #define CHECK_DELAY_MOVING 3
595 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
596 #define CHECK_DELAY_COLLISION 2
597 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
599 /* values for initial player move delay (initial delay counter value) */
600 #define INITIAL_MOVE_DELAY_OFF -1
601 #define INITIAL_MOVE_DELAY_ON 0
603 /* values for player movement speed (which is in fact a delay value) */
604 #define MOVE_DELAY_MIN_SPEED 32
605 #define MOVE_DELAY_NORMAL_SPEED 8
606 #define MOVE_DELAY_HIGH_SPEED 4
607 #define MOVE_DELAY_MAX_SPEED 1
609 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
610 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
612 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
613 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
615 /* values for other actions */
616 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
617 #define MOVE_STEPSIZE_MIN (1)
618 #define MOVE_STEPSIZE_MAX (TILEX)
620 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
621 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
623 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
625 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
626 RND(element_info[e].push_delay_random))
627 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
628 RND(element_info[e].drop_delay_random))
629 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
630 RND(element_info[e].move_delay_random))
631 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
632 (element_info[e].move_delay_random))
633 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
634 RND(element_info[e].ce_value_random_initial))
635 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
636 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
637 RND((c)->delay_random * (c)->delay_frames))
638 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
639 RND((c)->delay_random))
642 #define GET_VALID_RUNTIME_ELEMENT(e) \
643 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
645 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
646 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
647 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
648 (be) + (e) - EL_SELF)
650 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
651 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
652 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
653 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
654 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
655 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
656 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
657 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
658 RESOLVED_REFERENCE_ELEMENT(be, e) : \
661 #define CAN_GROW_INTO(e) \
662 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
664 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
665 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
668 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
669 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
670 (CAN_MOVE_INTO_ACID(e) && \
671 Feld[x][y] == EL_ACID) || \
674 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
675 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
676 (CAN_MOVE_INTO_ACID(e) && \
677 Feld[x][y] == EL_ACID) || \
680 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
681 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
683 (CAN_MOVE_INTO_ACID(e) && \
684 Feld[x][y] == EL_ACID) || \
685 (DONT_COLLIDE_WITH(e) && \
687 !PLAYER_ENEMY_PROTECTED(x, y))))
689 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
690 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
692 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
693 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
695 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
696 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
698 #define ANDROID_CAN_CLONE_FIELD(x, y) \
699 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
700 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
702 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
703 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
705 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
706 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
708 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
709 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
711 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
712 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
714 #define PIG_CAN_ENTER_FIELD(e, x, y) \
715 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
717 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
718 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
719 Feld[x][y] == EL_EM_EXIT_OPEN || \
720 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
721 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
722 IS_FOOD_PENGUIN(Feld[x][y])))
723 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
724 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
726 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
727 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
729 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
730 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
732 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
733 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
734 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
736 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
738 #define CE_ENTER_FIELD_COND(e, x, y) \
739 (!IS_PLAYER(x, y) && \
740 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
742 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
743 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
745 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
746 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
748 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
749 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
750 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
751 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
753 /* game button identifiers */
754 #define GAME_CTRL_ID_STOP 0
755 #define GAME_CTRL_ID_PAUSE 1
756 #define GAME_CTRL_ID_PLAY 2
757 #define SOUND_CTRL_ID_MUSIC 3
758 #define SOUND_CTRL_ID_LOOPS 4
759 #define SOUND_CTRL_ID_SIMPLE 5
761 #define NUM_GAME_BUTTONS 6
764 /* forward declaration for internal use */
766 static void CreateField(int, int, int);
768 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
769 static void AdvanceFrameAndPlayerCounters(int);
771 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
772 static boolean MovePlayer(struct PlayerInfo *, int, int);
773 static void ScrollPlayer(struct PlayerInfo *, int);
774 static void ScrollScreen(struct PlayerInfo *, int);
776 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
778 static void InitBeltMovement(void);
779 static void CloseAllOpenTimegates(void);
780 static void CheckGravityMovement(struct PlayerInfo *);
781 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
782 static void KillPlayerUnlessEnemyProtected(int, int);
783 static void KillPlayerUnlessExplosionProtected(int, int);
785 static void TestIfPlayerTouchesCustomElement(int, int);
786 static void TestIfElementTouchesCustomElement(int, int);
787 static void TestIfElementHitsCustomElement(int, int, int);
789 static void TestIfElementSmashesCustomElement(int, int, int);
792 static void HandleElementChange(int, int, int);
793 static void ExecuteCustomElementAction(int, int, int, int);
794 static boolean ChangeElement(int, int, int, int);
796 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
797 #define CheckTriggeredElementChange(x, y, e, ev) \
798 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
799 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
800 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
801 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
802 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
803 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
804 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
806 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
807 #define CheckElementChange(x, y, e, te, ev) \
808 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
809 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
810 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
811 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
812 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
814 static void PlayLevelSound(int, int, int);
815 static void PlayLevelSoundNearest(int, int, int);
816 static void PlayLevelSoundAction(int, int, int);
817 static void PlayLevelSoundElementAction(int, int, int, int);
818 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
819 static void PlayLevelSoundActionIfLoop(int, int, int);
820 static void StopLevelSoundActionIfLoop(int, int, int);
821 static void PlayLevelMusic();
823 static void MapGameButtons();
824 static void HandleGameButtons(struct GadgetInfo *);
826 int AmoebeNachbarNr(int, int);
827 void AmoebeUmwandeln(int, int);
828 void ContinueMoving(int, int);
830 void InitMovDir(int, int);
831 void InitAmoebaNr(int, int);
832 int NewHiScore(void);
834 void TestIfGoodThingHitsBadThing(int, int, int);
835 void TestIfBadThingHitsGoodThing(int, int, int);
836 void TestIfPlayerTouchesBadThing(int, int);
837 void TestIfPlayerRunsIntoBadThing(int, int, int);
838 void TestIfBadThingTouchesPlayer(int, int);
839 void TestIfBadThingRunsIntoPlayer(int, int, int);
840 void TestIfFriendTouchesBadThing(int, int);
841 void TestIfBadThingTouchesFriend(int, int);
842 void TestIfBadThingTouchesOtherBadThing(int, int);
844 void KillPlayer(struct PlayerInfo *);
845 void BuryPlayer(struct PlayerInfo *);
846 void RemovePlayer(struct PlayerInfo *);
848 boolean SnapField(struct PlayerInfo *, int, int);
849 boolean DropElement(struct PlayerInfo *);
851 static int getInvisibleActiveFromInvisibleElement(int);
852 static int getInvisibleFromInvisibleActiveElement(int);
854 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
856 /* for detection of endless loops, caused by custom element programming */
857 /* (using maximal playfield width x 10 is just a rough approximation) */
858 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
860 #define RECURSION_LOOP_DETECTION_START(e, rc) \
862 if (recursion_loop_detected) \
865 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
867 recursion_loop_detected = TRUE; \
868 recursion_loop_element = (e); \
871 recursion_loop_depth++; \
874 #define RECURSION_LOOP_DETECTION_END() \
876 recursion_loop_depth--; \
879 static int recursion_loop_depth;
880 static boolean recursion_loop_detected;
881 static boolean recursion_loop_element;
884 /* ------------------------------------------------------------------------- */
885 /* definition of elements that automatically change to other elements after */
886 /* a specified time, eventually calling a function when changing */
887 /* ------------------------------------------------------------------------- */
889 /* forward declaration for changer functions */
890 static void InitBuggyBase(int, int);
891 static void WarnBuggyBase(int, int);
893 static void InitTrap(int, int);
894 static void ActivateTrap(int, int);
895 static void ChangeActiveTrap(int, int);
897 static void InitRobotWheel(int, int);
898 static void RunRobotWheel(int, int);
899 static void StopRobotWheel(int, int);
901 static void InitTimegateWheel(int, int);
902 static void RunTimegateWheel(int, int);
904 static void InitMagicBallDelay(int, int);
905 static void ActivateMagicBall(int, int);
907 struct ChangingElementInfo
912 void (*pre_change_function)(int x, int y);
913 void (*change_function)(int x, int y);
914 void (*post_change_function)(int x, int y);
917 static struct ChangingElementInfo change_delay_list[] =
952 EL_STEEL_EXIT_OPENING,
960 EL_STEEL_EXIT_CLOSING,
961 EL_STEEL_EXIT_CLOSED,
988 EL_EM_STEEL_EXIT_OPENING,
989 EL_EM_STEEL_EXIT_OPEN,
996 EL_EM_STEEL_EXIT_CLOSING,
1000 EL_EM_STEEL_EXIT_CLOSED,
1024 EL_SWITCHGATE_OPENING,
1032 EL_SWITCHGATE_CLOSING,
1033 EL_SWITCHGATE_CLOSED,
1040 EL_TIMEGATE_OPENING,
1048 EL_TIMEGATE_CLOSING,
1057 EL_ACID_SPLASH_LEFT,
1065 EL_ACID_SPLASH_RIGHT,
1074 EL_SP_BUGGY_BASE_ACTIVATING,
1081 EL_SP_BUGGY_BASE_ACTIVATING,
1082 EL_SP_BUGGY_BASE_ACTIVE,
1089 EL_SP_BUGGY_BASE_ACTIVE,
1113 EL_ROBOT_WHEEL_ACTIVE,
1121 EL_TIMEGATE_SWITCH_ACTIVE,
1129 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1130 EL_DC_TIMEGATE_SWITCH,
1137 EL_EMC_MAGIC_BALL_ACTIVE,
1138 EL_EMC_MAGIC_BALL_ACTIVE,
1145 EL_EMC_SPRING_BUMPER_ACTIVE,
1146 EL_EMC_SPRING_BUMPER,
1153 EL_DIAGONAL_SHRINKING,
1161 EL_DIAGONAL_GROWING,
1182 int push_delay_fixed, push_delay_random;
1186 { EL_SPRING, 0, 0 },
1187 { EL_BALLOON, 0, 0 },
1189 { EL_SOKOBAN_OBJECT, 2, 0 },
1190 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1191 { EL_SATELLITE, 2, 0 },
1192 { EL_SP_DISK_YELLOW, 2, 0 },
1194 { EL_UNDEFINED, 0, 0 },
1202 move_stepsize_list[] =
1204 { EL_AMOEBA_DROP, 2 },
1205 { EL_AMOEBA_DROPPING, 2 },
1206 { EL_QUICKSAND_FILLING, 1 },
1207 { EL_QUICKSAND_EMPTYING, 1 },
1208 { EL_QUICKSAND_FAST_FILLING, 2 },
1209 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1210 { EL_MAGIC_WALL_FILLING, 2 },
1211 { EL_MAGIC_WALL_EMPTYING, 2 },
1212 { EL_BD_MAGIC_WALL_FILLING, 2 },
1213 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1214 { EL_DC_MAGIC_WALL_FILLING, 2 },
1215 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1217 { EL_UNDEFINED, 0 },
1225 collect_count_list[] =
1228 { EL_BD_DIAMOND, 1 },
1229 { EL_EMERALD_YELLOW, 1 },
1230 { EL_EMERALD_RED, 1 },
1231 { EL_EMERALD_PURPLE, 1 },
1233 { EL_SP_INFOTRON, 1 },
1237 { EL_UNDEFINED, 0 },
1245 access_direction_list[] =
1247 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1248 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1249 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1250 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1251 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1252 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1253 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1254 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1255 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1256 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1257 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1259 { EL_SP_PORT_LEFT, MV_RIGHT },
1260 { EL_SP_PORT_RIGHT, MV_LEFT },
1261 { EL_SP_PORT_UP, MV_DOWN },
1262 { EL_SP_PORT_DOWN, MV_UP },
1263 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1264 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1265 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1266 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1267 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1268 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1269 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1270 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1271 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1272 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1273 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1274 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1275 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1276 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1277 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1279 { EL_UNDEFINED, MV_NONE }
1282 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1284 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1285 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1286 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1287 IS_JUST_CHANGING(x, y))
1289 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1291 /* static variables for playfield scan mode (scanning forward or backward) */
1292 static int playfield_scan_start_x = 0;
1293 static int playfield_scan_start_y = 0;
1294 static int playfield_scan_delta_x = 1;
1295 static int playfield_scan_delta_y = 1;
1297 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1298 (y) >= 0 && (y) <= lev_fieldy - 1; \
1299 (y) += playfield_scan_delta_y) \
1300 for ((x) = playfield_scan_start_x; \
1301 (x) >= 0 && (x) <= lev_fieldx - 1; \
1302 (x) += playfield_scan_delta_x)
1305 void DEBUG_SetMaximumDynamite()
1309 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1310 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1311 local_player->inventory_element[local_player->inventory_size++] =
1316 static void InitPlayfieldScanModeVars()
1318 if (game.use_reverse_scan_direction)
1320 playfield_scan_start_x = lev_fieldx - 1;
1321 playfield_scan_start_y = lev_fieldy - 1;
1323 playfield_scan_delta_x = -1;
1324 playfield_scan_delta_y = -1;
1328 playfield_scan_start_x = 0;
1329 playfield_scan_start_y = 0;
1331 playfield_scan_delta_x = 1;
1332 playfield_scan_delta_y = 1;
1336 static void InitPlayfieldScanMode(int mode)
1338 game.use_reverse_scan_direction =
1339 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1341 InitPlayfieldScanModeVars();
1344 static int get_move_delay_from_stepsize(int move_stepsize)
1347 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1349 /* make sure that stepsize value is always a power of 2 */
1350 move_stepsize = (1 << log_2(move_stepsize));
1352 return TILEX / move_stepsize;
1355 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1358 int player_nr = player->index_nr;
1359 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1360 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1362 /* do no immediately change move delay -- the player might just be moving */
1363 player->move_delay_value_next = move_delay;
1365 /* information if player can move must be set separately */
1366 player->cannot_move = cannot_move;
1370 player->move_delay = game.initial_move_delay[player_nr];
1371 player->move_delay_value = game.initial_move_delay_value[player_nr];
1373 player->move_delay_value_next = -1;
1375 player->move_delay_reset_counter = 0;
1379 void GetPlayerConfig()
1381 GameFrameDelay = setup.game_frame_delay;
1383 if (!audio.sound_available)
1384 setup.sound_simple = FALSE;
1386 if (!audio.loops_available)
1387 setup.sound_loops = FALSE;
1389 if (!audio.music_available)
1390 setup.sound_music = FALSE;
1392 if (!video.fullscreen_available)
1393 setup.fullscreen = FALSE;
1395 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1397 SetAudioMode(setup.sound);
1401 int GetElementFromGroupElement(int element)
1403 if (IS_GROUP_ELEMENT(element))
1405 struct ElementGroupInfo *group = element_info[element].group;
1406 int last_anim_random_frame = gfx.anim_random_frame;
1409 if (group->choice_mode == ANIM_RANDOM)
1410 gfx.anim_random_frame = RND(group->num_elements_resolved);
1412 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1413 group->choice_mode, 0,
1416 if (group->choice_mode == ANIM_RANDOM)
1417 gfx.anim_random_frame = last_anim_random_frame;
1419 group->choice_pos++;
1421 element = group->element_resolved[element_pos];
1427 static void InitPlayerField(int x, int y, int element, boolean init_game)
1429 if (element == EL_SP_MURPHY)
1433 if (stored_player[0].present)
1435 Feld[x][y] = EL_SP_MURPHY_CLONE;
1441 stored_player[0].use_murphy = TRUE;
1443 if (!level.use_artwork_element[0])
1444 stored_player[0].artwork_element = EL_SP_MURPHY;
1447 Feld[x][y] = EL_PLAYER_1;
1453 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1454 int jx = player->jx, jy = player->jy;
1456 player->present = TRUE;
1458 player->block_last_field = (element == EL_SP_MURPHY ?
1459 level.sp_block_last_field :
1460 level.block_last_field);
1462 /* ---------- initialize player's last field block delay --------------- */
1464 /* always start with reliable default value (no adjustment needed) */
1465 player->block_delay_adjustment = 0;
1467 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1468 if (player->block_last_field && element == EL_SP_MURPHY)
1469 player->block_delay_adjustment = 1;
1471 /* special case 2: in game engines before 3.1.1, blocking was different */
1472 if (game.use_block_last_field_bug)
1473 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1475 if (!options.network || player->connected)
1477 player->active = TRUE;
1479 /* remove potentially duplicate players */
1480 if (StorePlayer[jx][jy] == Feld[x][y])
1481 StorePlayer[jx][jy] = 0;
1483 StorePlayer[x][y] = Feld[x][y];
1487 printf("Player %d activated.\n", player->element_nr);
1488 printf("[Local player is %d and currently %s.]\n",
1489 local_player->element_nr,
1490 local_player->active ? "active" : "not active");
1494 Feld[x][y] = EL_EMPTY;
1496 player->jx = player->last_jx = x;
1497 player->jy = player->last_jy = y;
1501 static void InitField(int x, int y, boolean init_game)
1503 int element = Feld[x][y];
1512 InitPlayerField(x, y, element, init_game);
1515 case EL_SOKOBAN_FIELD_PLAYER:
1516 element = Feld[x][y] = EL_PLAYER_1;
1517 InitField(x, y, init_game);
1519 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1520 InitField(x, y, init_game);
1523 case EL_SOKOBAN_FIELD_EMPTY:
1524 local_player->sokobanfields_still_needed++;
1528 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1529 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1530 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1531 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1532 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1533 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1534 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1535 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1536 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1537 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1546 case EL_SPACESHIP_RIGHT:
1547 case EL_SPACESHIP_UP:
1548 case EL_SPACESHIP_LEFT:
1549 case EL_SPACESHIP_DOWN:
1550 case EL_BD_BUTTERFLY:
1551 case EL_BD_BUTTERFLY_RIGHT:
1552 case EL_BD_BUTTERFLY_UP:
1553 case EL_BD_BUTTERFLY_LEFT:
1554 case EL_BD_BUTTERFLY_DOWN:
1556 case EL_BD_FIREFLY_RIGHT:
1557 case EL_BD_FIREFLY_UP:
1558 case EL_BD_FIREFLY_LEFT:
1559 case EL_BD_FIREFLY_DOWN:
1560 case EL_PACMAN_RIGHT:
1562 case EL_PACMAN_LEFT:
1563 case EL_PACMAN_DOWN:
1565 case EL_YAMYAM_LEFT:
1566 case EL_YAMYAM_RIGHT:
1568 case EL_YAMYAM_DOWN:
1569 case EL_DARK_YAMYAM:
1572 case EL_SP_SNIKSNAK:
1573 case EL_SP_ELECTRON:
1582 case EL_AMOEBA_FULL:
1587 case EL_AMOEBA_DROP:
1588 if (y == lev_fieldy - 1)
1590 Feld[x][y] = EL_AMOEBA_GROWING;
1591 Store[x][y] = EL_AMOEBA_WET;
1595 case EL_DYNAMITE_ACTIVE:
1596 case EL_SP_DISK_RED_ACTIVE:
1597 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1598 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1599 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1600 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1601 MovDelay[x][y] = 96;
1604 case EL_EM_DYNAMITE_ACTIVE:
1605 MovDelay[x][y] = 32;
1609 local_player->lights_still_needed++;
1613 local_player->friends_still_needed++;
1618 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1621 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1622 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1623 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1624 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1625 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1626 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1627 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1628 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1629 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1630 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1631 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1632 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1635 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1636 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1637 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1639 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1641 game.belt_dir[belt_nr] = belt_dir;
1642 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1644 else /* more than one switch -- set it like the first switch */
1646 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1651 #if !USE_BOTH_SWITCHGATE_SWITCHES
1652 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1654 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1657 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1659 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1663 case EL_LIGHT_SWITCH_ACTIVE:
1665 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1668 case EL_INVISIBLE_STEELWALL:
1669 case EL_INVISIBLE_WALL:
1670 case EL_INVISIBLE_SAND:
1671 if (game.light_time_left > 0 ||
1672 game.lenses_time_left > 0)
1673 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1676 case EL_EMC_MAGIC_BALL:
1677 if (game.ball_state)
1678 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1681 case EL_EMC_MAGIC_BALL_SWITCH:
1682 if (game.ball_state)
1683 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1687 if (IS_CUSTOM_ELEMENT(element))
1689 if (CAN_MOVE(element))
1692 #if USE_NEW_CUSTOM_VALUE
1693 if (!element_info[element].use_last_ce_value || init_game)
1694 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1697 else if (IS_GROUP_ELEMENT(element))
1699 Feld[x][y] = GetElementFromGroupElement(element);
1701 InitField(x, y, init_game);
1708 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1711 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1713 InitField(x, y, init_game);
1715 /* not needed to call InitMovDir() -- already done by InitField()! */
1716 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1717 CAN_MOVE(Feld[x][y]))
1721 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1723 int old_element = Feld[x][y];
1725 InitField(x, y, init_game);
1727 /* not needed to call InitMovDir() -- already done by InitField()! */
1728 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1729 CAN_MOVE(old_element) &&
1730 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1733 /* this case is in fact a combination of not less than three bugs:
1734 first, it calls InitMovDir() for elements that can move, although this is
1735 already done by InitField(); then, it checks the element that was at this
1736 field _before_ the call to InitField() (which can change it); lastly, it
1737 was not called for "mole with direction" elements, which were treated as
1738 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1744 static int get_key_element_from_nr(int key_nr)
1746 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1747 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1748 EL_EM_KEY_1 : EL_KEY_1);
1750 return key_base_element + key_nr;
1753 static int get_next_dropped_element(struct PlayerInfo *player)
1755 return (player->inventory_size > 0 ?
1756 player->inventory_element[player->inventory_size - 1] :
1757 player->inventory_infinite_element != EL_UNDEFINED ?
1758 player->inventory_infinite_element :
1759 player->dynabombs_left > 0 ?
1760 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1764 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1766 /* pos >= 0: get element from bottom of the stack;
1767 pos < 0: get element from top of the stack */
1771 int min_inventory_size = -pos;
1772 int inventory_pos = player->inventory_size - min_inventory_size;
1773 int min_dynabombs_left = min_inventory_size - player->inventory_size;
1775 return (player->inventory_size >= min_inventory_size ?
1776 player->inventory_element[inventory_pos] :
1777 player->inventory_infinite_element != EL_UNDEFINED ?
1778 player->inventory_infinite_element :
1779 player->dynabombs_left >= min_dynabombs_left ?
1780 EL_DYNABOMB_PLAYER_1 + player->index_nr :
1785 int min_dynabombs_left = pos + 1;
1786 int min_inventory_size = pos + 1 - player->dynabombs_left;
1787 int inventory_pos = pos - player->dynabombs_left;
1789 return (player->inventory_infinite_element != EL_UNDEFINED ?
1790 player->inventory_infinite_element :
1791 player->dynabombs_left >= min_dynabombs_left ?
1792 EL_DYNABOMB_PLAYER_1 + player->index_nr :
1793 player->inventory_size >= min_inventory_size ?
1794 player->inventory_element[inventory_pos] :
1799 void InitGameControlValues()
1803 for (i = 0; game_panel_controls[i].nr != -1; i++)
1805 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
1807 int type = gpc->type;
1808 struct TextPosInfo *pos = gpc->pos;
1812 Error(ERR_INFO, "'game_panel_controls' structure corrupted");
1813 Error(ERR_EXIT, "this should not happen -- please debug");
1816 /* force update of game controls after initialization */
1817 gpc->value = gpc->last_value = -1;
1818 gpc->frame = gpc->last_frame = -1;
1819 gpc->gfx_frame = -1;
1821 /* determine panel value width for later calculation of alignment */
1822 if (type == TYPE_INTEGER || type == TYPE_STRING)
1824 pos->width = pos->size * getFontWidth(pos->font);
1825 pos->height = getFontHeight(pos->font);
1827 else if (type == TYPE_ELEMENT)
1829 pos->width = pos->size;
1830 pos->height = pos->size;
1835 void UpdateGameControlValues()
1838 int time = (level.time == 0 ? TimePlayed : TimeLeft);
1839 int score = (local_player->LevelSolved ? local_player->score_final :
1840 local_player->score);
1841 int exit_closed = (local_player->gems_still_needed > 0 ||
1842 local_player->sokobanfields_still_needed > 0 ||
1843 local_player->lights_still_needed > 0);
1845 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
1846 game_panel_controls[GAME_PANEL_GEMS].value =
1847 local_player->gems_still_needed;
1849 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
1850 for (i = 0; i < MAX_NUM_KEYS; i++)
1851 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
1852 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
1853 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
1855 if (game.centered_player_nr == -1)
1857 for (i = 0; i < MAX_PLAYERS; i++)
1859 for (k = 0; k < MAX_NUM_KEYS; k++)
1860 if (stored_player[i].key[k])
1861 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1862 get_key_element_from_nr(k);
1864 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1865 stored_player[i].inventory_size;
1867 if (stored_player[i].num_white_keys > 0)
1868 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
1871 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1872 stored_player[i].num_white_keys;
1877 int player_nr = game.centered_player_nr;
1879 for (k = 0; k < MAX_NUM_KEYS; k++)
1880 if (stored_player[player_nr].key[k])
1881 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1882 get_key_element_from_nr(k);
1884 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1885 stored_player[player_nr].inventory_size;
1887 if (stored_player[player_nr].num_white_keys > 0)
1888 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
1890 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1891 stored_player[player_nr].num_white_keys;
1894 for (i = 0; i < 8; i++)
1896 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
1897 get_inventory_element_from_pos(local_player, i);
1898 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
1899 get_inventory_element_from_pos(local_player, -i - 1);
1902 game_panel_controls[GAME_PANEL_SCORE].value = score;
1904 game_panel_controls[GAME_PANEL_TIME].value = time;
1906 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
1907 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
1908 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
1910 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
1911 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1913 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
1914 local_player->shield_normal_time_left;
1915 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
1916 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1918 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
1919 local_player->shield_deadly_time_left;
1921 game_panel_controls[GAME_PANEL_EXIT].value =
1922 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
1924 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
1925 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
1926 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
1927 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
1928 EL_EMC_MAGIC_BALL_SWITCH);
1930 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
1931 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1932 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
1933 game.light_time_left;
1935 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
1936 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1937 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
1938 game.timegate_time_left;
1940 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
1941 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
1943 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
1944 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1945 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
1946 game.lenses_time_left;
1948 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
1949 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1950 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
1951 game.magnify_time_left;
1953 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
1954 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
1955 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1956 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
1957 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
1958 EL_BALLOON_SWITCH_NONE);
1960 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
1961 local_player->dynabomb_count;
1962 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
1963 local_player->dynabomb_size;
1964 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
1965 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1967 game_panel_controls[GAME_PANEL_PENGUINS].value =
1968 local_player->friends_still_needed;
1970 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
1971 local_player->sokobanfields_still_needed;
1972 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
1973 local_player->sokobanfields_still_needed;
1975 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
1976 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
1978 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1].value =
1979 (game.belt_dir[0] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
1980 EL_CONVEYOR_BELT_1_MIDDLE);
1981 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH].value =
1982 getBeltSwitchElementFromBeltNrAndBeltDir(0, game.belt_dir[0]);
1983 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_2].value =
1984 (game.belt_dir[1] != MV_NONE ? EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE :
1985 EL_CONVEYOR_BELT_2_MIDDLE);
1986 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_2_SWITCH].value =
1987 getBeltSwitchElementFromBeltNrAndBeltDir(1, game.belt_dir[1]);
1988 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_3].value =
1989 (game.belt_dir[2] != MV_NONE ? EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE :
1990 EL_CONVEYOR_BELT_3_MIDDLE);
1991 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_3_SWITCH].value =
1992 getBeltSwitchElementFromBeltNrAndBeltDir(2, game.belt_dir[2]);
1993 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_4].value =
1994 (game.belt_dir[3] != MV_NONE ? EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE :
1995 EL_CONVEYOR_BELT_4_MIDDLE);
1996 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_4_SWITCH].value =
1997 getBeltSwitchElementFromBeltNrAndBeltDir(3, game.belt_dir[3]);
1999 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2000 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2001 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2002 game.magic_wall_time_left;
2004 #if USE_PLAYER_GRAVITY
2005 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2006 local_player->gravity;
2008 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2011 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2012 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2013 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2015 for (i = 0; game_panel_controls[i].nr != -1; i++)
2017 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2019 if (gpc->type == TYPE_ELEMENT)
2021 if (gpc->value != gpc->last_value)
2026 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2032 void DisplayGameControlValues()
2036 game_status = GAME_MODE_PSEUDO_PANEL;
2038 for (i = 0; game_panel_controls[i].nr != -1; i++)
2040 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2042 int type = gpc->type;
2043 struct TextPosInfo *pos = gpc->pos;
2044 int value = gpc->value;
2045 int frame = gpc->frame;
2046 int last_value = gpc->last_value;
2047 int last_frame = gpc->last_frame;
2048 int size = pos->size;
2049 int font = pos->font;
2051 if (value == last_value && frame == last_frame)
2054 gpc->last_value = value;
2055 gpc->last_frame = frame;
2058 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2061 if (PANEL_DEACTIVATED(pos))
2064 if (type == TYPE_INTEGER)
2066 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2067 nr == GAME_PANEL_TIME)
2069 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2071 if (use_dynamic_size) /* use dynamic number of digits */
2073 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2074 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2075 int size2 = size1 + 1;
2076 int font1 = pos->font;
2077 int font2 = pos->font_alt;
2079 size = (value < value_change ? size1 : size2);
2080 font = (value < value_change ? font1 : font2);
2082 /* clear background if value just changed its size (dynamic digits) */
2083 if ((last_value < value_change) != (value < value_change))
2085 int width1 = size1 * getFontWidth(font1);
2086 int width2 = size2 * getFontWidth(font2);
2087 int max_width = MAX(width1, width2);
2088 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2090 pos->width = max_width;
2092 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2093 max_width, max_height);
2097 pos->width = size * getFontWidth(font);
2100 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2102 else if (type == TYPE_ELEMENT)
2104 int dst_x = PANEL_XPOS(pos);
2105 int dst_y = PANEL_YPOS(pos);
2107 if (value == EL_UNDEFINED || value == EL_EMPTY)
2109 int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2110 int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2112 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2113 size, size, dst_x, dst_y);
2117 int graphic = el2panelimg(value);
2119 DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, frame, size);
2122 else if (type == TYPE_STRING)
2124 boolean active = (value != 0);
2125 char *state_normal = "off";
2126 char *state_active = "on";
2127 char *state = (active ? state_active : state_normal);
2128 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2129 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2130 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2131 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2133 if (nr == GAME_PANEL_GRAVITY_STATE)
2135 int font1 = pos->font; /* (used for normal state) */
2136 int font2 = pos->font_alt; /* (used for active state) */
2137 int size1 = strlen(state_normal);
2138 int size2 = strlen(state_active);
2139 int width1 = size1 * getFontWidth(font1);
2140 int width2 = size2 * getFontWidth(font2);
2141 int max_width = MAX(width1, width2);
2142 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2144 pos->width = max_width;
2146 /* clear background for values that may have changed its size */
2147 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2148 max_width, max_height);
2150 font = (active ? font2 : font1);
2154 /* as with numbers, don't truncate output if "chars" is zero or less */
2155 size = (size > 0 ? size : strlen(s));
2160 char *s_cut = getStringCopyN(s, size);
2163 /* (not needed anymore with above correction of "size" value) */
2164 size = strlen(s_cut); /* string size may be smaller than "chars" */
2167 pos->width = size * getFontWidth(font);
2169 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2175 redraw_mask |= REDRAW_DOOR_1;
2178 game_status = GAME_MODE_PLAYING;
2181 void DrawGameValue_Emeralds(int value)
2183 struct TextPosInfo *pos = &game.panel.gems;
2185 int font_nr = pos->font;
2187 int font_nr = FONT_TEXT_2;
2189 int font_width = getFontWidth(font_nr);
2190 int chars = pos->size;
2193 return; /* !!! USE NEW STUFF !!! */
2196 if (PANEL_DEACTIVATED(pos))
2199 pos->width = chars * font_width;
2201 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2204 void DrawGameValue_Dynamite(int value)
2206 struct TextPosInfo *pos = &game.panel.inventory_count;
2208 int font_nr = pos->font;
2210 int font_nr = FONT_TEXT_2;
2212 int font_width = getFontWidth(font_nr);
2213 int chars = pos->size;
2216 return; /* !!! USE NEW STUFF !!! */
2219 if (PANEL_DEACTIVATED(pos))
2222 pos->width = chars * font_width;
2224 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2227 void DrawGameValue_Score(int value)
2229 struct TextPosInfo *pos = &game.panel.score;
2231 int font_nr = pos->font;
2233 int font_nr = FONT_TEXT_2;
2235 int font_width = getFontWidth(font_nr);
2236 int chars = pos->size;
2239 return; /* !!! USE NEW STUFF !!! */
2242 if (PANEL_DEACTIVATED(pos))
2245 pos->width = chars * font_width;
2247 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2250 void DrawGameValue_Time(int value)
2252 struct TextPosInfo *pos = &game.panel.time;
2253 static int last_value = -1;
2256 int chars = pos->size;
2258 int font1_nr = pos->font;
2259 int font2_nr = pos->font_alt;
2261 int font1_nr = FONT_TEXT_2;
2262 int font2_nr = FONT_TEXT_1;
2264 int font_nr = font1_nr;
2265 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2268 return; /* !!! USE NEW STUFF !!! */
2271 if (PANEL_DEACTIVATED(pos))
2274 if (use_dynamic_chars) /* use dynamic number of chars */
2276 chars = (value < 1000 ? chars1 : chars2);
2277 font_nr = (value < 1000 ? font1_nr : font2_nr);
2280 /* clear background if value just changed its size (dynamic chars only) */
2281 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2283 int width1 = chars1 * getFontWidth(font1_nr);
2284 int width2 = chars2 * getFontWidth(font2_nr);
2285 int max_width = MAX(width1, width2);
2286 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2288 pos->width = max_width;
2290 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2291 max_width, max_height);
2294 pos->width = chars * getFontWidth(font_nr);
2296 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2301 void DrawGameValue_Level(int value)
2303 struct TextPosInfo *pos = &game.panel.level_number;
2306 int chars = pos->size;
2308 int font1_nr = pos->font;
2309 int font2_nr = pos->font_alt;
2311 int font1_nr = FONT_TEXT_2;
2312 int font2_nr = FONT_TEXT_1;
2314 int font_nr = font1_nr;
2315 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2318 return; /* !!! USE NEW STUFF !!! */
2321 if (PANEL_DEACTIVATED(pos))
2324 if (use_dynamic_chars) /* use dynamic number of chars */
2326 chars = (level_nr < 100 ? chars1 : chars2);
2327 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2330 pos->width = chars * getFontWidth(font_nr);
2332 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2335 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2338 struct TextPosInfo *pos = &game.panel.keys;
2341 int base_key_graphic = EL_KEY_1;
2346 return; /* !!! USE NEW STUFF !!! */
2350 if (PANEL_DEACTIVATED(pos))
2355 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2356 base_key_graphic = EL_EM_KEY_1;
2360 pos->width = 4 * MINI_TILEX;
2364 for (i = 0; i < MAX_NUM_KEYS; i++)
2366 /* currently only 4 of 8 possible keys are displayed */
2367 for (i = 0; i < STD_NUM_KEYS; i++)
2371 struct TextPosInfo *pos = &game.panel.key[i];
2373 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2374 int src_y = DOOR_GFX_PAGEY1 + 123;
2376 int dst_x = PANEL_XPOS(pos);
2377 int dst_y = PANEL_YPOS(pos);
2379 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2380 int dst_y = PANEL_YPOS(pos);
2384 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2385 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2387 int graphic = el2edimg(element);
2391 if (PANEL_DEACTIVATED(pos))
2396 /* masked blit with tiles from half-size scaled bitmap does not work yet
2397 (no mask bitmap created for these sizes after loading and scaling) --
2398 solution: load without creating mask, scale, then create final mask */
2400 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2401 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2406 int graphic = el2edimg(base_key_graphic + i);
2411 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2413 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2414 dst_x - src_x, dst_y - src_y);
2415 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2421 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2423 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2424 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2427 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2429 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2430 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2438 void DrawGameValue_Emeralds(int value)
2440 int font_nr = FONT_TEXT_2;
2441 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2443 if (PANEL_DEACTIVATED(game.panel.gems))
2446 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2449 void DrawGameValue_Dynamite(int value)
2451 int font_nr = FONT_TEXT_2;
2452 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2454 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2457 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2460 void DrawGameValue_Score(int value)
2462 int font_nr = FONT_TEXT_2;
2463 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2465 if (PANEL_DEACTIVATED(game.panel.score))
2468 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2471 void DrawGameValue_Time(int value)
2473 int font1_nr = FONT_TEXT_2;
2475 int font2_nr = FONT_TEXT_1;
2477 int font2_nr = FONT_LEVEL_NUMBER;
2479 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2480 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2482 if (PANEL_DEACTIVATED(game.panel.time))
2485 /* clear background if value just changed its size */
2486 if (value == 999 || value == 1000)
2487 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2490 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2492 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2495 void DrawGameValue_Level(int value)
2497 int font1_nr = FONT_TEXT_2;
2499 int font2_nr = FONT_TEXT_1;
2501 int font2_nr = FONT_LEVEL_NUMBER;
2504 if (PANEL_DEACTIVATED(game.panel.level))
2508 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2510 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2513 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2515 int base_key_graphic = EL_KEY_1;
2518 if (PANEL_DEACTIVATED(game.panel.keys))
2521 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2522 base_key_graphic = EL_EM_KEY_1;
2524 /* currently only 4 of 8 possible keys are displayed */
2525 for (i = 0; i < STD_NUM_KEYS; i++)
2527 int x = XX_KEYS + i * MINI_TILEX;
2531 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2533 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2534 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2540 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2543 int key[MAX_NUM_KEYS];
2546 /* prevent EM engine from updating time/score values parallel to GameWon() */
2547 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2548 local_player->LevelSolved)
2551 for (i = 0; i < MAX_NUM_KEYS; i++)
2552 key[i] = key_bits & (1 << i);
2554 DrawGameValue_Level(level_nr);
2556 DrawGameValue_Emeralds(emeralds);
2557 DrawGameValue_Dynamite(dynamite);
2558 DrawGameValue_Score(score);
2559 DrawGameValue_Time(time);
2561 DrawGameValue_Keys(key);
2564 void UpdateGameDoorValues()
2566 UpdateGameControlValues();
2569 void DrawGameDoorValues()
2571 DisplayGameControlValues();
2574 void DrawGameDoorValues_OLD()
2576 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2577 int dynamite_value = 0;
2578 int score_value = (local_player->LevelSolved ? local_player->score_final :
2579 local_player->score);
2580 int gems_value = local_player->gems_still_needed;
2584 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2586 DrawGameDoorValues_EM();
2591 if (game.centered_player_nr == -1)
2593 for (i = 0; i < MAX_PLAYERS; i++)
2595 for (j = 0; j < MAX_NUM_KEYS; j++)
2596 if (stored_player[i].key[j])
2597 key_bits |= (1 << j);
2599 dynamite_value += stored_player[i].inventory_size;
2604 int player_nr = game.centered_player_nr;
2606 for (i = 0; i < MAX_NUM_KEYS; i++)
2607 if (stored_player[player_nr].key[i])
2608 key_bits |= (1 << i);
2610 dynamite_value = stored_player[player_nr].inventory_size;
2613 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2619 =============================================================================
2621 -----------------------------------------------------------------------------
2622 initialize game engine due to level / tape version number
2623 =============================================================================
2626 static void InitGameEngine()
2628 int i, j, k, l, x, y;
2630 /* set game engine from tape file when re-playing, else from level file */
2631 game.engine_version = (tape.playing ? tape.engine_version :
2632 level.game_version);
2634 /* ---------------------------------------------------------------------- */
2635 /* set flags for bugs and changes according to active game engine version */
2636 /* ---------------------------------------------------------------------- */
2639 Summary of bugfix/change:
2640 Fixed handling for custom elements that change when pushed by the player.
2642 Fixed/changed in version:
2646 Before 3.1.0, custom elements that "change when pushing" changed directly
2647 after the player started pushing them (until then handled in "DigField()").
2648 Since 3.1.0, these custom elements are not changed until the "pushing"
2649 move of the element is finished (now handled in "ContinueMoving()").
2651 Affected levels/tapes:
2652 The first condition is generally needed for all levels/tapes before version
2653 3.1.0, which might use the old behaviour before it was changed; known tapes
2654 that are affected are some tapes from the level set "Walpurgis Gardens" by
2656 The second condition is an exception from the above case and is needed for
2657 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2658 above (including some development versions of 3.1.0), but before it was
2659 known that this change would break tapes like the above and was fixed in
2660 3.1.1, so that the changed behaviour was active although the engine version
2661 while recording maybe was before 3.1.0. There is at least one tape that is
2662 affected by this exception, which is the tape for the one-level set "Bug
2663 Machine" by Juergen Bonhagen.
2666 game.use_change_when_pushing_bug =
2667 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2669 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2670 tape.game_version < VERSION_IDENT(3,1,1,0)));
2673 Summary of bugfix/change:
2674 Fixed handling for blocking the field the player leaves when moving.
2676 Fixed/changed in version:
2680 Before 3.1.1, when "block last field when moving" was enabled, the field
2681 the player is leaving when moving was blocked for the time of the move,
2682 and was directly unblocked afterwards. This resulted in the last field
2683 being blocked for exactly one less than the number of frames of one player
2684 move. Additionally, even when blocking was disabled, the last field was
2685 blocked for exactly one frame.
2686 Since 3.1.1, due to changes in player movement handling, the last field
2687 is not blocked at all when blocking is disabled. When blocking is enabled,
2688 the last field is blocked for exactly the number of frames of one player
2689 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2690 last field is blocked for exactly one more than the number of frames of
2693 Affected levels/tapes:
2694 (!!! yet to be determined -- probably many !!!)
2697 game.use_block_last_field_bug =
2698 (game.engine_version < VERSION_IDENT(3,1,1,0));
2701 Summary of bugfix/change:
2702 Changed behaviour of CE changes with multiple changes per single frame.
2704 Fixed/changed in version:
2708 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2709 This resulted in race conditions where CEs seem to behave strange in some
2710 situations (where triggered CE changes were just skipped because there was
2711 already a CE change on that tile in the playfield in that engine frame).
2712 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2713 (The number of changes per frame must be limited in any case, because else
2714 it is easily possible to define CE changes that would result in an infinite
2715 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2716 should be set large enough so that it would only be reached in cases where
2717 the corresponding CE change conditions run into a loop. Therefore, it seems
2718 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2719 maximal number of change pages for custom elements.)
2721 Affected levels/tapes:
2725 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2726 game.max_num_changes_per_frame = 1;
2728 game.max_num_changes_per_frame =
2729 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2732 /* ---------------------------------------------------------------------- */
2734 /* default scan direction: scan playfield from top/left to bottom/right */
2735 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2737 /* dynamically adjust element properties according to game engine version */
2738 InitElementPropertiesEngine(game.engine_version);
2741 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2742 printf(" tape version == %06d [%s] [file: %06d]\n",
2743 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2745 printf(" => game.engine_version == %06d\n", game.engine_version);
2748 /* ---------- initialize player's initial move delay --------------------- */
2750 /* dynamically adjust player properties according to level information */
2751 for (i = 0; i < MAX_PLAYERS; i++)
2752 game.initial_move_delay_value[i] =
2753 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2755 /* dynamically adjust player properties according to game engine version */
2756 for (i = 0; i < MAX_PLAYERS; i++)
2757 game.initial_move_delay[i] =
2758 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2759 game.initial_move_delay_value[i] : 0);
2761 /* ---------- initialize player's initial push delay --------------------- */
2763 /* dynamically adjust player properties according to game engine version */
2764 game.initial_push_delay_value =
2765 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2767 /* ---------- initialize changing elements ------------------------------- */
2769 /* initialize changing elements information */
2770 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2772 struct ElementInfo *ei = &element_info[i];
2774 /* this pointer might have been changed in the level editor */
2775 ei->change = &ei->change_page[0];
2777 if (!IS_CUSTOM_ELEMENT(i))
2779 ei->change->target_element = EL_EMPTY_SPACE;
2780 ei->change->delay_fixed = 0;
2781 ei->change->delay_random = 0;
2782 ei->change->delay_frames = 1;
2785 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2787 ei->has_change_event[j] = FALSE;
2789 ei->event_page_nr[j] = 0;
2790 ei->event_page[j] = &ei->change_page[0];
2794 /* add changing elements from pre-defined list */
2795 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2797 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2798 struct ElementInfo *ei = &element_info[ch_delay->element];
2800 ei->change->target_element = ch_delay->target_element;
2801 ei->change->delay_fixed = ch_delay->change_delay;
2803 ei->change->pre_change_function = ch_delay->pre_change_function;
2804 ei->change->change_function = ch_delay->change_function;
2805 ei->change->post_change_function = ch_delay->post_change_function;
2807 ei->change->can_change = TRUE;
2808 ei->change->can_change_or_has_action = TRUE;
2810 ei->has_change_event[CE_DELAY] = TRUE;
2812 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2813 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2816 /* ---------- initialize internal run-time variables ------------- */
2818 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2820 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2822 for (j = 0; j < ei->num_change_pages; j++)
2824 ei->change_page[j].can_change_or_has_action =
2825 (ei->change_page[j].can_change |
2826 ei->change_page[j].has_action);
2830 /* add change events from custom element configuration */
2831 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2833 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2835 for (j = 0; j < ei->num_change_pages; j++)
2837 if (!ei->change_page[j].can_change_or_has_action)
2840 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2842 /* only add event page for the first page found with this event */
2843 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2845 ei->has_change_event[k] = TRUE;
2847 ei->event_page_nr[k] = j;
2848 ei->event_page[k] = &ei->change_page[j];
2854 /* ---------- initialize run-time trigger player and element ------------- */
2856 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2858 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2860 for (j = 0; j < ei->num_change_pages; j++)
2862 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2863 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2864 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2865 ei->change_page[j].actual_trigger_ce_value = 0;
2866 ei->change_page[j].actual_trigger_ce_score = 0;
2870 /* ---------- initialize trigger events ---------------------------------- */
2872 /* initialize trigger events information */
2873 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2874 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2875 trigger_events[i][j] = FALSE;
2877 /* add trigger events from element change event properties */
2878 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2880 struct ElementInfo *ei = &element_info[i];
2882 for (j = 0; j < ei->num_change_pages; j++)
2884 if (!ei->change_page[j].can_change_or_has_action)
2887 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2889 int trigger_element = ei->change_page[j].trigger_element;
2891 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2893 if (ei->change_page[j].has_event[k])
2895 if (IS_GROUP_ELEMENT(trigger_element))
2897 struct ElementGroupInfo *group =
2898 element_info[trigger_element].group;
2900 for (l = 0; l < group->num_elements_resolved; l++)
2901 trigger_events[group->element_resolved[l]][k] = TRUE;
2903 else if (trigger_element == EL_ANY_ELEMENT)
2904 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2905 trigger_events[l][k] = TRUE;
2907 trigger_events[trigger_element][k] = TRUE;
2914 /* ---------- initialize push delay -------------------------------------- */
2916 /* initialize push delay values to default */
2917 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2919 if (!IS_CUSTOM_ELEMENT(i))
2921 /* set default push delay values (corrected since version 3.0.7-1) */
2922 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2924 element_info[i].push_delay_fixed = 2;
2925 element_info[i].push_delay_random = 8;
2929 element_info[i].push_delay_fixed = 8;
2930 element_info[i].push_delay_random = 8;
2935 /* set push delay value for certain elements from pre-defined list */
2936 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2938 int e = push_delay_list[i].element;
2940 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2941 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2944 /* set push delay value for Supaplex elements for newer engine versions */
2945 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2947 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2949 if (IS_SP_ELEMENT(i))
2951 /* set SP push delay to just enough to push under a falling zonk */
2952 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2954 element_info[i].push_delay_fixed = delay;
2955 element_info[i].push_delay_random = 0;
2960 /* ---------- initialize move stepsize ----------------------------------- */
2962 /* initialize move stepsize values to default */
2963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2964 if (!IS_CUSTOM_ELEMENT(i))
2965 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2967 /* set move stepsize value for certain elements from pre-defined list */
2968 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2970 int e = move_stepsize_list[i].element;
2972 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2975 /* ---------- initialize collect score ----------------------------------- */
2977 /* initialize collect score values for custom elements from initial value */
2978 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2979 if (IS_CUSTOM_ELEMENT(i))
2980 element_info[i].collect_score = element_info[i].collect_score_initial;
2982 /* ---------- initialize collect count ----------------------------------- */
2984 /* initialize collect count values for non-custom elements */
2985 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986 if (!IS_CUSTOM_ELEMENT(i))
2987 element_info[i].collect_count_initial = 0;
2989 /* add collect count values for all elements from pre-defined list */
2990 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2991 element_info[collect_count_list[i].element].collect_count_initial =
2992 collect_count_list[i].count;
2994 /* ---------- initialize access direction -------------------------------- */
2996 /* initialize access direction values to default (access from every side) */
2997 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2998 if (!IS_CUSTOM_ELEMENT(i))
2999 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3001 /* set access direction value for certain elements from pre-defined list */
3002 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3003 element_info[access_direction_list[i].element].access_direction =
3004 access_direction_list[i].direction;
3006 /* ---------- initialize explosion content ------------------------------- */
3007 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3009 if (IS_CUSTOM_ELEMENT(i))
3012 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3014 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3016 element_info[i].content.e[x][y] =
3017 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3018 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3019 i == EL_PLAYER_3 ? EL_EMERALD :
3020 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3021 i == EL_MOLE ? EL_EMERALD_RED :
3022 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3023 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3024 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3025 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3026 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3027 i == EL_WALL_EMERALD ? EL_EMERALD :
3028 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3029 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3030 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3031 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3032 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3033 i == EL_WALL_PEARL ? EL_PEARL :
3034 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3039 /* ---------- initialize recursion detection ------------------------------ */
3040 recursion_loop_depth = 0;
3041 recursion_loop_detected = FALSE;
3042 recursion_loop_element = EL_UNDEFINED;
3044 /* ---------- initialize graphics engine ---------------------------------- */
3045 game.scroll_delay_value =
3046 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3047 setup.scroll_delay ? setup.scroll_delay_value : 0);
3048 game.scroll_delay_value =
3049 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3052 int get_num_special_action(int element, int action_first, int action_last)
3054 int num_special_action = 0;
3057 for (i = action_first; i <= action_last; i++)
3059 boolean found = FALSE;
3061 for (j = 0; j < NUM_DIRECTIONS; j++)
3062 if (el_act_dir2img(element, i, j) !=
3063 el_act_dir2img(element, ACTION_DEFAULT, j))
3067 num_special_action++;
3072 return num_special_action;
3077 =============================================================================
3079 -----------------------------------------------------------------------------
3080 initialize and start new game
3081 =============================================================================
3086 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3087 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3088 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3090 boolean do_fading = (game_status == GAME_MODE_MAIN);
3094 game_status = GAME_MODE_PLAYING;
3097 InitGameControlValues();
3099 /* don't play tapes over network */
3100 network_playing = (options.network && !tape.playing);
3102 for (i = 0; i < MAX_PLAYERS; i++)
3104 struct PlayerInfo *player = &stored_player[i];
3106 player->index_nr = i;
3107 player->index_bit = (1 << i);
3108 player->element_nr = EL_PLAYER_1 + i;
3110 player->present = FALSE;
3111 player->active = FALSE;
3112 player->killed = FALSE;
3115 player->effective_action = 0;
3116 player->programmed_action = 0;
3119 player->score_final = 0;
3121 player->gems_still_needed = level.gems_needed;
3122 player->sokobanfields_still_needed = 0;
3123 player->lights_still_needed = 0;
3124 player->friends_still_needed = 0;
3126 for (j = 0; j < MAX_NUM_KEYS; j++)
3127 player->key[j] = FALSE;
3129 player->num_white_keys = 0;
3131 player->dynabomb_count = 0;
3132 player->dynabomb_size = 1;
3133 player->dynabombs_left = 0;
3134 player->dynabomb_xl = FALSE;
3136 player->MovDir = MV_NONE;
3139 player->GfxDir = MV_NONE;
3140 player->GfxAction = ACTION_DEFAULT;
3142 player->StepFrame = 0;
3144 player->use_murphy = FALSE;
3145 player->artwork_element =
3146 (level.use_artwork_element[i] ? level.artwork_element[i] :
3147 player->element_nr);
3149 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3150 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3152 player->gravity = level.initial_player_gravity[i];
3154 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3156 player->actual_frame_counter = 0;
3158 player->step_counter = 0;
3160 player->last_move_dir = MV_NONE;
3162 player->is_active = FALSE;
3164 player->is_waiting = FALSE;
3165 player->is_moving = FALSE;
3166 player->is_auto_moving = FALSE;
3167 player->is_digging = FALSE;
3168 player->is_snapping = FALSE;
3169 player->is_collecting = FALSE;
3170 player->is_pushing = FALSE;
3171 player->is_switching = FALSE;
3172 player->is_dropping = FALSE;
3173 player->is_dropping_pressed = FALSE;
3175 player->is_bored = FALSE;
3176 player->is_sleeping = FALSE;
3178 player->frame_counter_bored = -1;
3179 player->frame_counter_sleeping = -1;
3181 player->anim_delay_counter = 0;
3182 player->post_delay_counter = 0;
3184 player->dir_waiting = MV_NONE;
3185 player->action_waiting = ACTION_DEFAULT;
3186 player->last_action_waiting = ACTION_DEFAULT;
3187 player->special_action_bored = ACTION_DEFAULT;
3188 player->special_action_sleeping = ACTION_DEFAULT;
3190 player->switch_x = -1;
3191 player->switch_y = -1;
3193 player->drop_x = -1;
3194 player->drop_y = -1;
3196 player->show_envelope = 0;
3198 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3200 player->push_delay = -1; /* initialized when pushing starts */
3201 player->push_delay_value = game.initial_push_delay_value;
3203 player->drop_delay = 0;
3204 player->drop_pressed_delay = 0;
3206 player->last_jx = -1;
3207 player->last_jy = -1;
3211 player->shield_normal_time_left = 0;
3212 player->shield_deadly_time_left = 0;
3214 player->inventory_infinite_element = EL_UNDEFINED;
3215 player->inventory_size = 0;
3217 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3218 SnapField(player, 0, 0);
3220 player->LevelSolved = FALSE;
3221 player->GameOver = FALSE;
3223 player->LevelSolved_GameWon = FALSE;
3224 player->LevelSolved_GameEnd = FALSE;
3225 player->LevelSolved_PanelOff = FALSE;
3226 player->LevelSolved_SaveTape = FALSE;
3227 player->LevelSolved_SaveScore = FALSE;
3230 network_player_action_received = FALSE;
3232 #if defined(NETWORK_AVALIABLE)
3233 /* initial null action */
3234 if (network_playing)
3235 SendToServer_MovePlayer(MV_NONE);
3244 TimeLeft = level.time;
3247 ScreenMovDir = MV_NONE;
3251 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3253 AllPlayersGone = FALSE;
3255 game.yamyam_content_nr = 0;
3256 game.robot_wheel_active = FALSE;
3257 game.magic_wall_active = FALSE;
3258 game.magic_wall_time_left = 0;
3259 game.light_time_left = 0;
3260 game.timegate_time_left = 0;
3261 game.switchgate_pos = 0;
3262 game.wind_direction = level.wind_direction_initial;
3264 #if !USE_PLAYER_GRAVITY
3265 game.gravity = FALSE;
3266 game.explosions_delayed = TRUE;
3269 game.lenses_time_left = 0;
3270 game.magnify_time_left = 0;
3272 game.ball_state = level.ball_state_initial;
3273 game.ball_content_nr = 0;
3275 game.envelope_active = FALSE;
3277 /* set focus to local player for network games, else to all players */
3278 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3279 game.centered_player_nr_next = game.centered_player_nr;
3280 game.set_centered_player = FALSE;
3282 if (network_playing && tape.recording)
3284 /* store client dependent player focus when recording network games */
3285 tape.centered_player_nr_next = game.centered_player_nr_next;
3286 tape.set_centered_player = TRUE;
3289 for (i = 0; i < NUM_BELTS; i++)
3291 game.belt_dir[i] = MV_NONE;
3292 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3295 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3296 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3298 SCAN_PLAYFIELD(x, y)
3300 Feld[x][y] = level.field[x][y];
3301 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3302 ChangeDelay[x][y] = 0;
3303 ChangePage[x][y] = -1;
3304 #if USE_NEW_CUSTOM_VALUE
3305 CustomValue[x][y] = 0; /* initialized in InitField() */
3307 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3309 WasJustMoving[x][y] = 0;
3310 WasJustFalling[x][y] = 0;
3311 CheckCollision[x][y] = 0;
3312 CheckImpact[x][y] = 0;
3314 Pushed[x][y] = FALSE;
3316 ChangeCount[x][y] = 0;
3317 ChangeEvent[x][y] = -1;
3319 ExplodePhase[x][y] = 0;
3320 ExplodeDelay[x][y] = 0;
3321 ExplodeField[x][y] = EX_TYPE_NONE;
3323 RunnerVisit[x][y] = 0;
3324 PlayerVisit[x][y] = 0;
3327 GfxRandom[x][y] = INIT_GFX_RANDOM();
3328 GfxElement[x][y] = EL_UNDEFINED;
3329 GfxAction[x][y] = ACTION_DEFAULT;
3330 GfxDir[x][y] = MV_NONE;
3333 SCAN_PLAYFIELD(x, y)
3335 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3337 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3339 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3342 InitField(x, y, TRUE);
3347 for (i = 0; i < MAX_PLAYERS; i++)
3349 struct PlayerInfo *player = &stored_player[i];
3351 /* set number of special actions for bored and sleeping animation */
3352 player->num_special_action_bored =
3353 get_num_special_action(player->artwork_element,
3354 ACTION_BORING_1, ACTION_BORING_LAST);
3355 player->num_special_action_sleeping =
3356 get_num_special_action(player->artwork_element,
3357 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3360 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3361 emulate_sb ? EMU_SOKOBAN :
3362 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3364 #if USE_NEW_ALL_SLIPPERY
3365 /* initialize type of slippery elements */
3366 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3368 if (!IS_CUSTOM_ELEMENT(i))
3370 /* default: elements slip down either to the left or right randomly */
3371 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3373 /* SP style elements prefer to slip down on the left side */
3374 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3375 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3377 /* BD style elements prefer to slip down on the left side */
3378 if (game.emulation == EMU_BOULDERDASH)
3379 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3384 /* initialize explosion and ignition delay */
3385 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387 if (!IS_CUSTOM_ELEMENT(i))
3390 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3391 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3392 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3393 int last_phase = (num_phase + 1) * delay;
3394 int half_phase = (num_phase / 2) * delay;
3396 element_info[i].explosion_delay = last_phase - 1;
3397 element_info[i].ignition_delay = half_phase;
3399 if (i == EL_BLACK_ORB)
3400 element_info[i].ignition_delay = 1;
3404 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3405 element_info[i].explosion_delay = 1;
3407 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3408 element_info[i].ignition_delay = 1;
3412 /* correct non-moving belts to start moving left */
3413 for (i = 0; i < NUM_BELTS; i++)
3414 if (game.belt_dir[i] == MV_NONE)
3415 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3417 /* check if any connected player was not found in playfield */
3418 for (i = 0; i < MAX_PLAYERS; i++)
3420 struct PlayerInfo *player = &stored_player[i];
3422 if (player->connected && !player->present)
3424 for (j = 0; j < MAX_PLAYERS; j++)
3426 struct PlayerInfo *some_player = &stored_player[j];
3427 int jx = some_player->jx, jy = some_player->jy;
3429 /* assign first free player found that is present in the playfield */
3430 if (some_player->present && !some_player->connected)
3432 player->present = TRUE;
3433 player->active = TRUE;
3435 some_player->present = FALSE;
3436 some_player->active = FALSE;
3438 player->artwork_element = some_player->artwork_element;
3440 player->block_last_field = some_player->block_last_field;
3441 player->block_delay_adjustment = some_player->block_delay_adjustment;
3443 StorePlayer[jx][jy] = player->element_nr;
3444 player->jx = player->last_jx = jx;
3445 player->jy = player->last_jy = jy;
3455 /* when playing a tape, eliminate all players who do not participate */
3457 for (i = 0; i < MAX_PLAYERS; i++)
3459 if (stored_player[i].active && !tape.player_participates[i])
3461 struct PlayerInfo *player = &stored_player[i];
3462 int jx = player->jx, jy = player->jy;
3464 player->active = FALSE;
3465 StorePlayer[jx][jy] = 0;
3466 Feld[jx][jy] = EL_EMPTY;
3470 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3472 /* when in single player mode, eliminate all but the first active player */
3474 for (i = 0; i < MAX_PLAYERS; i++)
3476 if (stored_player[i].active)
3478 for (j = i + 1; j < MAX_PLAYERS; j++)
3480 if (stored_player[j].active)
3482 struct PlayerInfo *player = &stored_player[j];
3483 int jx = player->jx, jy = player->jy;
3485 player->active = FALSE;
3486 player->present = FALSE;
3488 StorePlayer[jx][jy] = 0;
3489 Feld[jx][jy] = EL_EMPTY;
3496 /* when recording the game, store which players take part in the game */
3499 for (i = 0; i < MAX_PLAYERS; i++)
3500 if (stored_player[i].active)
3501 tape.player_participates[i] = TRUE;
3506 for (i = 0; i < MAX_PLAYERS; i++)
3508 struct PlayerInfo *player = &stored_player[i];
3510 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3515 if (local_player == player)
3516 printf("Player %d is local player.\n", i+1);
3520 if (BorderElement == EL_EMPTY)
3523 SBX_Right = lev_fieldx - SCR_FIELDX;
3525 SBY_Lower = lev_fieldy - SCR_FIELDY;
3530 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3532 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3535 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3536 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3538 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3539 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3541 /* if local player not found, look for custom element that might create
3542 the player (make some assumptions about the right custom element) */
3543 if (!local_player->present)
3545 int start_x = 0, start_y = 0;
3546 int found_rating = 0;
3547 int found_element = EL_UNDEFINED;
3548 int player_nr = local_player->index_nr;
3550 SCAN_PLAYFIELD(x, y)
3552 int element = Feld[x][y];
3557 if (level.use_start_element[player_nr] &&
3558 level.start_element[player_nr] == element &&
3565 found_element = element;
3568 if (!IS_CUSTOM_ELEMENT(element))
3571 if (CAN_CHANGE(element))
3573 for (i = 0; i < element_info[element].num_change_pages; i++)
3575 /* check for player created from custom element as single target */
3576 content = element_info[element].change_page[i].target_element;
3577 is_player = ELEM_IS_PLAYER(content);
3579 if (is_player && (found_rating < 3 ||
3580 (found_rating == 3 && element < found_element)))
3586 found_element = element;
3591 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3593 /* check for player created from custom element as explosion content */
3594 content = element_info[element].content.e[xx][yy];
3595 is_player = ELEM_IS_PLAYER(content);
3597 if (is_player && (found_rating < 2 ||
3598 (found_rating == 2 && element < found_element)))
3600 start_x = x + xx - 1;
3601 start_y = y + yy - 1;
3604 found_element = element;
3607 if (!CAN_CHANGE(element))
3610 for (i = 0; i < element_info[element].num_change_pages; i++)
3612 /* check for player created from custom element as extended target */
3614 element_info[element].change_page[i].target_content.e[xx][yy];
3616 is_player = ELEM_IS_PLAYER(content);
3618 if (is_player && (found_rating < 1 ||
3619 (found_rating == 1 && element < found_element)))
3621 start_x = x + xx - 1;
3622 start_y = y + yy - 1;
3625 found_element = element;
3631 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3632 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3635 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3636 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3641 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3642 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3643 local_player->jx - MIDPOSX);
3645 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3646 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3647 local_player->jy - MIDPOSY);
3650 /* do not use PLAYING mask for fading out from main screen */
3651 game_status = GAME_MODE_MAIN;
3655 if (!game.restart_level)
3656 CloseDoor(DOOR_CLOSE_1);
3659 if (level_editor_test_game)
3660 FadeSkipNextFadeIn();
3662 FadeSetEnterScreen();
3664 if (level_editor_test_game)
3665 fading = fading_none;
3667 fading = menu.destination;
3671 FadeOut(REDRAW_FIELD);
3674 FadeOut(REDRAW_FIELD);
3677 game_status = GAME_MODE_PLAYING;
3679 /* !!! FIX THIS (START) !!! */
3680 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3682 InitGameEngine_EM();
3684 /* blit playfield from scroll buffer to normal back buffer for fading in */
3685 BlitScreenToBitmap_EM(backbuffer);
3692 /* after drawing the level, correct some elements */
3693 if (game.timegate_time_left == 0)
3694 CloseAllOpenTimegates();
3696 /* blit playfield from scroll buffer to normal back buffer for fading in */
3697 if (setup.soft_scrolling)
3698 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3700 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3702 /* !!! FIX THIS (END) !!! */
3705 FadeIn(REDRAW_FIELD);
3708 FadeIn(REDRAW_FIELD);
3713 if (!game.restart_level)
3715 /* copy default game door content to main double buffer */
3716 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3717 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3720 SetPanelBackground();
3721 SetDrawBackgroundMask(REDRAW_DOOR_1);
3723 UpdateGameDoorValues();
3724 DrawGameDoorValues();
3726 if (!game.restart_level)
3730 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3731 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3732 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3736 /* copy actual game door content to door double buffer for OpenDoor() */
3737 BlitBitmap(drawto, bitmap_db_door,
3738 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3740 OpenDoor(DOOR_OPEN_ALL);
3742 PlaySound(SND_GAME_STARTING);
3744 if (setup.sound_music)
3747 KeyboardAutoRepeatOffUnlessAutoplay();
3751 for (i = 0; i < MAX_PLAYERS; i++)
3752 printf("Player %d %sactive.\n",
3753 i + 1, (stored_player[i].active ? "" : "not "));
3764 game.restart_level = FALSE;
3767 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3769 /* this is used for non-R'n'D game engines to update certain engine values */
3771 /* needed to determine if sounds are played within the visible screen area */
3772 scroll_x = actual_scroll_x;
3773 scroll_y = actual_scroll_y;
3776 void InitMovDir(int x, int y)
3778 int i, element = Feld[x][y];
3779 static int xy[4][2] =
3786 static int direction[3][4] =
3788 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3789 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3790 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3799 Feld[x][y] = EL_BUG;
3800 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3803 case EL_SPACESHIP_RIGHT:
3804 case EL_SPACESHIP_UP:
3805 case EL_SPACESHIP_LEFT:
3806 case EL_SPACESHIP_DOWN:
3807 Feld[x][y] = EL_SPACESHIP;
3808 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3811 case EL_BD_BUTTERFLY_RIGHT:
3812 case EL_BD_BUTTERFLY_UP:
3813 case EL_BD_BUTTERFLY_LEFT:
3814 case EL_BD_BUTTERFLY_DOWN:
3815 Feld[x][y] = EL_BD_BUTTERFLY;
3816 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3819 case EL_BD_FIREFLY_RIGHT:
3820 case EL_BD_FIREFLY_UP:
3821 case EL_BD_FIREFLY_LEFT:
3822 case EL_BD_FIREFLY_DOWN:
3823 Feld[x][y] = EL_BD_FIREFLY;
3824 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3827 case EL_PACMAN_RIGHT:
3829 case EL_PACMAN_LEFT:
3830 case EL_PACMAN_DOWN:
3831 Feld[x][y] = EL_PACMAN;
3832 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3835 case EL_YAMYAM_LEFT:
3836 case EL_YAMYAM_RIGHT:
3838 case EL_YAMYAM_DOWN:
3839 Feld[x][y] = EL_YAMYAM;
3840 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3843 case EL_SP_SNIKSNAK:
3844 MovDir[x][y] = MV_UP;
3847 case EL_SP_ELECTRON:
3848 MovDir[x][y] = MV_LEFT;
3855 Feld[x][y] = EL_MOLE;
3856 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3860 if (IS_CUSTOM_ELEMENT(element))
3862 struct ElementInfo *ei = &element_info[element];
3863 int move_direction_initial = ei->move_direction_initial;
3864 int move_pattern = ei->move_pattern;
3866 if (move_direction_initial == MV_START_PREVIOUS)
3868 if (MovDir[x][y] != MV_NONE)
3871 move_direction_initial = MV_START_AUTOMATIC;
3874 if (move_direction_initial == MV_START_RANDOM)
3875 MovDir[x][y] = 1 << RND(4);
3876 else if (move_direction_initial & MV_ANY_DIRECTION)
3877 MovDir[x][y] = move_direction_initial;
3878 else if (move_pattern == MV_ALL_DIRECTIONS ||
3879 move_pattern == MV_TURNING_LEFT ||
3880 move_pattern == MV_TURNING_RIGHT ||
3881 move_pattern == MV_TURNING_LEFT_RIGHT ||
3882 move_pattern == MV_TURNING_RIGHT_LEFT ||
3883 move_pattern == MV_TURNING_RANDOM)
3884 MovDir[x][y] = 1 << RND(4);
3885 else if (move_pattern == MV_HORIZONTAL)
3886 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3887 else if (move_pattern == MV_VERTICAL)
3888 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3889 else if (move_pattern & MV_ANY_DIRECTION)
3890 MovDir[x][y] = element_info[element].move_pattern;
3891 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3892 move_pattern == MV_ALONG_RIGHT_SIDE)
3894 /* use random direction as default start direction */
3895 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3896 MovDir[x][y] = 1 << RND(4);
3898 for (i = 0; i < NUM_DIRECTIONS; i++)
3900 int x1 = x + xy[i][0];
3901 int y1 = y + xy[i][1];
3903 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3905 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3906 MovDir[x][y] = direction[0][i];
3908 MovDir[x][y] = direction[1][i];
3917 MovDir[x][y] = 1 << RND(4);
3919 if (element != EL_BUG &&
3920 element != EL_SPACESHIP &&
3921 element != EL_BD_BUTTERFLY &&
3922 element != EL_BD_FIREFLY)
3925 for (i = 0; i < NUM_DIRECTIONS; i++)
3927 int x1 = x + xy[i][0];
3928 int y1 = y + xy[i][1];
3930 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3932 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3934 MovDir[x][y] = direction[0][i];
3937 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3938 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3940 MovDir[x][y] = direction[1][i];
3949 GfxDir[x][y] = MovDir[x][y];
3952 void InitAmoebaNr(int x, int y)
3955 int group_nr = AmoebeNachbarNr(x, y);
3959 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3961 if (AmoebaCnt[i] == 0)
3969 AmoebaNr[x][y] = group_nr;
3970 AmoebaCnt[group_nr]++;
3971 AmoebaCnt2[group_nr]++;
3974 static void PlayerWins(struct PlayerInfo *player)
3976 player->LevelSolved = TRUE;
3977 player->GameOver = TRUE;
3979 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3980 level.native_em_level->lev->score : player->score);
3985 static int time, time_final;
3986 static int score, score_final;
3987 static int game_over_delay_1 = 0;
3988 static int game_over_delay_2 = 0;
3989 int game_over_delay_value_1 = 50;
3990 int game_over_delay_value_2 = 50;
3992 if (!local_player->LevelSolved_GameWon)
3996 /* do not start end game actions before the player stops moving (to exit) */
3997 if (local_player->MovPos)
4000 local_player->LevelSolved_GameWon = TRUE;
4001 local_player->LevelSolved_SaveTape = tape.recording;
4002 local_player->LevelSolved_SaveScore = !tape.playing;
4004 if (tape.auto_play) /* tape might already be stopped here */
4005 tape.auto_play_level_solved = TRUE;
4011 game_over_delay_1 = game_over_delay_value_1;
4012 game_over_delay_2 = game_over_delay_value_2;
4014 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4015 score = score_final = local_player->score_final;
4020 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4022 else if (level.time == 0 && TimePlayed < 999)
4025 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4028 local_player->score_final = score_final;
4030 if (level_editor_test_game)
4033 score = score_final;
4036 game_panel_controls[GAME_PANEL_TIME].value = time;
4037 game_panel_controls[GAME_PANEL_SCORE].value = score;
4039 DisplayGameControlValues();
4041 DrawGameValue_Time(time);
4042 DrawGameValue_Score(score);
4046 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4048 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4050 /* close exit door after last player */
4051 if ((AllPlayersGone &&
4052 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4053 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4054 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4055 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4056 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4058 int element = Feld[ExitX][ExitY];
4061 if (element == EL_EM_EXIT_OPEN ||
4062 element == EL_EM_STEEL_EXIT_OPEN)
4069 Feld[ExitX][ExitY] =
4070 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4071 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4072 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4073 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4074 EL_EM_STEEL_EXIT_CLOSING);
4076 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4080 /* player disappears */
4081 DrawLevelField(ExitX, ExitY);
4084 for (i = 0; i < MAX_PLAYERS; i++)
4086 struct PlayerInfo *player = &stored_player[i];
4088 if (player->present)
4090 RemovePlayer(player);
4092 /* player disappears */
4093 DrawLevelField(player->jx, player->jy);
4098 PlaySound(SND_GAME_WINNING);
4101 if (game_over_delay_1 > 0)
4103 game_over_delay_1--;
4108 if (time != time_final)
4110 int time_to_go = ABS(time_final - time);
4111 int time_count_dir = (time < time_final ? +1 : -1);
4112 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4114 time += time_count_steps * time_count_dir;
4115 score += time_count_steps * level.score[SC_TIME_BONUS];
4118 game_panel_controls[GAME_PANEL_TIME].value = time;
4119 game_panel_controls[GAME_PANEL_SCORE].value = score;
4121 DisplayGameControlValues();
4123 DrawGameValue_Time(time);
4124 DrawGameValue_Score(score);
4127 if (time == time_final)
4128 StopSound(SND_GAME_LEVELTIME_BONUS);
4129 else if (setup.sound_loops)
4130 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4132 PlaySound(SND_GAME_LEVELTIME_BONUS);
4137 local_player->LevelSolved_PanelOff = TRUE;
4139 if (game_over_delay_2 > 0)
4141 game_over_delay_2--;
4154 boolean raise_level = FALSE;
4156 local_player->LevelSolved_GameEnd = TRUE;
4158 CloseDoor(DOOR_CLOSE_1);
4160 if (local_player->LevelSolved_SaveTape)
4167 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4169 SaveTape(tape.level_nr); /* ask to save tape */
4173 if (level_editor_test_game)
4175 game_status = GAME_MODE_MAIN;
4178 DrawAndFadeInMainMenu(REDRAW_FIELD);
4186 if (!local_player->LevelSolved_SaveScore)
4189 FadeOut(REDRAW_FIELD);
4192 game_status = GAME_MODE_MAIN;
4194 DrawAndFadeInMainMenu(REDRAW_FIELD);
4199 if (level_nr == leveldir_current->handicap_level)
4201 leveldir_current->handicap_level++;
4202 SaveLevelSetup_SeriesInfo();
4205 if (level_nr < leveldir_current->last_level)
4206 raise_level = TRUE; /* advance to next level */
4208 if ((hi_pos = NewHiScore()) >= 0)
4210 game_status = GAME_MODE_SCORES;
4212 DrawHallOfFame(hi_pos);
4223 FadeOut(REDRAW_FIELD);
4226 game_status = GAME_MODE_MAIN;
4234 DrawAndFadeInMainMenu(REDRAW_FIELD);
4243 LoadScore(level_nr);
4245 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4246 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4249 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4251 if (local_player->score_final > highscore[k].Score)
4253 /* player has made it to the hall of fame */
4255 if (k < MAX_SCORE_ENTRIES - 1)
4257 int m = MAX_SCORE_ENTRIES - 1;
4260 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4261 if (strEqual(setup.player_name, highscore[l].Name))
4263 if (m == k) /* player's new highscore overwrites his old one */
4267 for (l = m; l > k; l--)
4269 strcpy(highscore[l].Name, highscore[l - 1].Name);
4270 highscore[l].Score = highscore[l - 1].Score;
4277 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4278 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4279 highscore[k].Score = local_player->score_final;
4285 else if (!strncmp(setup.player_name, highscore[k].Name,
4286 MAX_PLAYER_NAME_LEN))
4287 break; /* player already there with a higher score */
4293 SaveScore(level_nr);
4298 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4300 int element = Feld[x][y];
4301 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4302 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4303 int horiz_move = (dx != 0);
4304 int sign = (horiz_move ? dx : dy);
4305 int step = sign * element_info[element].move_stepsize;
4307 /* special values for move stepsize for spring and things on conveyor belt */
4310 if (CAN_FALL(element) &&
4311 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4312 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4313 else if (element == EL_SPRING)
4314 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4320 inline static int getElementMoveStepsize(int x, int y)
4322 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4325 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4327 if (player->GfxAction != action || player->GfxDir != dir)
4330 printf("Player frame reset! (%d => %d, %d => %d)\n",
4331 player->GfxAction, action, player->GfxDir, dir);
4334 player->GfxAction = action;
4335 player->GfxDir = dir;
4337 player->StepFrame = 0;
4341 #if USE_GFX_RESET_GFX_ANIMATION
4342 static void ResetGfxFrame(int x, int y, boolean redraw)
4344 int element = Feld[x][y];
4345 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4346 int last_gfx_frame = GfxFrame[x][y];
4348 if (graphic_info[graphic].anim_global_sync)
4349 GfxFrame[x][y] = FrameCounter;
4350 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4351 GfxFrame[x][y] = CustomValue[x][y];
4352 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4353 GfxFrame[x][y] = element_info[element].collect_score;
4354 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4355 GfxFrame[x][y] = ChangeDelay[x][y];
4357 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4358 DrawLevelGraphicAnimation(x, y, graphic);
4362 static void ResetGfxAnimation(int x, int y)
4364 GfxAction[x][y] = ACTION_DEFAULT;
4365 GfxDir[x][y] = MovDir[x][y];
4368 #if USE_GFX_RESET_GFX_ANIMATION
4369 ResetGfxFrame(x, y, FALSE);
4373 static void ResetRandomAnimationValue(int x, int y)
4375 GfxRandom[x][y] = INIT_GFX_RANDOM();
4378 void InitMovingField(int x, int y, int direction)
4380 int element = Feld[x][y];
4381 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4382 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4385 boolean is_moving_before, is_moving_after;
4387 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4390 /* check if element was/is moving or being moved before/after mode change */
4393 is_moving_before = (WasJustMoving[x][y] != 0);
4395 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4396 is_moving_before = WasJustMoving[x][y];
4399 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4401 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4403 /* reset animation only for moving elements which change direction of moving
4404 or which just started or stopped moving
4405 (else CEs with property "can move" / "not moving" are reset each frame) */
4406 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4408 if (is_moving_before != is_moving_after ||
4409 direction != MovDir[x][y])
4410 ResetGfxAnimation(x, y);
4412 if ((is_moving_before || is_moving_after) && !continues_moving)
4413 ResetGfxAnimation(x, y);
4416 if (!continues_moving)
4417 ResetGfxAnimation(x, y);
4420 MovDir[x][y] = direction;
4421 GfxDir[x][y] = direction;
4423 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4424 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4425 direction == MV_DOWN && CAN_FALL(element) ?
4426 ACTION_FALLING : ACTION_MOVING);
4428 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4429 ACTION_FALLING : ACTION_MOVING);
4432 /* this is needed for CEs with property "can move" / "not moving" */
4434 if (is_moving_after)
4436 if (Feld[newx][newy] == EL_EMPTY)
4437 Feld[newx][newy] = EL_BLOCKED;
4439 MovDir[newx][newy] = MovDir[x][y];
4441 #if USE_NEW_CUSTOM_VALUE
4442 CustomValue[newx][newy] = CustomValue[x][y];
4445 GfxFrame[newx][newy] = GfxFrame[x][y];
4446 GfxRandom[newx][newy] = GfxRandom[x][y];
4447 GfxAction[newx][newy] = GfxAction[x][y];
4448 GfxDir[newx][newy] = GfxDir[x][y];
4452 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4454 int direction = MovDir[x][y];
4455 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4456 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4462 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4464 int oldx = x, oldy = y;
4465 int direction = MovDir[x][y];
4467 if (direction == MV_LEFT)
4469 else if (direction == MV_RIGHT)
4471 else if (direction == MV_UP)
4473 else if (direction == MV_DOWN)
4476 *comes_from_x = oldx;
4477 *comes_from_y = oldy;
4480 int MovingOrBlocked2Element(int x, int y)
4482 int element = Feld[x][y];
4484 if (element == EL_BLOCKED)
4488 Blocked2Moving(x, y, &oldx, &oldy);
4489 return Feld[oldx][oldy];
4495 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4497 /* like MovingOrBlocked2Element(), but if element is moving
4498 and (x,y) is the field the moving element is just leaving,
4499 return EL_BLOCKED instead of the element value */
4500 int element = Feld[x][y];
4502 if (IS_MOVING(x, y))
4504 if (element == EL_BLOCKED)
4508 Blocked2Moving(x, y, &oldx, &oldy);
4509 return Feld[oldx][oldy];
4518 static void RemoveField(int x, int y)
4520 Feld[x][y] = EL_EMPTY;
4526 #if USE_NEW_CUSTOM_VALUE
4527 CustomValue[x][y] = 0;
4531 ChangeDelay[x][y] = 0;
4532 ChangePage[x][y] = -1;
4533 Pushed[x][y] = FALSE;
4536 ExplodeField[x][y] = EX_TYPE_NONE;
4539 GfxElement[x][y] = EL_UNDEFINED;
4540 GfxAction[x][y] = ACTION_DEFAULT;
4541 GfxDir[x][y] = MV_NONE;
4544 void RemoveMovingField(int x, int y)
4546 int oldx = x, oldy = y, newx = x, newy = y;
4547 int element = Feld[x][y];
4548 int next_element = EL_UNDEFINED;
4550 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4553 if (IS_MOVING(x, y))
4555 Moving2Blocked(x, y, &newx, &newy);
4557 if (Feld[newx][newy] != EL_BLOCKED)
4559 /* element is moving, but target field is not free (blocked), but
4560 already occupied by something different (example: acid pool);
4561 in this case, only remove the moving field, but not the target */
4563 RemoveField(oldx, oldy);
4565 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4567 DrawLevelField(oldx, oldy);
4572 else if (element == EL_BLOCKED)
4574 Blocked2Moving(x, y, &oldx, &oldy);
4575 if (!IS_MOVING(oldx, oldy))
4579 if (element == EL_BLOCKED &&
4580 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4581 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4582 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4583 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4584 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4585 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4586 next_element = get_next_element(Feld[oldx][oldy]);
4588 RemoveField(oldx, oldy);
4589 RemoveField(newx, newy);
4591 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4593 if (next_element != EL_UNDEFINED)
4594 Feld[oldx][oldy] = next_element;
4596 DrawLevelField(oldx, oldy);
4597 DrawLevelField(newx, newy);
4600 void DrawDynamite(int x, int y)
4602 int sx = SCREENX(x), sy = SCREENY(y);
4603 int graphic = el2img(Feld[x][y]);
4606 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4609 if (IS_WALKABLE_INSIDE(Back[x][y]))
4613 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4614 else if (Store[x][y])
4615 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4617 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4619 if (Back[x][y] || Store[x][y])
4620 DrawGraphicThruMask(sx, sy, graphic, frame);
4622 DrawGraphic(sx, sy, graphic, frame);
4625 void CheckDynamite(int x, int y)
4627 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4631 if (MovDelay[x][y] != 0)
4634 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4640 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4645 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4647 boolean num_checked_players = 0;
4650 for (i = 0; i < MAX_PLAYERS; i++)
4652 if (stored_player[i].active)
4654 int sx = stored_player[i].jx;
4655 int sy = stored_player[i].jy;
4657 if (num_checked_players == 0)
4664 *sx1 = MIN(*sx1, sx);
4665 *sy1 = MIN(*sy1, sy);
4666 *sx2 = MAX(*sx2, sx);
4667 *sy2 = MAX(*sy2, sy);
4670 num_checked_players++;
4675 static boolean checkIfAllPlayersFitToScreen_RND()
4677 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4679 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4681 return (sx2 - sx1 < SCR_FIELDX &&
4682 sy2 - sy1 < SCR_FIELDY);
4685 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4687 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4689 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4691 *sx = (sx1 + sx2) / 2;
4692 *sy = (sy1 + sy2) / 2;
4695 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4696 boolean center_screen, boolean quick_relocation)
4698 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4699 boolean no_delay = (tape.warp_forward);
4700 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4701 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4703 if (quick_relocation)
4705 int offset = game.scroll_delay_value;
4707 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4709 if (!level.shifted_relocation || center_screen)
4711 /* quick relocation (without scrolling), with centering of screen */
4713 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4714 x > SBX_Right + MIDPOSX ? SBX_Right :
4717 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4718 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4723 /* quick relocation (without scrolling), but do not center screen */
4725 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4726 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4729 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4730 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4733 int offset_x = x + (scroll_x - center_scroll_x);
4734 int offset_y = y + (scroll_y - center_scroll_y);
4736 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4737 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4738 offset_x - MIDPOSX);
4740 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4741 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4742 offset_y - MIDPOSY);
4747 /* quick relocation (without scrolling), inside visible screen area */
4749 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4750 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4751 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4753 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4754 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4755 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4757 /* don't scroll over playfield boundaries */
4758 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4759 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4761 /* don't scroll over playfield boundaries */
4762 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4763 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4766 RedrawPlayfield(TRUE, 0,0,0,0);
4771 int scroll_xx, scroll_yy;
4773 if (!level.shifted_relocation || center_screen)
4775 /* visible relocation (with scrolling), with centering of screen */
4777 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4778 x > SBX_Right + MIDPOSX ? SBX_Right :
4781 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4782 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4787 /* visible relocation (with scrolling), but do not center screen */
4789 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4790 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4793 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4794 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4797 int offset_x = x + (scroll_x - center_scroll_x);
4798 int offset_y = y + (scroll_y - center_scroll_y);
4800 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4801 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4802 offset_x - MIDPOSX);
4804 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4805 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4806 offset_y - MIDPOSY);
4811 /* visible relocation (with scrolling), with centering of screen */
4813 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4814 x > SBX_Right + MIDPOSX ? SBX_Right :
4817 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4818 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4822 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4824 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4827 int fx = FX, fy = FY;
4829 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4830 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4832 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4838 fx += dx * TILEX / 2;
4839 fy += dy * TILEY / 2;
4841 ScrollLevel(dx, dy);
4844 /* scroll in two steps of half tile size to make things smoother */
4845 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4847 Delay(wait_delay_value);
4849 /* scroll second step to align at full tile size */
4851 Delay(wait_delay_value);
4856 Delay(wait_delay_value);
4860 void RelocatePlayer(int jx, int jy, int el_player_raw)
4862 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4863 int player_nr = GET_PLAYER_NR(el_player);
4864 struct PlayerInfo *player = &stored_player[player_nr];
4865 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4866 boolean no_delay = (tape.warp_forward);
4867 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4868 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4869 int old_jx = player->jx;
4870 int old_jy = player->jy;
4871 int old_element = Feld[old_jx][old_jy];
4872 int element = Feld[jx][jy];
4873 boolean player_relocated = (old_jx != jx || old_jy != jy);
4875 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4876 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4877 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4878 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4879 int leave_side_horiz = move_dir_horiz;
4880 int leave_side_vert = move_dir_vert;
4881 int enter_side = enter_side_horiz | enter_side_vert;
4882 int leave_side = leave_side_horiz | leave_side_vert;
4884 if (player->GameOver) /* do not reanimate dead player */
4887 if (!player_relocated) /* no need to relocate the player */
4890 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4892 RemoveField(jx, jy); /* temporarily remove newly placed player */
4893 DrawLevelField(jx, jy);
4896 if (player->present)
4898 while (player->MovPos)
4900 ScrollPlayer(player, SCROLL_GO_ON);
4901 ScrollScreen(NULL, SCROLL_GO_ON);
4903 AdvanceFrameAndPlayerCounters(player->index_nr);
4908 Delay(wait_delay_value);
4911 DrawPlayer(player); /* needed here only to cleanup last field */
4912 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4914 player->is_moving = FALSE;
4917 if (IS_CUSTOM_ELEMENT(old_element))
4918 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4920 player->index_bit, leave_side);
4922 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4924 player->index_bit, leave_side);
4926 Feld[jx][jy] = el_player;
4927 InitPlayerField(jx, jy, el_player, TRUE);
4929 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4931 Feld[jx][jy] = element;
4932 InitField(jx, jy, FALSE);
4935 /* only visually relocate centered player */
4936 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4937 FALSE, level.instant_relocation);
4939 TestIfPlayerTouchesBadThing(jx, jy);
4940 TestIfPlayerTouchesCustomElement(jx, jy);
4942 if (IS_CUSTOM_ELEMENT(element))
4943 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4944 player->index_bit, enter_side);
4946 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4947 player->index_bit, enter_side);
4950 void Explode(int ex, int ey, int phase, int mode)
4956 /* !!! eliminate this variable !!! */
4957 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4959 if (game.explosions_delayed)
4961 ExplodeField[ex][ey] = mode;
4965 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4967 int center_element = Feld[ex][ey];
4968 int artwork_element, explosion_element; /* set these values later */
4971 /* --- This is only really needed (and now handled) in "Impact()". --- */
4972 /* do not explode moving elements that left the explode field in time */
4973 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4974 center_element == EL_EMPTY &&
4975 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4980 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4981 if (mode == EX_TYPE_NORMAL ||
4982 mode == EX_TYPE_CENTER ||
4983 mode == EX_TYPE_CROSS)
4984 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4987 /* remove things displayed in background while burning dynamite */
4988 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4991 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4993 /* put moving element to center field (and let it explode there) */
4994 center_element = MovingOrBlocked2Element(ex, ey);
4995 RemoveMovingField(ex, ey);
4996 Feld[ex][ey] = center_element;
4999 /* now "center_element" is finally determined -- set related values now */
5000 artwork_element = center_element; /* for custom player artwork */
5001 explosion_element = center_element; /* for custom player artwork */
5003 if (IS_PLAYER(ex, ey))
5005 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5007 artwork_element = stored_player[player_nr].artwork_element;
5009 if (level.use_explosion_element[player_nr])
5011 explosion_element = level.explosion_element[player_nr];
5012 artwork_element = explosion_element;
5017 if (mode == EX_TYPE_NORMAL ||
5018 mode == EX_TYPE_CENTER ||
5019 mode == EX_TYPE_CROSS)
5020 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5023 last_phase = element_info[explosion_element].explosion_delay + 1;
5025 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5027 int xx = x - ex + 1;
5028 int yy = y - ey + 1;
5031 if (!IN_LEV_FIELD(x, y) ||
5032 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5033 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5036 element = Feld[x][y];
5038 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5040 element = MovingOrBlocked2Element(x, y);
5042 if (!IS_EXPLOSION_PROOF(element))
5043 RemoveMovingField(x, y);
5046 /* indestructible elements can only explode in center (but not flames) */
5047 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5048 mode == EX_TYPE_BORDER)) ||
5049 element == EL_FLAMES)
5052 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5053 behaviour, for example when touching a yamyam that explodes to rocks
5054 with active deadly shield, a rock is created under the player !!! */
5055 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5057 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5058 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5059 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5061 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5064 if (IS_ACTIVE_BOMB(element))
5066 /* re-activate things under the bomb like gate or penguin */
5067 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5074 /* save walkable background elements while explosion on same tile */
5075 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5076 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5077 Back[x][y] = element;
5079 /* ignite explodable elements reached by other explosion */
5080 if (element == EL_EXPLOSION)
5081 element = Store2[x][y];
5083 if (AmoebaNr[x][y] &&
5084 (element == EL_AMOEBA_FULL ||
5085 element == EL_BD_AMOEBA ||
5086 element == EL_AMOEBA_GROWING))
5088 AmoebaCnt[AmoebaNr[x][y]]--;
5089 AmoebaCnt2[AmoebaNr[x][y]]--;
5094 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5096 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5098 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5100 if (PLAYERINFO(ex, ey)->use_murphy)
5101 Store[x][y] = EL_EMPTY;
5104 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5105 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5106 else if (ELEM_IS_PLAYER(center_element))
5107 Store[x][y] = EL_EMPTY;
5108 else if (center_element == EL_YAMYAM)
5109 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5110 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5111 Store[x][y] = element_info[center_element].content.e[xx][yy];
5113 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5114 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5115 otherwise) -- FIX THIS !!! */
5116 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5117 Store[x][y] = element_info[element].content.e[1][1];
5119 else if (!CAN_EXPLODE(element))
5120 Store[x][y] = element_info[element].content.e[1][1];
5123 Store[x][y] = EL_EMPTY;
5125 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5126 center_element == EL_AMOEBA_TO_DIAMOND)
5127 Store2[x][y] = element;
5129 Feld[x][y] = EL_EXPLOSION;
5130 GfxElement[x][y] = artwork_element;
5132 ExplodePhase[x][y] = 1;
5133 ExplodeDelay[x][y] = last_phase;
5138 if (center_element == EL_YAMYAM)
5139 game.yamyam_content_nr =
5140 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5152 GfxFrame[x][y] = 0; /* restart explosion animation */
5154 last_phase = ExplodeDelay[x][y];
5156 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5160 /* activate this even in non-DEBUG version until cause for crash in
5161 getGraphicAnimationFrame() (see below) is found and eliminated */
5167 /* this can happen if the player leaves an explosion just in time */
5168 if (GfxElement[x][y] == EL_UNDEFINED)
5169 GfxElement[x][y] = EL_EMPTY;
5171 if (GfxElement[x][y] == EL_UNDEFINED)
5174 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5175 printf("Explode(): This should never happen!\n");
5178 GfxElement[x][y] = EL_EMPTY;
5184 border_element = Store2[x][y];
5185 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5186 border_element = StorePlayer[x][y];
5188 if (phase == element_info[border_element].ignition_delay ||
5189 phase == last_phase)
5191 boolean border_explosion = FALSE;
5193 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5194 !PLAYER_EXPLOSION_PROTECTED(x, y))
5196 KillPlayerUnlessExplosionProtected(x, y);
5197 border_explosion = TRUE;
5199 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5201 Feld[x][y] = Store2[x][y];
5204 border_explosion = TRUE;
5206 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5208 AmoebeUmwandeln(x, y);
5210 border_explosion = TRUE;
5213 /* if an element just explodes due to another explosion (chain-reaction),
5214 do not immediately end the new explosion when it was the last frame of
5215 the explosion (as it would be done in the following "if"-statement!) */
5216 if (border_explosion && phase == last_phase)
5220 if (phase == last_phase)
5224 element = Feld[x][y] = Store[x][y];
5225 Store[x][y] = Store2[x][y] = 0;
5226 GfxElement[x][y] = EL_UNDEFINED;
5228 /* player can escape from explosions and might therefore be still alive */
5229 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5230 element <= EL_PLAYER_IS_EXPLODING_4)
5232 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5233 int explosion_element = EL_PLAYER_1 + player_nr;
5234 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5235 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5237 if (level.use_explosion_element[player_nr])
5238 explosion_element = level.explosion_element[player_nr];
5240 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5241 element_info[explosion_element].content.e[xx][yy]);
5244 /* restore probably existing indestructible background element */
5245 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5246 element = Feld[x][y] = Back[x][y];
5249 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5250 GfxDir[x][y] = MV_NONE;
5251 ChangeDelay[x][y] = 0;
5252 ChangePage[x][y] = -1;
5254 #if USE_NEW_CUSTOM_VALUE
5255 CustomValue[x][y] = 0;
5258 InitField_WithBug2(x, y, FALSE);
5260 DrawLevelField(x, y);
5262 TestIfElementTouchesCustomElement(x, y);
5264 if (GFX_CRUMBLED(element))
5265 DrawLevelFieldCrumbledSandNeighbours(x, y);
5267 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5268 StorePlayer[x][y] = 0;
5270 if (ELEM_IS_PLAYER(element))
5271 RelocatePlayer(x, y, element);
5273 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5275 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5276 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5279 DrawLevelFieldCrumbledSand(x, y);
5281 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5283 DrawLevelElement(x, y, Back[x][y]);
5284 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5286 else if (IS_WALKABLE_UNDER(Back[x][y]))
5288 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5289 DrawLevelElementThruMask(x, y, Back[x][y]);
5291 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5292 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5296 void DynaExplode(int ex, int ey)
5299 int dynabomb_element = Feld[ex][ey];
5300 int dynabomb_size = 1;
5301 boolean dynabomb_xl = FALSE;
5302 struct PlayerInfo *player;
5303 static int xy[4][2] =
5311 if (IS_ACTIVE_BOMB(dynabomb_element))
5313 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5314 dynabomb_size = player->dynabomb_size;
5315 dynabomb_xl = player->dynabomb_xl;
5316 player->dynabombs_left++;
5319 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5321 for (i = 0; i < NUM_DIRECTIONS; i++)
5323 for (j = 1; j <= dynabomb_size; j++)
5325 int x = ex + j * xy[i][0];
5326 int y = ey + j * xy[i][1];
5329 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5332 element = Feld[x][y];
5334 /* do not restart explosions of fields with active bombs */
5335 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5338 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5340 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5341 !IS_DIGGABLE(element) && !dynabomb_xl)
5347 void Bang(int x, int y)
5349 int element = MovingOrBlocked2Element(x, y);
5350 int explosion_type = EX_TYPE_NORMAL;
5352 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5354 struct PlayerInfo *player = PLAYERINFO(x, y);
5356 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5357 player->element_nr);
5359 if (level.use_explosion_element[player->index_nr])
5361 int explosion_element = level.explosion_element[player->index_nr];
5363 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5364 explosion_type = EX_TYPE_CROSS;
5365 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5366 explosion_type = EX_TYPE_CENTER;
5374 case EL_BD_BUTTERFLY:
5377 case EL_DARK_YAMYAM:
5381 RaiseScoreElement(element);
5384 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5385 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5386 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5387 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5388 case EL_DYNABOMB_INCREASE_NUMBER:
5389 case EL_DYNABOMB_INCREASE_SIZE:
5390 case EL_DYNABOMB_INCREASE_POWER:
5391 explosion_type = EX_TYPE_DYNA;
5394 case EL_DC_LANDMINE:
5396 case EL_EM_EXIT_OPEN:
5397 case EL_EM_STEEL_EXIT_OPEN:
5399 explosion_type = EX_TYPE_CENTER;
5404 case EL_LAMP_ACTIVE:
5405 case EL_AMOEBA_TO_DIAMOND:
5406 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5407 explosion_type = EX_TYPE_CENTER;
5411 if (element_info[element].explosion_type == EXPLODES_CROSS)
5412 explosion_type = EX_TYPE_CROSS;
5413 else if (element_info[element].explosion_type == EXPLODES_1X1)
5414 explosion_type = EX_TYPE_CENTER;
5418 if (explosion_type == EX_TYPE_DYNA)
5421 Explode(x, y, EX_PHASE_START, explosion_type);
5423 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5426 void SplashAcid(int x, int y)
5428 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5429 (!IN_LEV_FIELD(x - 1, y - 2) ||
5430 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5431 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5433 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5434 (!IN_LEV_FIELD(x + 1, y - 2) ||
5435 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5436 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5438 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5441 static void InitBeltMovement()
5443 static int belt_base_element[4] =
5445 EL_CONVEYOR_BELT_1_LEFT,
5446 EL_CONVEYOR_BELT_2_LEFT,
5447 EL_CONVEYOR_BELT_3_LEFT,
5448 EL_CONVEYOR_BELT_4_LEFT
5450 static int belt_base_active_element[4] =
5452 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5453 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5454 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5455 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5460 /* set frame order for belt animation graphic according to belt direction */
5461 for (i = 0; i < NUM_BELTS; i++)
5465 for (j = 0; j < NUM_BELT_PARTS; j++)
5467 int element = belt_base_active_element[belt_nr] + j;
5468 int graphic = el2img(element);
5470 if (game.belt_dir[i] == MV_LEFT)
5471 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5473 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5477 SCAN_PLAYFIELD(x, y)
5479 int element = Feld[x][y];
5481 for (i = 0; i < NUM_BELTS; i++)
5483 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5485 int e_belt_nr = getBeltNrFromBeltElement(element);
5488 if (e_belt_nr == belt_nr)
5490 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5492 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5499 static void ToggleBeltSwitch(int x, int y)
5501 static int belt_base_element[4] =
5503 EL_CONVEYOR_BELT_1_LEFT,
5504 EL_CONVEYOR_BELT_2_LEFT,
5505 EL_CONVEYOR_BELT_3_LEFT,
5506 EL_CONVEYOR_BELT_4_LEFT
5508 static int belt_base_active_element[4] =
5510 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5511 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5512 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5513 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5515 static int belt_base_switch_element[4] =
5517 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5518 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5519 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5520 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5522 static int belt_move_dir[4] =
5530 int element = Feld[x][y];
5531 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5532 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5533 int belt_dir = belt_move_dir[belt_dir_nr];
5536 if (!IS_BELT_SWITCH(element))
5539 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5540 game.belt_dir[belt_nr] = belt_dir;
5542 if (belt_dir_nr == 3)
5545 /* set frame order for belt animation graphic according to belt direction */
5546 for (i = 0; i < NUM_BELT_PARTS; i++)
5548 int element = belt_base_active_element[belt_nr] + i;
5549 int graphic = el2img(element);
5551 if (belt_dir == MV_LEFT)
5552 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5554 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5557 SCAN_PLAYFIELD(xx, yy)
5559 int element = Feld[xx][yy];
5561 if (IS_BELT_SWITCH(element))
5563 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5565 if (e_belt_nr == belt_nr)
5567 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5568 DrawLevelField(xx, yy);
5571 else if (IS_BELT(element) && belt_dir != MV_NONE)
5573 int e_belt_nr = getBeltNrFromBeltElement(element);
5575 if (e_belt_nr == belt_nr)
5577 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5579 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5580 DrawLevelField(xx, yy);
5583 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5585 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5587 if (e_belt_nr == belt_nr)
5589 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5591 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5592 DrawLevelField(xx, yy);
5598 static void ToggleSwitchgateSwitch(int x, int y)
5602 game.switchgate_pos = !game.switchgate_pos;
5604 SCAN_PLAYFIELD(xx, yy)
5606 int element = Feld[xx][yy];
5608 #if !USE_BOTH_SWITCHGATE_SWITCHES
5609 if (element == EL_SWITCHGATE_SWITCH_UP ||
5610 element == EL_SWITCHGATE_SWITCH_DOWN)
5612 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5613 DrawLevelField(xx, yy);
5615 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5616 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5618 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5619 DrawLevelField(xx, yy);
5622 if (element == EL_SWITCHGATE_SWITCH_UP)
5624 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5625 DrawLevelField(xx, yy);
5627 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5629 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5630 DrawLevelField(xx, yy);
5632 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5634 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5635 DrawLevelField(xx, yy);
5637 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5639 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5640 DrawLevelField(xx, yy);
5643 else if (element == EL_SWITCHGATE_OPEN ||
5644 element == EL_SWITCHGATE_OPENING)
5646 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5648 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5650 else if (element == EL_SWITCHGATE_CLOSED ||
5651 element == EL_SWITCHGATE_CLOSING)
5653 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5655 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5660 static int getInvisibleActiveFromInvisibleElement(int element)
5662 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5663 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5664 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5668 static int getInvisibleFromInvisibleActiveElement(int element)
5670 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5671 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5672 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5676 static void RedrawAllLightSwitchesAndInvisibleElements()
5680 SCAN_PLAYFIELD(x, y)
5682 int element = Feld[x][y];
5684 if (element == EL_LIGHT_SWITCH &&
5685 game.light_time_left > 0)
5687 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5688 DrawLevelField(x, y);
5690 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5691 game.light_time_left == 0)
5693 Feld[x][y] = EL_LIGHT_SWITCH;
5694 DrawLevelField(x, y);
5696 else if (element == EL_EMC_DRIPPER &&
5697 game.light_time_left > 0)
5699 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5700 DrawLevelField(x, y);
5702 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5703 game.light_time_left == 0)
5705 Feld[x][y] = EL_EMC_DRIPPER;
5706 DrawLevelField(x, y);
5708 else if (element == EL_INVISIBLE_STEELWALL ||
5709 element == EL_INVISIBLE_WALL ||
5710 element == EL_INVISIBLE_SAND)
5712 if (game.light_time_left > 0)
5713 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5715 DrawLevelField(x, y);
5717 /* uncrumble neighbour fields, if needed */
5718 if (element == EL_INVISIBLE_SAND)
5719 DrawLevelFieldCrumbledSandNeighbours(x, y);
5721 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5722 element == EL_INVISIBLE_WALL_ACTIVE ||
5723 element == EL_INVISIBLE_SAND_ACTIVE)
5725 if (game.light_time_left == 0)
5726 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5728 DrawLevelField(x, y);
5730 /* re-crumble neighbour fields, if needed */
5731 if (element == EL_INVISIBLE_SAND)
5732 DrawLevelFieldCrumbledSandNeighbours(x, y);
5737 static void RedrawAllInvisibleElementsForLenses()
5741 SCAN_PLAYFIELD(x, y)
5743 int element = Feld[x][y];
5745 if (element == EL_EMC_DRIPPER &&
5746 game.lenses_time_left > 0)
5748 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5749 DrawLevelField(x, y);
5751 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5752 game.lenses_time_left == 0)
5754 Feld[x][y] = EL_EMC_DRIPPER;
5755 DrawLevelField(x, y);
5757 else if (element == EL_INVISIBLE_STEELWALL ||
5758 element == EL_INVISIBLE_WALL ||
5759 element == EL_INVISIBLE_SAND)
5761 if (game.lenses_time_left > 0)
5762 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5764 DrawLevelField(x, y);
5766 /* uncrumble neighbour fields, if needed */
5767 if (element == EL_INVISIBLE_SAND)
5768 DrawLevelFieldCrumbledSandNeighbours(x, y);
5770 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5771 element == EL_INVISIBLE_WALL_ACTIVE ||
5772 element == EL_INVISIBLE_SAND_ACTIVE)
5774 if (game.lenses_time_left == 0)
5775 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5777 DrawLevelField(x, y);
5779 /* re-crumble neighbour fields, if needed */
5780 if (element == EL_INVISIBLE_SAND)
5781 DrawLevelFieldCrumbledSandNeighbours(x, y);
5786 static void RedrawAllInvisibleElementsForMagnifier()
5790 SCAN_PLAYFIELD(x, y)
5792 int element = Feld[x][y];
5794 if (element == EL_EMC_FAKE_GRASS &&
5795 game.magnify_time_left > 0)
5797 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5798 DrawLevelField(x, y);
5800 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5801 game.magnify_time_left == 0)
5803 Feld[x][y] = EL_EMC_FAKE_GRASS;
5804 DrawLevelField(x, y);
5806 else if (IS_GATE_GRAY(element) &&
5807 game.magnify_time_left > 0)
5809 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5810 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5811 IS_EM_GATE_GRAY(element) ?
5812 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5813 IS_EMC_GATE_GRAY(element) ?
5814 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5816 DrawLevelField(x, y);
5818 else if (IS_GATE_GRAY_ACTIVE(element) &&
5819 game.magnify_time_left == 0)
5821 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5822 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5823 IS_EM_GATE_GRAY_ACTIVE(element) ?
5824 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5825 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5826 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5828 DrawLevelField(x, y);
5833 static void ToggleLightSwitch(int x, int y)
5835 int element = Feld[x][y];
5837 game.light_time_left =
5838 (element == EL_LIGHT_SWITCH ?
5839 level.time_light * FRAMES_PER_SECOND : 0);
5841 RedrawAllLightSwitchesAndInvisibleElements();
5844 static void ActivateTimegateSwitch(int x, int y)
5848 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5850 SCAN_PLAYFIELD(xx, yy)
5852 int element = Feld[xx][yy];
5854 if (element == EL_TIMEGATE_CLOSED ||
5855 element == EL_TIMEGATE_CLOSING)
5857 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5858 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5862 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5864 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5865 DrawLevelField(xx, yy);
5872 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5873 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5875 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5879 void Impact(int x, int y)
5881 boolean last_line = (y == lev_fieldy - 1);
5882 boolean object_hit = FALSE;
5883 boolean impact = (last_line || object_hit);
5884 int element = Feld[x][y];
5885 int smashed = EL_STEELWALL;
5887 if (!last_line) /* check if element below was hit */
5889 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5892 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5893 MovDir[x][y + 1] != MV_DOWN ||
5894 MovPos[x][y + 1] <= TILEY / 2));
5896 /* do not smash moving elements that left the smashed field in time */
5897 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5898 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5901 #if USE_QUICKSAND_IMPACT_BUGFIX
5902 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5904 RemoveMovingField(x, y + 1);
5905 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5906 Feld[x][y + 2] = EL_ROCK;
5907 DrawLevelField(x, y + 2);
5912 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5914 RemoveMovingField(x, y + 1);
5915 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5916 Feld[x][y + 2] = EL_ROCK;
5917 DrawLevelField(x, y + 2);
5924 smashed = MovingOrBlocked2Element(x, y + 1);
5926 impact = (last_line || object_hit);
5929 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5931 SplashAcid(x, y + 1);
5935 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5936 /* only reset graphic animation if graphic really changes after impact */
5938 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5940 ResetGfxAnimation(x, y);
5941 DrawLevelField(x, y);
5944 if (impact && CAN_EXPLODE_IMPACT(element))
5949 else if (impact && element == EL_PEARL &&
5950 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5952 ResetGfxAnimation(x, y);
5954 Feld[x][y] = EL_PEARL_BREAKING;
5955 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5958 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5960 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5965 if (impact && element == EL_AMOEBA_DROP)
5967 if (object_hit && IS_PLAYER(x, y + 1))
5968 KillPlayerUnlessEnemyProtected(x, y + 1);
5969 else if (object_hit && smashed == EL_PENGUIN)
5973 Feld[x][y] = EL_AMOEBA_GROWING;
5974 Store[x][y] = EL_AMOEBA_WET;
5976 ResetRandomAnimationValue(x, y);
5981 if (object_hit) /* check which object was hit */
5983 if ((CAN_PASS_MAGIC_WALL(element) &&
5984 (smashed == EL_MAGIC_WALL ||
5985 smashed == EL_BD_MAGIC_WALL)) ||
5986 (CAN_PASS_DC_MAGIC_WALL(element) &&
5987 smashed == EL_DC_MAGIC_WALL))
5990 int activated_magic_wall =
5991 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5992 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5993 EL_DC_MAGIC_WALL_ACTIVE);
5995 /* activate magic wall / mill */
5996 SCAN_PLAYFIELD(xx, yy)
5998 if (Feld[xx][yy] == smashed)
5999 Feld[xx][yy] = activated_magic_wall;
6002 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6003 game.magic_wall_active = TRUE;
6005 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6006 SND_MAGIC_WALL_ACTIVATING :
6007 smashed == EL_BD_MAGIC_WALL ?
6008 SND_BD_MAGIC_WALL_ACTIVATING :
6009 SND_DC_MAGIC_WALL_ACTIVATING));
6012 if (IS_PLAYER(x, y + 1))
6014 if (CAN_SMASH_PLAYER(element))
6016 KillPlayerUnlessEnemyProtected(x, y + 1);
6020 else if (smashed == EL_PENGUIN)
6022 if (CAN_SMASH_PLAYER(element))
6028 else if (element == EL_BD_DIAMOND)
6030 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6036 else if (((element == EL_SP_INFOTRON ||
6037 element == EL_SP_ZONK) &&
6038 (smashed == EL_SP_SNIKSNAK ||
6039 smashed == EL_SP_ELECTRON ||
6040 smashed == EL_SP_DISK_ORANGE)) ||
6041 (element == EL_SP_INFOTRON &&
6042 smashed == EL_SP_DISK_YELLOW))
6047 else if (CAN_SMASH_EVERYTHING(element))
6049 if (IS_CLASSIC_ENEMY(smashed) ||
6050 CAN_EXPLODE_SMASHED(smashed))
6055 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6057 if (smashed == EL_LAMP ||
6058 smashed == EL_LAMP_ACTIVE)
6063 else if (smashed == EL_NUT)
6065 Feld[x][y + 1] = EL_NUT_BREAKING;
6066 PlayLevelSound(x, y, SND_NUT_BREAKING);
6067 RaiseScoreElement(EL_NUT);
6070 else if (smashed == EL_PEARL)
6072 ResetGfxAnimation(x, y);
6074 Feld[x][y + 1] = EL_PEARL_BREAKING;
6075 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6078 else if (smashed == EL_DIAMOND)
6080 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6081 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6084 else if (IS_BELT_SWITCH(smashed))
6086 ToggleBeltSwitch(x, y + 1);
6088 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6089 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6090 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6091 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6093 ToggleSwitchgateSwitch(x, y + 1);
6095 else if (smashed == EL_LIGHT_SWITCH ||
6096 smashed == EL_LIGHT_SWITCH_ACTIVE)
6098 ToggleLightSwitch(x, y + 1);
6103 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6106 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6108 CheckElementChangeBySide(x, y + 1, smashed, element,
6109 CE_SWITCHED, CH_SIDE_TOP);
6110 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6116 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6121 /* play sound of magic wall / mill */
6123 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6124 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6125 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6127 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6128 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6129 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6130 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6131 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6132 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6137 /* play sound of object that hits the ground */
6138 if (last_line || object_hit)
6139 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6142 inline static void TurnRoundExt(int x, int y)
6154 { 0, 0 }, { 0, 0 }, { 0, 0 },
6159 int left, right, back;
6163 { MV_DOWN, MV_UP, MV_RIGHT },
6164 { MV_UP, MV_DOWN, MV_LEFT },
6166 { MV_LEFT, MV_RIGHT, MV_DOWN },
6170 { MV_RIGHT, MV_LEFT, MV_UP }
6173 int element = Feld[x][y];
6174 int move_pattern = element_info[element].move_pattern;
6176 int old_move_dir = MovDir[x][y];
6177 int left_dir = turn[old_move_dir].left;
6178 int right_dir = turn[old_move_dir].right;
6179 int back_dir = turn[old_move_dir].back;
6181 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6182 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6183 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6184 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6186 int left_x = x + left_dx, left_y = y + left_dy;
6187 int right_x = x + right_dx, right_y = y + right_dy;
6188 int move_x = x + move_dx, move_y = y + move_dy;
6192 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6194 TestIfBadThingTouchesOtherBadThing(x, y);
6196 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6197 MovDir[x][y] = right_dir;
6198 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6199 MovDir[x][y] = left_dir;
6201 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6203 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6206 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6208 TestIfBadThingTouchesOtherBadThing(x, y);
6210 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6211 MovDir[x][y] = left_dir;
6212 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6213 MovDir[x][y] = right_dir;
6215 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6217 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6220 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6222 TestIfBadThingTouchesOtherBadThing(x, y);
6224 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6225 MovDir[x][y] = left_dir;
6226 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6227 MovDir[x][y] = right_dir;
6229 if (MovDir[x][y] != old_move_dir)
6232 else if (element == EL_YAMYAM)
6234 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6235 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6237 if (can_turn_left && can_turn_right)
6238 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6239 else if (can_turn_left)
6240 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6241 else if (can_turn_right)
6242 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6244 MovDir[x][y] = back_dir;
6246 MovDelay[x][y] = 16 + 16 * RND(3);
6248 else if (element == EL_DARK_YAMYAM)
6250 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6252 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6255 if (can_turn_left && can_turn_right)
6256 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6257 else if (can_turn_left)
6258 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6259 else if (can_turn_right)
6260 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6262 MovDir[x][y] = back_dir;
6264 MovDelay[x][y] = 16 + 16 * RND(3);
6266 else if (element == EL_PACMAN)
6268 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6269 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6271 if (can_turn_left && can_turn_right)
6272 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6273 else if (can_turn_left)
6274 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6275 else if (can_turn_right)
6276 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6278 MovDir[x][y] = back_dir;
6280 MovDelay[x][y] = 6 + RND(40);
6282 else if (element == EL_PIG)
6284 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6285 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6286 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6287 boolean should_turn_left, should_turn_right, should_move_on;
6289 int rnd = RND(rnd_value);
6291 should_turn_left = (can_turn_left &&
6293 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6294 y + back_dy + left_dy)));
6295 should_turn_right = (can_turn_right &&
6297 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6298 y + back_dy + right_dy)));
6299 should_move_on = (can_move_on &&
6302 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6303 y + move_dy + left_dy) ||
6304 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6305 y + move_dy + right_dy)));
6307 if (should_turn_left || should_turn_right || should_move_on)
6309 if (should_turn_left && should_turn_right && should_move_on)
6310 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6311 rnd < 2 * rnd_value / 3 ? right_dir :
6313 else if (should_turn_left && should_turn_right)
6314 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6315 else if (should_turn_left && should_move_on)
6316 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6317 else if (should_turn_right && should_move_on)
6318 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6319 else if (should_turn_left)
6320 MovDir[x][y] = left_dir;
6321 else if (should_turn_right)
6322 MovDir[x][y] = right_dir;
6323 else if (should_move_on)
6324 MovDir[x][y] = old_move_dir;
6326 else if (can_move_on && rnd > rnd_value / 8)
6327 MovDir[x][y] = old_move_dir;
6328 else if (can_turn_left && can_turn_right)
6329 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6330 else if (can_turn_left && rnd > rnd_value / 8)
6331 MovDir[x][y] = left_dir;
6332 else if (can_turn_right && rnd > rnd_value/8)
6333 MovDir[x][y] = right_dir;
6335 MovDir[x][y] = back_dir;
6337 xx = x + move_xy[MovDir[x][y]].dx;
6338 yy = y + move_xy[MovDir[x][y]].dy;
6340 if (!IN_LEV_FIELD(xx, yy) ||
6341 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6342 MovDir[x][y] = old_move_dir;
6346 else if (element == EL_DRAGON)
6348 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6349 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6350 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6352 int rnd = RND(rnd_value);
6354 if (can_move_on && rnd > rnd_value / 8)
6355 MovDir[x][y] = old_move_dir;
6356 else if (can_turn_left && can_turn_right)
6357 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6358 else if (can_turn_left && rnd > rnd_value / 8)
6359 MovDir[x][y] = left_dir;
6360 else if (can_turn_right && rnd > rnd_value / 8)
6361 MovDir[x][y] = right_dir;
6363 MovDir[x][y] = back_dir;
6365 xx = x + move_xy[MovDir[x][y]].dx;
6366 yy = y + move_xy[MovDir[x][y]].dy;
6368 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6369 MovDir[x][y] = old_move_dir;
6373 else if (element == EL_MOLE)
6375 boolean can_move_on =
6376 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6377 IS_AMOEBOID(Feld[move_x][move_y]) ||
6378 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6381 boolean can_turn_left =
6382 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6383 IS_AMOEBOID(Feld[left_x][left_y])));
6385 boolean can_turn_right =
6386 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6387 IS_AMOEBOID(Feld[right_x][right_y])));
6389 if (can_turn_left && can_turn_right)
6390 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6391 else if (can_turn_left)
6392 MovDir[x][y] = left_dir;
6394 MovDir[x][y] = right_dir;
6397 if (MovDir[x][y] != old_move_dir)
6400 else if (element == EL_BALLOON)
6402 MovDir[x][y] = game.wind_direction;
6405 else if (element == EL_SPRING)
6407 #if USE_NEW_SPRING_BUMPER
6408 if (MovDir[x][y] & MV_HORIZONTAL)
6410 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6411 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6413 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6414 ResetGfxAnimation(move_x, move_y);
6415 DrawLevelField(move_x, move_y);
6417 MovDir[x][y] = back_dir;
6419 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6420 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6421 MovDir[x][y] = MV_NONE;
6424 if (MovDir[x][y] & MV_HORIZONTAL &&
6425 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6426 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6427 MovDir[x][y] = MV_NONE;
6432 else if (element == EL_ROBOT ||
6433 element == EL_SATELLITE ||
6434 element == EL_PENGUIN ||
6435 element == EL_EMC_ANDROID)
6437 int attr_x = -1, attr_y = -1;
6448 for (i = 0; i < MAX_PLAYERS; i++)
6450 struct PlayerInfo *player = &stored_player[i];
6451 int jx = player->jx, jy = player->jy;
6453 if (!player->active)
6457 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6465 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6466 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6467 game.engine_version < VERSION_IDENT(3,1,0,0)))
6473 if (element == EL_PENGUIN)
6476 static int xy[4][2] =
6484 for (i = 0; i < NUM_DIRECTIONS; i++)
6486 int ex = x + xy[i][0];
6487 int ey = y + xy[i][1];
6489 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6490 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6491 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6492 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6501 MovDir[x][y] = MV_NONE;
6503 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6504 else if (attr_x > x)
6505 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6507 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6508 else if (attr_y > y)
6509 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6511 if (element == EL_ROBOT)
6515 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6516 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6517 Moving2Blocked(x, y, &newx, &newy);
6519 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6520 MovDelay[x][y] = 8 + 8 * !RND(3);
6522 MovDelay[x][y] = 16;
6524 else if (element == EL_PENGUIN)
6530 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6532 boolean first_horiz = RND(2);
6533 int new_move_dir = MovDir[x][y];
6536 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6537 Moving2Blocked(x, y, &newx, &newy);
6539 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6543 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6544 Moving2Blocked(x, y, &newx, &newy);
6546 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6549 MovDir[x][y] = old_move_dir;
6553 else if (element == EL_SATELLITE)
6559 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6561 boolean first_horiz = RND(2);
6562 int new_move_dir = MovDir[x][y];
6565 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6566 Moving2Blocked(x, y, &newx, &newy);
6568 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6572 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6573 Moving2Blocked(x, y, &newx, &newy);
6575 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6578 MovDir[x][y] = old_move_dir;
6582 else if (element == EL_EMC_ANDROID)
6584 static int check_pos[16] =
6586 -1, /* 0 => (invalid) */
6587 7, /* 1 => MV_LEFT */
6588 3, /* 2 => MV_RIGHT */
6589 -1, /* 3 => (invalid) */
6591 0, /* 5 => MV_LEFT | MV_UP */
6592 2, /* 6 => MV_RIGHT | MV_UP */
6593 -1, /* 7 => (invalid) */
6594 5, /* 8 => MV_DOWN */
6595 6, /* 9 => MV_LEFT | MV_DOWN */
6596 4, /* 10 => MV_RIGHT | MV_DOWN */
6597 -1, /* 11 => (invalid) */
6598 -1, /* 12 => (invalid) */
6599 -1, /* 13 => (invalid) */
6600 -1, /* 14 => (invalid) */
6601 -1, /* 15 => (invalid) */
6609 { -1, -1, MV_LEFT | MV_UP },
6611 { +1, -1, MV_RIGHT | MV_UP },
6612 { +1, 0, MV_RIGHT },
6613 { +1, +1, MV_RIGHT | MV_DOWN },
6615 { -1, +1, MV_LEFT | MV_DOWN },
6618 int start_pos, check_order;
6619 boolean can_clone = FALSE;
6622 /* check if there is any free field around current position */
6623 for (i = 0; i < 8; i++)
6625 int newx = x + check_xy[i].dx;
6626 int newy = y + check_xy[i].dy;
6628 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6636 if (can_clone) /* randomly find an element to clone */
6640 start_pos = check_pos[RND(8)];
6641 check_order = (RND(2) ? -1 : +1);
6643 for (i = 0; i < 8; i++)
6645 int pos_raw = start_pos + i * check_order;
6646 int pos = (pos_raw + 8) % 8;
6647 int newx = x + check_xy[pos].dx;
6648 int newy = y + check_xy[pos].dy;
6650 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6652 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6653 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6655 Store[x][y] = Feld[newx][newy];
6664 if (can_clone) /* randomly find a direction to move */
6668 start_pos = check_pos[RND(8)];
6669 check_order = (RND(2) ? -1 : +1);
6671 for (i = 0; i < 8; i++)
6673 int pos_raw = start_pos + i * check_order;
6674 int pos = (pos_raw + 8) % 8;
6675 int newx = x + check_xy[pos].dx;
6676 int newy = y + check_xy[pos].dy;
6677 int new_move_dir = check_xy[pos].dir;
6679 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6681 MovDir[x][y] = new_move_dir;
6682 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6691 if (can_clone) /* cloning and moving successful */
6694 /* cannot clone -- try to move towards player */
6696 start_pos = check_pos[MovDir[x][y] & 0x0f];
6697 check_order = (RND(2) ? -1 : +1);
6699 for (i = 0; i < 3; i++)
6701 /* first check start_pos, then previous/next or (next/previous) pos */
6702 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6703 int pos = (pos_raw + 8) % 8;
6704 int newx = x + check_xy[pos].dx;
6705 int newy = y + check_xy[pos].dy;
6706 int new_move_dir = check_xy[pos].dir;
6708 if (IS_PLAYER(newx, newy))
6711 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6713 MovDir[x][y] = new_move_dir;
6714 MovDelay[x][y] = level.android_move_time * 8 + 1;
6721 else if (move_pattern == MV_TURNING_LEFT ||
6722 move_pattern == MV_TURNING_RIGHT ||
6723 move_pattern == MV_TURNING_LEFT_RIGHT ||
6724 move_pattern == MV_TURNING_RIGHT_LEFT ||
6725 move_pattern == MV_TURNING_RANDOM ||
6726 move_pattern == MV_ALL_DIRECTIONS)
6728 boolean can_turn_left =
6729 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6730 boolean can_turn_right =
6731 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6733 if (element_info[element].move_stepsize == 0) /* "not moving" */
6736 if (move_pattern == MV_TURNING_LEFT)
6737 MovDir[x][y] = left_dir;
6738 else if (move_pattern == MV_TURNING_RIGHT)
6739 MovDir[x][y] = right_dir;
6740 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6741 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6742 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6743 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6744 else if (move_pattern == MV_TURNING_RANDOM)
6745 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6746 can_turn_right && !can_turn_left ? right_dir :
6747 RND(2) ? left_dir : right_dir);
6748 else if (can_turn_left && can_turn_right)
6749 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6750 else if (can_turn_left)
6751 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6752 else if (can_turn_right)
6753 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6755 MovDir[x][y] = back_dir;
6757 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6759 else if (move_pattern == MV_HORIZONTAL ||
6760 move_pattern == MV_VERTICAL)
6762 if (move_pattern & old_move_dir)
6763 MovDir[x][y] = back_dir;
6764 else if (move_pattern == MV_HORIZONTAL)
6765 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6766 else if (move_pattern == MV_VERTICAL)
6767 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6769 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6771 else if (move_pattern & MV_ANY_DIRECTION)
6773 MovDir[x][y] = move_pattern;
6774 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6776 else if (move_pattern & MV_WIND_DIRECTION)
6778 MovDir[x][y] = game.wind_direction;
6779 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6781 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6783 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6784 MovDir[x][y] = left_dir;
6785 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6786 MovDir[x][y] = right_dir;
6788 if (MovDir[x][y] != old_move_dir)
6789 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6791 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6793 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6794 MovDir[x][y] = right_dir;
6795 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6796 MovDir[x][y] = left_dir;
6798 if (MovDir[x][y] != old_move_dir)
6799 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6801 else if (move_pattern == MV_TOWARDS_PLAYER ||
6802 move_pattern == MV_AWAY_FROM_PLAYER)
6804 int attr_x = -1, attr_y = -1;
6806 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6817 for (i = 0; i < MAX_PLAYERS; i++)
6819 struct PlayerInfo *player = &stored_player[i];
6820 int jx = player->jx, jy = player->jy;
6822 if (!player->active)
6826 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6834 MovDir[x][y] = MV_NONE;
6836 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6837 else if (attr_x > x)
6838 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6840 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6841 else if (attr_y > y)
6842 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6844 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6846 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6848 boolean first_horiz = RND(2);
6849 int new_move_dir = MovDir[x][y];
6851 if (element_info[element].move_stepsize == 0) /* "not moving" */
6853 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6854 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6860 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6861 Moving2Blocked(x, y, &newx, &newy);
6863 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6867 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6868 Moving2Blocked(x, y, &newx, &newy);
6870 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6873 MovDir[x][y] = old_move_dir;
6876 else if (move_pattern == MV_WHEN_PUSHED ||
6877 move_pattern == MV_WHEN_DROPPED)
6879 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6880 MovDir[x][y] = MV_NONE;
6884 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6886 static int test_xy[7][2] =
6896 static int test_dir[7] =
6906 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6907 int move_preference = -1000000; /* start with very low preference */
6908 int new_move_dir = MV_NONE;
6909 int start_test = RND(4);
6912 for (i = 0; i < NUM_DIRECTIONS; i++)
6914 int move_dir = test_dir[start_test + i];
6915 int move_dir_preference;
6917 xx = x + test_xy[start_test + i][0];
6918 yy = y + test_xy[start_test + i][1];
6920 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6921 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6923 new_move_dir = move_dir;
6928 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6931 move_dir_preference = -1 * RunnerVisit[xx][yy];
6932 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6933 move_dir_preference = PlayerVisit[xx][yy];
6935 if (move_dir_preference > move_preference)
6937 /* prefer field that has not been visited for the longest time */
6938 move_preference = move_dir_preference;
6939 new_move_dir = move_dir;
6941 else if (move_dir_preference == move_preference &&
6942 move_dir == old_move_dir)
6944 /* prefer last direction when all directions are preferred equally */
6945 move_preference = move_dir_preference;
6946 new_move_dir = move_dir;
6950 MovDir[x][y] = new_move_dir;
6951 if (old_move_dir != new_move_dir)
6952 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6956 static void TurnRound(int x, int y)
6958 int direction = MovDir[x][y];
6962 GfxDir[x][y] = MovDir[x][y];
6964 if (direction != MovDir[x][y])
6968 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6970 ResetGfxFrame(x, y, FALSE);
6973 static boolean JustBeingPushed(int x, int y)
6977 for (i = 0; i < MAX_PLAYERS; i++)
6979 struct PlayerInfo *player = &stored_player[i];
6981 if (player->active && player->is_pushing && player->MovPos)
6983 int next_jx = player->jx + (player->jx - player->last_jx);
6984 int next_jy = player->jy + (player->jy - player->last_jy);
6986 if (x == next_jx && y == next_jy)
6994 void StartMoving(int x, int y)
6996 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6997 int element = Feld[x][y];
7002 if (MovDelay[x][y] == 0)
7003 GfxAction[x][y] = ACTION_DEFAULT;
7005 if (CAN_FALL(element) && y < lev_fieldy - 1)
7007 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7008 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7009 if (JustBeingPushed(x, y))
7012 if (element == EL_QUICKSAND_FULL)
7014 if (IS_FREE(x, y + 1))
7016 InitMovingField(x, y, MV_DOWN);
7017 started_moving = TRUE;
7019 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7020 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7021 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7022 Store[x][y] = EL_ROCK;
7024 Store[x][y] = EL_ROCK;
7027 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7029 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7031 if (!MovDelay[x][y])
7032 MovDelay[x][y] = TILEY + 1;
7041 Feld[x][y] = EL_QUICKSAND_EMPTY;
7042 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7043 Store[x][y + 1] = Store[x][y];
7046 PlayLevelSoundAction(x, y, ACTION_FILLING);
7049 else if (element == EL_QUICKSAND_FAST_FULL)
7051 if (IS_FREE(x, y + 1))
7053 InitMovingField(x, y, MV_DOWN);
7054 started_moving = TRUE;
7056 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7057 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7058 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7059 Store[x][y] = EL_ROCK;
7061 Store[x][y] = EL_ROCK;
7064 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7066 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7068 if (!MovDelay[x][y])
7069 MovDelay[x][y] = TILEY + 1;
7078 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7079 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7080 Store[x][y + 1] = Store[x][y];
7083 PlayLevelSoundAction(x, y, ACTION_FILLING);
7086 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7087 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7089 InitMovingField(x, y, MV_DOWN);
7090 started_moving = TRUE;
7092 Feld[x][y] = EL_QUICKSAND_FILLING;
7093 Store[x][y] = element;
7095 PlayLevelSoundAction(x, y, ACTION_FILLING);
7097 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7098 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7100 InitMovingField(x, y, MV_DOWN);
7101 started_moving = TRUE;
7103 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7104 Store[x][y] = element;
7106 PlayLevelSoundAction(x, y, ACTION_FILLING);
7108 else if (element == EL_MAGIC_WALL_FULL)
7110 if (IS_FREE(x, y + 1))
7112 InitMovingField(x, y, MV_DOWN);
7113 started_moving = TRUE;
7115 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7116 Store[x][y] = EL_CHANGED(Store[x][y]);
7118 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7120 if (!MovDelay[x][y])
7121 MovDelay[x][y] = TILEY/4 + 1;
7130 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7131 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7132 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7136 else if (element == EL_BD_MAGIC_WALL_FULL)
7138 if (IS_FREE(x, y + 1))
7140 InitMovingField(x, y, MV_DOWN);
7141 started_moving = TRUE;
7143 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7144 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7146 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7148 if (!MovDelay[x][y])
7149 MovDelay[x][y] = TILEY/4 + 1;
7158 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7159 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7160 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7164 else if (element == EL_DC_MAGIC_WALL_FULL)
7166 if (IS_FREE(x, y + 1))
7168 InitMovingField(x, y, MV_DOWN);
7169 started_moving = TRUE;
7171 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7172 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7174 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7176 if (!MovDelay[x][y])
7177 MovDelay[x][y] = TILEY/4 + 1;
7186 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7187 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7188 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7192 else if ((CAN_PASS_MAGIC_WALL(element) &&
7193 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7194 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7195 (CAN_PASS_DC_MAGIC_WALL(element) &&
7196 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7199 InitMovingField(x, y, MV_DOWN);
7200 started_moving = TRUE;
7203 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7204 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7205 EL_DC_MAGIC_WALL_FILLING);
7206 Store[x][y] = element;
7208 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7210 SplashAcid(x, y + 1);
7212 InitMovingField(x, y, MV_DOWN);
7213 started_moving = TRUE;
7215 Store[x][y] = EL_ACID;
7218 #if USE_FIX_IMPACT_COLLISION
7219 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7220 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7222 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7223 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7225 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7226 CAN_FALL(element) && WasJustFalling[x][y] &&
7227 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7229 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7230 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7231 (Feld[x][y + 1] == EL_BLOCKED)))
7233 /* this is needed for a special case not covered by calling "Impact()"
7234 from "ContinueMoving()": if an element moves to a tile directly below
7235 another element which was just falling on that tile (which was empty
7236 in the previous frame), the falling element above would just stop
7237 instead of smashing the element below (in previous version, the above
7238 element was just checked for "moving" instead of "falling", resulting
7239 in incorrect smashes caused by horizontal movement of the above
7240 element; also, the case of the player being the element to smash was
7241 simply not covered here... :-/ ) */
7243 CheckCollision[x][y] = 0;
7244 CheckImpact[x][y] = 0;
7248 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7250 if (MovDir[x][y] == MV_NONE)
7252 InitMovingField(x, y, MV_DOWN);
7253 started_moving = TRUE;
7256 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7258 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7259 MovDir[x][y] = MV_DOWN;
7261 InitMovingField(x, y, MV_DOWN);
7262 started_moving = TRUE;
7264 else if (element == EL_AMOEBA_DROP)
7266 Feld[x][y] = EL_AMOEBA_GROWING;
7267 Store[x][y] = EL_AMOEBA_WET;
7269 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7270 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7271 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7272 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7274 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7275 (IS_FREE(x - 1, y + 1) ||
7276 Feld[x - 1][y + 1] == EL_ACID));
7277 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7278 (IS_FREE(x + 1, y + 1) ||
7279 Feld[x + 1][y + 1] == EL_ACID));
7280 boolean can_fall_any = (can_fall_left || can_fall_right);
7281 boolean can_fall_both = (can_fall_left && can_fall_right);
7282 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7284 #if USE_NEW_ALL_SLIPPERY
7285 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7287 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7288 can_fall_right = FALSE;
7289 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7290 can_fall_left = FALSE;
7291 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7292 can_fall_right = FALSE;
7293 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7294 can_fall_left = FALSE;
7296 can_fall_any = (can_fall_left || can_fall_right);
7297 can_fall_both = FALSE;
7300 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7302 if (slippery_type == SLIPPERY_ONLY_LEFT)
7303 can_fall_right = FALSE;
7304 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7305 can_fall_left = FALSE;
7306 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7307 can_fall_right = FALSE;
7308 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7309 can_fall_left = FALSE;
7311 can_fall_any = (can_fall_left || can_fall_right);
7312 can_fall_both = (can_fall_left && can_fall_right);
7316 #if USE_NEW_ALL_SLIPPERY
7318 #if USE_NEW_SP_SLIPPERY
7319 /* !!! better use the same properties as for custom elements here !!! */
7320 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7321 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7323 can_fall_right = FALSE; /* slip down on left side */
7324 can_fall_both = FALSE;
7329 #if USE_NEW_ALL_SLIPPERY
7332 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7333 can_fall_right = FALSE; /* slip down on left side */
7335 can_fall_left = !(can_fall_right = RND(2));
7337 can_fall_both = FALSE;
7342 if (game.emulation == EMU_BOULDERDASH ||
7343 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7344 can_fall_right = FALSE; /* slip down on left side */
7346 can_fall_left = !(can_fall_right = RND(2));
7348 can_fall_both = FALSE;
7354 /* if not determined otherwise, prefer left side for slipping down */
7355 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7356 started_moving = TRUE;
7360 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7362 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7365 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7366 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7367 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7368 int belt_dir = game.belt_dir[belt_nr];
7370 if ((belt_dir == MV_LEFT && left_is_free) ||
7371 (belt_dir == MV_RIGHT && right_is_free))
7373 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7375 InitMovingField(x, y, belt_dir);
7376 started_moving = TRUE;
7378 Pushed[x][y] = TRUE;
7379 Pushed[nextx][y] = TRUE;
7381 GfxAction[x][y] = ACTION_DEFAULT;
7385 MovDir[x][y] = 0; /* if element was moving, stop it */
7390 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7392 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7394 if (CAN_MOVE(element) && !started_moving)
7397 int move_pattern = element_info[element].move_pattern;
7402 if (MovDir[x][y] == MV_NONE)
7404 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7405 x, y, element, element_info[element].token_name);
7406 printf("StartMoving(): This should never happen!\n");
7411 Moving2Blocked(x, y, &newx, &newy);
7413 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7416 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7417 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7419 WasJustMoving[x][y] = 0;
7420 CheckCollision[x][y] = 0;
7422 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7424 if (Feld[x][y] != element) /* element has changed */
7428 if (!MovDelay[x][y]) /* start new movement phase */
7430 /* all objects that can change their move direction after each step
7431 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7433 if (element != EL_YAMYAM &&
7434 element != EL_DARK_YAMYAM &&
7435 element != EL_PACMAN &&
7436 !(move_pattern & MV_ANY_DIRECTION) &&
7437 move_pattern != MV_TURNING_LEFT &&
7438 move_pattern != MV_TURNING_RIGHT &&
7439 move_pattern != MV_TURNING_LEFT_RIGHT &&
7440 move_pattern != MV_TURNING_RIGHT_LEFT &&
7441 move_pattern != MV_TURNING_RANDOM)
7445 if (MovDelay[x][y] && (element == EL_BUG ||
7446 element == EL_SPACESHIP ||
7447 element == EL_SP_SNIKSNAK ||
7448 element == EL_SP_ELECTRON ||
7449 element == EL_MOLE))
7450 DrawLevelField(x, y);
7454 if (MovDelay[x][y]) /* wait some time before next movement */
7458 if (element == EL_ROBOT ||
7459 element == EL_YAMYAM ||
7460 element == EL_DARK_YAMYAM)
7462 DrawLevelElementAnimationIfNeeded(x, y, element);
7463 PlayLevelSoundAction(x, y, ACTION_WAITING);
7465 else if (element == EL_SP_ELECTRON)
7466 DrawLevelElementAnimationIfNeeded(x, y, element);
7467 else if (element == EL_DRAGON)
7470 int dir = MovDir[x][y];
7471 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7472 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7473 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7474 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7475 dir == MV_UP ? IMG_FLAMES_1_UP :
7476 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7477 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7479 GfxAction[x][y] = ACTION_ATTACKING;
7481 if (IS_PLAYER(x, y))
7482 DrawPlayerField(x, y);
7484 DrawLevelField(x, y);
7486 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7488 for (i = 1; i <= 3; i++)
7490 int xx = x + i * dx;
7491 int yy = y + i * dy;
7492 int sx = SCREENX(xx);
7493 int sy = SCREENY(yy);
7494 int flame_graphic = graphic + (i - 1);
7496 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7501 int flamed = MovingOrBlocked2Element(xx, yy);
7505 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7507 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7508 RemoveMovingField(xx, yy);
7510 RemoveField(xx, yy);
7512 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7515 RemoveMovingField(xx, yy);
7518 ChangeDelay[xx][yy] = 0;
7520 Feld[xx][yy] = EL_FLAMES;
7522 if (IN_SCR_FIELD(sx, sy))
7524 DrawLevelFieldCrumbledSand(xx, yy);
7525 DrawGraphic(sx, sy, flame_graphic, frame);
7530 if (Feld[xx][yy] == EL_FLAMES)
7531 Feld[xx][yy] = EL_EMPTY;
7532 DrawLevelField(xx, yy);
7537 if (MovDelay[x][y]) /* element still has to wait some time */
7539 PlayLevelSoundAction(x, y, ACTION_WAITING);
7545 /* now make next step */
7547 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7549 if (DONT_COLLIDE_WITH(element) &&
7550 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7551 !PLAYER_ENEMY_PROTECTED(newx, newy))
7553 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7558 else if (CAN_MOVE_INTO_ACID(element) &&
7559 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7560 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7561 (MovDir[x][y] == MV_DOWN ||
7562 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7564 SplashAcid(newx, newy);
7565 Store[x][y] = EL_ACID;
7567 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7569 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7570 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7571 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7572 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7575 DrawLevelField(x, y);
7577 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7578 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7579 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7581 local_player->friends_still_needed--;
7582 if (!local_player->friends_still_needed &&
7583 !local_player->GameOver && AllPlayersGone)
7584 PlayerWins(local_player);
7588 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7590 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7591 DrawLevelField(newx, newy);
7593 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7595 else if (!IS_FREE(newx, newy))
7597 GfxAction[x][y] = ACTION_WAITING;
7599 if (IS_PLAYER(x, y))
7600 DrawPlayerField(x, y);
7602 DrawLevelField(x, y);
7607 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7609 if (IS_FOOD_PIG(Feld[newx][newy]))
7611 if (IS_MOVING(newx, newy))
7612 RemoveMovingField(newx, newy);
7615 Feld[newx][newy] = EL_EMPTY;
7616 DrawLevelField(newx, newy);
7619 PlayLevelSound(x, y, SND_PIG_DIGGING);
7621 else if (!IS_FREE(newx, newy))
7623 if (IS_PLAYER(x, y))
7624 DrawPlayerField(x, y);
7626 DrawLevelField(x, y);
7631 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7633 if (Store[x][y] != EL_EMPTY)
7635 boolean can_clone = FALSE;
7638 /* check if element to clone is still there */
7639 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7641 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7649 /* cannot clone or target field not free anymore -- do not clone */
7650 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7651 Store[x][y] = EL_EMPTY;
7654 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7656 if (IS_MV_DIAGONAL(MovDir[x][y]))
7658 int diagonal_move_dir = MovDir[x][y];
7659 int stored = Store[x][y];
7660 int change_delay = 8;
7663 /* android is moving diagonally */
7665 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7667 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7668 GfxElement[x][y] = EL_EMC_ANDROID;
7669 GfxAction[x][y] = ACTION_SHRINKING;
7670 GfxDir[x][y] = diagonal_move_dir;
7671 ChangeDelay[x][y] = change_delay;
7673 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7676 DrawLevelGraphicAnimation(x, y, graphic);
7677 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7679 if (Feld[newx][newy] == EL_ACID)
7681 SplashAcid(newx, newy);
7686 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7688 Store[newx][newy] = EL_EMC_ANDROID;
7689 GfxElement[newx][newy] = EL_EMC_ANDROID;
7690 GfxAction[newx][newy] = ACTION_GROWING;
7691 GfxDir[newx][newy] = diagonal_move_dir;
7692 ChangeDelay[newx][newy] = change_delay;
7694 graphic = el_act_dir2img(GfxElement[newx][newy],
7695 GfxAction[newx][newy], GfxDir[newx][newy]);
7697 DrawLevelGraphicAnimation(newx, newy, graphic);
7698 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7704 Feld[newx][newy] = EL_EMPTY;
7705 DrawLevelField(newx, newy);
7707 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7710 else if (!IS_FREE(newx, newy))
7713 if (IS_PLAYER(x, y))
7714 DrawPlayerField(x, y);
7716 DrawLevelField(x, y);
7722 else if (IS_CUSTOM_ELEMENT(element) &&
7723 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7725 int new_element = Feld[newx][newy];
7727 if (!IS_FREE(newx, newy))
7729 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7730 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7733 /* no element can dig solid indestructible elements */
7734 if (IS_INDESTRUCTIBLE(new_element) &&
7735 !IS_DIGGABLE(new_element) &&
7736 !IS_COLLECTIBLE(new_element))
7739 if (AmoebaNr[newx][newy] &&
7740 (new_element == EL_AMOEBA_FULL ||
7741 new_element == EL_BD_AMOEBA ||
7742 new_element == EL_AMOEBA_GROWING))
7744 AmoebaCnt[AmoebaNr[newx][newy]]--;
7745 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7748 if (IS_MOVING(newx, newy))
7749 RemoveMovingField(newx, newy);
7752 RemoveField(newx, newy);
7753 DrawLevelField(newx, newy);
7756 /* if digged element was about to explode, prevent the explosion */
7757 ExplodeField[newx][newy] = EX_TYPE_NONE;
7759 PlayLevelSoundAction(x, y, action);
7762 Store[newx][newy] = EL_EMPTY;
7764 /* this makes it possible to leave the removed element again */
7765 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7766 Store[newx][newy] = new_element;
7768 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7770 int move_leave_element = element_info[element].move_leave_element;
7772 /* this makes it possible to leave the removed element again */
7773 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7774 new_element : move_leave_element);
7778 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7780 RunnerVisit[x][y] = FrameCounter;
7781 PlayerVisit[x][y] /= 8; /* expire player visit path */
7784 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7786 if (!IS_FREE(newx, newy))
7788 if (IS_PLAYER(x, y))
7789 DrawPlayerField(x, y);
7791 DrawLevelField(x, y);
7797 boolean wanna_flame = !RND(10);
7798 int dx = newx - x, dy = newy - y;
7799 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7800 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7801 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7802 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7803 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7804 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7807 IS_CLASSIC_ENEMY(element1) ||
7808 IS_CLASSIC_ENEMY(element2)) &&
7809 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7810 element1 != EL_FLAMES && element2 != EL_FLAMES)
7812 ResetGfxAnimation(x, y);
7813 GfxAction[x][y] = ACTION_ATTACKING;
7815 if (IS_PLAYER(x, y))
7816 DrawPlayerField(x, y);
7818 DrawLevelField(x, y);
7820 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7822 MovDelay[x][y] = 50;
7826 RemoveField(newx, newy);
7828 Feld[newx][newy] = EL_FLAMES;
7829 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7832 RemoveField(newx1, newy1);
7834 Feld[newx1][newy1] = EL_FLAMES;
7836 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7839 RemoveField(newx2, newy2);
7841 Feld[newx2][newy2] = EL_FLAMES;
7848 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7849 Feld[newx][newy] == EL_DIAMOND)
7851 if (IS_MOVING(newx, newy))
7852 RemoveMovingField(newx, newy);
7855 Feld[newx][newy] = EL_EMPTY;
7856 DrawLevelField(newx, newy);
7859 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7861 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7862 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7864 if (AmoebaNr[newx][newy])
7866 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7867 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7868 Feld[newx][newy] == EL_BD_AMOEBA)
7869 AmoebaCnt[AmoebaNr[newx][newy]]--;
7874 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7876 RemoveMovingField(newx, newy);
7879 if (IS_MOVING(newx, newy))
7881 RemoveMovingField(newx, newy);
7886 Feld[newx][newy] = EL_EMPTY;
7887 DrawLevelField(newx, newy);
7890 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7892 else if ((element == EL_PACMAN || element == EL_MOLE)
7893 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7895 if (AmoebaNr[newx][newy])
7897 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7898 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7899 Feld[newx][newy] == EL_BD_AMOEBA)
7900 AmoebaCnt[AmoebaNr[newx][newy]]--;
7903 if (element == EL_MOLE)
7905 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7906 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7908 ResetGfxAnimation(x, y);
7909 GfxAction[x][y] = ACTION_DIGGING;
7910 DrawLevelField(x, y);
7912 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7914 return; /* wait for shrinking amoeba */
7916 else /* element == EL_PACMAN */
7918 Feld[newx][newy] = EL_EMPTY;
7919 DrawLevelField(newx, newy);
7920 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7923 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7924 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7925 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7927 /* wait for shrinking amoeba to completely disappear */
7930 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7932 /* object was running against a wall */
7937 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7938 if (move_pattern & MV_ANY_DIRECTION &&
7939 move_pattern == MovDir[x][y])
7941 int blocking_element =
7942 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7944 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7947 element = Feld[x][y]; /* element might have changed */
7951 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7952 DrawLevelElementAnimation(x, y, element);
7954 if (DONT_TOUCH(element))
7955 TestIfBadThingTouchesPlayer(x, y);
7960 InitMovingField(x, y, MovDir[x][y]);
7962 PlayLevelSoundAction(x, y, ACTION_MOVING);
7966 ContinueMoving(x, y);
7969 void ContinueMoving(int x, int y)
7971 int element = Feld[x][y];
7972 struct ElementInfo *ei = &element_info[element];
7973 int direction = MovDir[x][y];
7974 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7975 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7976 int newx = x + dx, newy = y + dy;
7977 int stored = Store[x][y];
7978 int stored_new = Store[newx][newy];
7979 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7980 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7981 boolean last_line = (newy == lev_fieldy - 1);
7983 MovPos[x][y] += getElementMoveStepsize(x, y);
7985 if (pushed_by_player) /* special case: moving object pushed by player */
7986 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7988 if (ABS(MovPos[x][y]) < TILEX)
7991 int ee = Feld[x][y];
7992 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7993 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7995 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7996 x, y, ABS(MovPos[x][y]),
7998 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8001 DrawLevelField(x, y);
8003 return; /* element is still moving */
8006 /* element reached destination field */
8008 Feld[x][y] = EL_EMPTY;
8009 Feld[newx][newy] = element;
8010 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8012 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8014 element = Feld[newx][newy] = EL_ACID;
8016 else if (element == EL_MOLE)
8018 Feld[x][y] = EL_SAND;
8020 DrawLevelFieldCrumbledSandNeighbours(x, y);
8022 else if (element == EL_QUICKSAND_FILLING)
8024 element = Feld[newx][newy] = get_next_element(element);
8025 Store[newx][newy] = Store[x][y];
8027 else if (element == EL_QUICKSAND_EMPTYING)
8029 Feld[x][y] = get_next_element(element);
8030 element = Feld[newx][newy] = Store[x][y];
8032 else if (element == EL_QUICKSAND_FAST_FILLING)
8034 element = Feld[newx][newy] = get_next_element(element);
8035 Store[newx][newy] = Store[x][y];
8037 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8039 Feld[x][y] = get_next_element(element);
8040 element = Feld[newx][newy] = Store[x][y];
8042 else if (element == EL_MAGIC_WALL_FILLING)
8044 element = Feld[newx][newy] = get_next_element(element);
8045 if (!game.magic_wall_active)
8046 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8047 Store[newx][newy] = Store[x][y];
8049 else if (element == EL_MAGIC_WALL_EMPTYING)
8051 Feld[x][y] = get_next_element(element);
8052 if (!game.magic_wall_active)
8053 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8054 element = Feld[newx][newy] = Store[x][y];
8056 #if USE_NEW_CUSTOM_VALUE
8057 InitField(newx, newy, FALSE);
8060 else if (element == EL_BD_MAGIC_WALL_FILLING)
8062 element = Feld[newx][newy] = get_next_element(element);
8063 if (!game.magic_wall_active)
8064 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8065 Store[newx][newy] = Store[x][y];
8067 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8069 Feld[x][y] = get_next_element(element);
8070 if (!game.magic_wall_active)
8071 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8072 element = Feld[newx][newy] = Store[x][y];
8074 #if USE_NEW_CUSTOM_VALUE
8075 InitField(newx, newy, FALSE);
8078 else if (element == EL_DC_MAGIC_WALL_FILLING)
8080 element = Feld[newx][newy] = get_next_element(element);
8081 if (!game.magic_wall_active)
8082 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8083 Store[newx][newy] = Store[x][y];
8085 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8087 Feld[x][y] = get_next_element(element);
8088 if (!game.magic_wall_active)
8089 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8090 element = Feld[newx][newy] = Store[x][y];
8092 #if USE_NEW_CUSTOM_VALUE
8093 InitField(newx, newy, FALSE);
8096 else if (element == EL_AMOEBA_DROPPING)
8098 Feld[x][y] = get_next_element(element);
8099 element = Feld[newx][newy] = Store[x][y];
8101 else if (element == EL_SOKOBAN_OBJECT)
8104 Feld[x][y] = Back[x][y];
8106 if (Back[newx][newy])
8107 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8109 Back[x][y] = Back[newx][newy] = 0;
8112 Store[x][y] = EL_EMPTY;
8117 MovDelay[newx][newy] = 0;
8119 if (CAN_CHANGE_OR_HAS_ACTION(element))
8121 /* copy element change control values to new field */
8122 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8123 ChangePage[newx][newy] = ChangePage[x][y];
8124 ChangeCount[newx][newy] = ChangeCount[x][y];
8125 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8128 #if USE_NEW_CUSTOM_VALUE
8129 CustomValue[newx][newy] = CustomValue[x][y];
8132 ChangeDelay[x][y] = 0;
8133 ChangePage[x][y] = -1;
8134 ChangeCount[x][y] = 0;
8135 ChangeEvent[x][y] = -1;
8137 #if USE_NEW_CUSTOM_VALUE
8138 CustomValue[x][y] = 0;
8141 /* copy animation control values to new field */
8142 GfxFrame[newx][newy] = GfxFrame[x][y];
8143 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8144 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8145 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8147 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8149 /* some elements can leave other elements behind after moving */
8151 if (ei->move_leave_element != EL_EMPTY &&
8152 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8153 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8155 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8156 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8157 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8160 int move_leave_element = ei->move_leave_element;
8164 /* this makes it possible to leave the removed element again */
8165 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8166 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8168 /* this makes it possible to leave the removed element again */
8169 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8170 move_leave_element = stored;
8173 /* this makes it possible to leave the removed element again */
8174 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8175 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8176 move_leave_element = stored;
8179 Feld[x][y] = move_leave_element;
8181 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8182 MovDir[x][y] = direction;
8184 InitField(x, y, FALSE);
8186 if (GFX_CRUMBLED(Feld[x][y]))
8187 DrawLevelFieldCrumbledSandNeighbours(x, y);
8189 if (ELEM_IS_PLAYER(move_leave_element))
8190 RelocatePlayer(x, y, move_leave_element);
8193 /* do this after checking for left-behind element */
8194 ResetGfxAnimation(x, y); /* reset animation values for old field */
8196 if (!CAN_MOVE(element) ||
8197 (CAN_FALL(element) && direction == MV_DOWN &&
8198 (element == EL_SPRING ||
8199 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8200 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8201 GfxDir[x][y] = MovDir[newx][newy] = 0;
8203 DrawLevelField(x, y);
8204 DrawLevelField(newx, newy);
8206 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8208 /* prevent pushed element from moving on in pushed direction */
8209 if (pushed_by_player && CAN_MOVE(element) &&
8210 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8211 !(element_info[element].move_pattern & direction))
8212 TurnRound(newx, newy);
8214 /* prevent elements on conveyor belt from moving on in last direction */
8215 if (pushed_by_conveyor && CAN_FALL(element) &&
8216 direction & MV_HORIZONTAL)
8217 MovDir[newx][newy] = 0;
8219 if (!pushed_by_player)
8221 int nextx = newx + dx, nexty = newy + dy;
8222 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8224 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8226 if (CAN_FALL(element) && direction == MV_DOWN)
8227 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8229 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8230 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8232 #if USE_FIX_IMPACT_COLLISION
8233 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8234 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8238 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8240 TestIfBadThingTouchesPlayer(newx, newy);
8241 TestIfBadThingTouchesFriend(newx, newy);
8243 if (!IS_CUSTOM_ELEMENT(element))
8244 TestIfBadThingTouchesOtherBadThing(newx, newy);
8246 else if (element == EL_PENGUIN)
8247 TestIfFriendTouchesBadThing(newx, newy);
8249 /* give the player one last chance (one more frame) to move away */
8250 if (CAN_FALL(element) && direction == MV_DOWN &&
8251 (last_line || (!IS_FREE(x, newy + 1) &&
8252 (!IS_PLAYER(x, newy + 1) ||
8253 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8256 if (pushed_by_player && !game.use_change_when_pushing_bug)
8258 int push_side = MV_DIR_OPPOSITE(direction);
8259 struct PlayerInfo *player = PLAYERINFO(x, y);
8261 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8262 player->index_bit, push_side);
8263 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8264 player->index_bit, push_side);
8267 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8268 MovDelay[newx][newy] = 1;
8270 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8272 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8275 if (ChangePage[newx][newy] != -1) /* delayed change */
8277 int page = ChangePage[newx][newy];
8278 struct ElementChangeInfo *change = &ei->change_page[page];
8280 ChangePage[newx][newy] = -1;
8282 if (change->can_change)
8284 if (ChangeElement(newx, newy, element, page))
8286 if (change->post_change_function)
8287 change->post_change_function(newx, newy);
8291 if (change->has_action)
8292 ExecuteCustomElementAction(newx, newy, element, page);
8296 TestIfElementHitsCustomElement(newx, newy, direction);
8297 TestIfPlayerTouchesCustomElement(newx, newy);
8298 TestIfElementTouchesCustomElement(newx, newy);
8300 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8301 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8302 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8303 MV_DIR_OPPOSITE(direction));
8306 int AmoebeNachbarNr(int ax, int ay)
8309 int element = Feld[ax][ay];
8311 static int xy[4][2] =
8319 for (i = 0; i < NUM_DIRECTIONS; i++)
8321 int x = ax + xy[i][0];
8322 int y = ay + xy[i][1];
8324 if (!IN_LEV_FIELD(x, y))
8327 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8328 group_nr = AmoebaNr[x][y];
8334 void AmoebenVereinigen(int ax, int ay)
8336 int i, x, y, xx, yy;
8337 int new_group_nr = AmoebaNr[ax][ay];
8338 static int xy[4][2] =
8346 if (new_group_nr == 0)
8349 for (i = 0; i < NUM_DIRECTIONS; i++)
8354 if (!IN_LEV_FIELD(x, y))
8357 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8358 Feld[x][y] == EL_BD_AMOEBA ||
8359 Feld[x][y] == EL_AMOEBA_DEAD) &&
8360 AmoebaNr[x][y] != new_group_nr)
8362 int old_group_nr = AmoebaNr[x][y];
8364 if (old_group_nr == 0)
8367 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8368 AmoebaCnt[old_group_nr] = 0;
8369 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8370 AmoebaCnt2[old_group_nr] = 0;
8372 SCAN_PLAYFIELD(xx, yy)
8374 if (AmoebaNr[xx][yy] == old_group_nr)
8375 AmoebaNr[xx][yy] = new_group_nr;
8381 void AmoebeUmwandeln(int ax, int ay)
8385 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8387 int group_nr = AmoebaNr[ax][ay];
8392 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8393 printf("AmoebeUmwandeln(): This should never happen!\n");
8398 SCAN_PLAYFIELD(x, y)
8400 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8403 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8407 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8408 SND_AMOEBA_TURNING_TO_GEM :
8409 SND_AMOEBA_TURNING_TO_ROCK));
8414 static int xy[4][2] =
8422 for (i = 0; i < NUM_DIRECTIONS; i++)
8427 if (!IN_LEV_FIELD(x, y))
8430 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8432 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8433 SND_AMOEBA_TURNING_TO_GEM :
8434 SND_AMOEBA_TURNING_TO_ROCK));
8441 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8444 int group_nr = AmoebaNr[ax][ay];
8445 boolean done = FALSE;
8450 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8451 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8456 SCAN_PLAYFIELD(x, y)
8458 if (AmoebaNr[x][y] == group_nr &&
8459 (Feld[x][y] == EL_AMOEBA_DEAD ||
8460 Feld[x][y] == EL_BD_AMOEBA ||
8461 Feld[x][y] == EL_AMOEBA_GROWING))
8464 Feld[x][y] = new_element;
8465 InitField(x, y, FALSE);
8466 DrawLevelField(x, y);
8472 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8473 SND_BD_AMOEBA_TURNING_TO_ROCK :
8474 SND_BD_AMOEBA_TURNING_TO_GEM));
8477 void AmoebeWaechst(int x, int y)
8479 static unsigned long sound_delay = 0;
8480 static unsigned long sound_delay_value = 0;
8482 if (!MovDelay[x][y]) /* start new growing cycle */
8486 if (DelayReached(&sound_delay, sound_delay_value))
8488 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8489 sound_delay_value = 30;
8493 if (MovDelay[x][y]) /* wait some time before growing bigger */
8496 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8498 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8499 6 - MovDelay[x][y]);
8501 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8504 if (!MovDelay[x][y])
8506 Feld[x][y] = Store[x][y];
8508 DrawLevelField(x, y);
8513 void AmoebaDisappearing(int x, int y)
8515 static unsigned long sound_delay = 0;
8516 static unsigned long sound_delay_value = 0;
8518 if (!MovDelay[x][y]) /* start new shrinking cycle */
8522 if (DelayReached(&sound_delay, sound_delay_value))
8523 sound_delay_value = 30;
8526 if (MovDelay[x][y]) /* wait some time before shrinking */
8529 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8531 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8532 6 - MovDelay[x][y]);
8534 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8537 if (!MovDelay[x][y])
8539 Feld[x][y] = EL_EMPTY;
8540 DrawLevelField(x, y);
8542 /* don't let mole enter this field in this cycle;
8543 (give priority to objects falling to this field from above) */
8549 void AmoebeAbleger(int ax, int ay)
8552 int element = Feld[ax][ay];
8553 int graphic = el2img(element);
8554 int newax = ax, neway = ay;
8555 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8556 static int xy[4][2] =
8564 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8566 Feld[ax][ay] = EL_AMOEBA_DEAD;
8567 DrawLevelField(ax, ay);
8571 if (IS_ANIMATED(graphic))
8572 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8574 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8575 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8577 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8580 if (MovDelay[ax][ay])
8584 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8587 int x = ax + xy[start][0];
8588 int y = ay + xy[start][1];
8590 if (!IN_LEV_FIELD(x, y))
8593 if (IS_FREE(x, y) ||
8594 CAN_GROW_INTO(Feld[x][y]) ||
8595 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8596 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8602 if (newax == ax && neway == ay)
8605 else /* normal or "filled" (BD style) amoeba */
8608 boolean waiting_for_player = FALSE;
8610 for (i = 0; i < NUM_DIRECTIONS; i++)
8612 int j = (start + i) % 4;
8613 int x = ax + xy[j][0];
8614 int y = ay + xy[j][1];
8616 if (!IN_LEV_FIELD(x, y))
8619 if (IS_FREE(x, y) ||
8620 CAN_GROW_INTO(Feld[x][y]) ||
8621 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8622 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8628 else if (IS_PLAYER(x, y))
8629 waiting_for_player = TRUE;
8632 if (newax == ax && neway == ay) /* amoeba cannot grow */
8634 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8636 Feld[ax][ay] = EL_AMOEBA_DEAD;
8637 DrawLevelField(ax, ay);
8638 AmoebaCnt[AmoebaNr[ax][ay]]--;
8640 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8642 if (element == EL_AMOEBA_FULL)
8643 AmoebeUmwandeln(ax, ay);
8644 else if (element == EL_BD_AMOEBA)
8645 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8650 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8652 /* amoeba gets larger by growing in some direction */
8654 int new_group_nr = AmoebaNr[ax][ay];
8657 if (new_group_nr == 0)
8659 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8660 printf("AmoebeAbleger(): This should never happen!\n");
8665 AmoebaNr[newax][neway] = new_group_nr;
8666 AmoebaCnt[new_group_nr]++;
8667 AmoebaCnt2[new_group_nr]++;
8669 /* if amoeba touches other amoeba(s) after growing, unify them */
8670 AmoebenVereinigen(newax, neway);
8672 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8674 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8680 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8681 (neway == lev_fieldy - 1 && newax != ax))
8683 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8684 Store[newax][neway] = element;
8686 else if (neway == ay || element == EL_EMC_DRIPPER)
8688 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8690 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8694 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8695 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8696 Store[ax][ay] = EL_AMOEBA_DROP;
8697 ContinueMoving(ax, ay);
8701 DrawLevelField(newax, neway);
8704 void Life(int ax, int ay)
8708 int element = Feld[ax][ay];
8709 int graphic = el2img(element);
8710 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8712 boolean changed = FALSE;
8714 if (IS_ANIMATED(graphic))
8715 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8720 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8721 MovDelay[ax][ay] = life_time;
8723 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8726 if (MovDelay[ax][ay])
8730 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8732 int xx = ax+x1, yy = ay+y1;
8735 if (!IN_LEV_FIELD(xx, yy))
8738 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8740 int x = xx+x2, y = yy+y2;
8742 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8745 if (((Feld[x][y] == element ||
8746 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8748 (IS_FREE(x, y) && Stop[x][y]))
8752 if (xx == ax && yy == ay) /* field in the middle */
8754 if (nachbarn < life_parameter[0] ||
8755 nachbarn > life_parameter[1])
8757 Feld[xx][yy] = EL_EMPTY;
8759 DrawLevelField(xx, yy);
8760 Stop[xx][yy] = TRUE;
8764 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8765 { /* free border field */
8766 if (nachbarn >= life_parameter[2] &&
8767 nachbarn <= life_parameter[3])
8769 Feld[xx][yy] = element;
8770 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8772 DrawLevelField(xx, yy);
8773 Stop[xx][yy] = TRUE;
8780 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8781 SND_GAME_OF_LIFE_GROWING);
8784 static void InitRobotWheel(int x, int y)
8786 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8789 static void RunRobotWheel(int x, int y)
8791 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8794 static void StopRobotWheel(int x, int y)
8796 if (ZX == x && ZY == y)
8800 game.robot_wheel_active = FALSE;
8804 static void InitTimegateWheel(int x, int y)
8806 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8809 static void RunTimegateWheel(int x, int y)
8811 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8814 static void InitMagicBallDelay(int x, int y)
8817 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8819 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8823 static void ActivateMagicBall(int bx, int by)
8827 if (level.ball_random)
8829 int pos_border = RND(8); /* select one of the eight border elements */
8830 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8831 int xx = pos_content % 3;
8832 int yy = pos_content / 3;
8837 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8838 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8842 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8844 int xx = x - bx + 1;
8845 int yy = y - by + 1;
8847 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8848 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8852 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8855 void CheckExit(int x, int y)
8857 if (local_player->gems_still_needed > 0 ||
8858 local_player->sokobanfields_still_needed > 0 ||
8859 local_player->lights_still_needed > 0)
8861 int element = Feld[x][y];
8862 int graphic = el2img(element);
8864 if (IS_ANIMATED(graphic))
8865 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8870 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8873 Feld[x][y] = EL_EXIT_OPENING;
8875 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8878 void CheckExitEM(int x, int y)
8880 if (local_player->gems_still_needed > 0 ||
8881 local_player->sokobanfields_still_needed > 0 ||
8882 local_player->lights_still_needed > 0)
8884 int element = Feld[x][y];
8885 int graphic = el2img(element);
8887 if (IS_ANIMATED(graphic))
8888 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8893 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8896 Feld[x][y] = EL_EM_EXIT_OPENING;
8898 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8901 void CheckExitSteel(int x, int y)
8903 if (local_player->gems_still_needed > 0 ||
8904 local_player->sokobanfields_still_needed > 0 ||
8905 local_player->lights_still_needed > 0)
8907 int element = Feld[x][y];
8908 int graphic = el2img(element);
8910 if (IS_ANIMATED(graphic))
8911 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8916 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8919 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8921 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8924 void CheckExitSteelEM(int x, int y)
8926 if (local_player->gems_still_needed > 0 ||
8927 local_player->sokobanfields_still_needed > 0 ||
8928 local_player->lights_still_needed > 0)
8930 int element = Feld[x][y];
8931 int graphic = el2img(element);
8933 if (IS_ANIMATED(graphic))
8934 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8939 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8942 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8944 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8947 void CheckExitSP(int x, int y)
8949 if (local_player->gems_still_needed > 0)
8951 int element = Feld[x][y];
8952 int graphic = el2img(element);
8954 if (IS_ANIMATED(graphic))
8955 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8960 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8963 Feld[x][y] = EL_SP_EXIT_OPENING;
8965 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8968 static void CloseAllOpenTimegates()
8972 SCAN_PLAYFIELD(x, y)
8974 int element = Feld[x][y];
8976 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8978 Feld[x][y] = EL_TIMEGATE_CLOSING;
8980 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8985 void DrawTwinkleOnField(int x, int y)
8987 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8990 if (Feld[x][y] == EL_BD_DIAMOND)
8993 if (MovDelay[x][y] == 0) /* next animation frame */
8994 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8996 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9000 if (setup.direct_draw && MovDelay[x][y])
9001 SetDrawtoField(DRAW_BUFFERED);
9003 DrawLevelElementAnimation(x, y, Feld[x][y]);
9005 if (MovDelay[x][y] != 0)
9007 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9008 10 - MovDelay[x][y]);
9010 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9012 if (setup.direct_draw)
9016 dest_x = FX + SCREENX(x) * TILEX;
9017 dest_y = FY + SCREENY(y) * TILEY;
9019 BlitBitmap(drawto_field, window,
9020 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9021 SetDrawtoField(DRAW_DIRECT);
9027 void MauerWaechst(int x, int y)
9031 if (!MovDelay[x][y]) /* next animation frame */
9032 MovDelay[x][y] = 3 * delay;
9034 if (MovDelay[x][y]) /* wait some time before next frame */
9038 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9040 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9041 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9043 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9046 if (!MovDelay[x][y])
9048 if (MovDir[x][y] == MV_LEFT)
9050 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9051 DrawLevelField(x - 1, y);
9053 else if (MovDir[x][y] == MV_RIGHT)
9055 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9056 DrawLevelField(x + 1, y);
9058 else if (MovDir[x][y] == MV_UP)
9060 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9061 DrawLevelField(x, y - 1);
9065 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9066 DrawLevelField(x, y + 1);
9069 Feld[x][y] = Store[x][y];
9071 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9072 DrawLevelField(x, y);
9077 void MauerAbleger(int ax, int ay)
9079 int element = Feld[ax][ay];
9080 int graphic = el2img(element);
9081 boolean oben_frei = FALSE, unten_frei = FALSE;
9082 boolean links_frei = FALSE, rechts_frei = FALSE;
9083 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9084 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9085 boolean new_wall = FALSE;
9087 if (IS_ANIMATED(graphic))
9088 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9090 if (!MovDelay[ax][ay]) /* start building new wall */
9091 MovDelay[ax][ay] = 6;
9093 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9096 if (MovDelay[ax][ay])
9100 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9102 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9104 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9106 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9109 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9110 element == EL_EXPANDABLE_WALL_ANY)
9114 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9115 Store[ax][ay-1] = element;
9116 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9117 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9118 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9119 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9124 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9125 Store[ax][ay+1] = element;
9126 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9127 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9128 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9129 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9134 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9135 element == EL_EXPANDABLE_WALL_ANY ||
9136 element == EL_EXPANDABLE_WALL ||
9137 element == EL_BD_EXPANDABLE_WALL)
9141 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9142 Store[ax-1][ay] = element;
9143 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9144 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9145 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9146 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9152 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9153 Store[ax+1][ay] = element;
9154 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9155 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9156 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9157 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9162 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9163 DrawLevelField(ax, ay);
9165 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9167 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9168 unten_massiv = TRUE;
9169 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9170 links_massiv = TRUE;
9171 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9172 rechts_massiv = TRUE;
9174 if (((oben_massiv && unten_massiv) ||
9175 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9176 element == EL_EXPANDABLE_WALL) &&
9177 ((links_massiv && rechts_massiv) ||
9178 element == EL_EXPANDABLE_WALL_VERTICAL))
9179 Feld[ax][ay] = EL_WALL;
9182 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9185 void MauerAblegerStahl(int ax, int ay)
9187 int element = Feld[ax][ay];
9188 int graphic = el2img(element);
9189 boolean oben_frei = FALSE, unten_frei = FALSE;
9190 boolean links_frei = FALSE, rechts_frei = FALSE;
9191 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9192 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9193 boolean new_wall = FALSE;
9195 if (IS_ANIMATED(graphic))
9196 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9198 if (!MovDelay[ax][ay]) /* start building new wall */
9199 MovDelay[ax][ay] = 6;
9201 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9204 if (MovDelay[ax][ay])
9208 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9210 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9212 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9214 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9217 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9218 element == EL_EXPANDABLE_STEELWALL_ANY)
9222 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9223 Store[ax][ay-1] = element;
9224 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9225 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9226 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9227 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9232 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9233 Store[ax][ay+1] = element;
9234 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9235 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9236 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9237 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9242 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9243 element == EL_EXPANDABLE_STEELWALL_ANY)
9247 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9248 Store[ax-1][ay] = element;
9249 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9250 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9251 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9252 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9258 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9259 Store[ax+1][ay] = element;
9260 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9261 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9262 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9263 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9268 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9270 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9271 unten_massiv = TRUE;
9272 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9273 links_massiv = TRUE;
9274 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9275 rechts_massiv = TRUE;
9277 if (((oben_massiv && unten_massiv) ||
9278 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9279 ((links_massiv && rechts_massiv) ||
9280 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9281 Feld[ax][ay] = EL_WALL;
9284 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9287 void CheckForDragon(int x, int y)
9290 boolean dragon_found = FALSE;
9291 static int xy[4][2] =
9299 for (i = 0; i < NUM_DIRECTIONS; i++)
9301 for (j = 0; j < 4; j++)
9303 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9305 if (IN_LEV_FIELD(xx, yy) &&
9306 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9308 if (Feld[xx][yy] == EL_DRAGON)
9309 dragon_found = TRUE;
9318 for (i = 0; i < NUM_DIRECTIONS; i++)
9320 for (j = 0; j < 3; j++)
9322 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9324 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9326 Feld[xx][yy] = EL_EMPTY;
9327 DrawLevelField(xx, yy);
9336 static void InitBuggyBase(int x, int y)
9338 int element = Feld[x][y];
9339 int activating_delay = FRAMES_PER_SECOND / 4;
9342 (element == EL_SP_BUGGY_BASE ?
9343 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9344 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9346 element == EL_SP_BUGGY_BASE_ACTIVE ?
9347 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9350 static void WarnBuggyBase(int x, int y)
9353 static int xy[4][2] =
9361 for (i = 0; i < NUM_DIRECTIONS; i++)
9363 int xx = x + xy[i][0];
9364 int yy = y + xy[i][1];
9366 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9368 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9375 static void InitTrap(int x, int y)
9377 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9380 static void ActivateTrap(int x, int y)
9382 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9385 static void ChangeActiveTrap(int x, int y)
9387 int graphic = IMG_TRAP_ACTIVE;
9389 /* if new animation frame was drawn, correct crumbled sand border */
9390 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9391 DrawLevelFieldCrumbledSand(x, y);
9394 static int getSpecialActionElement(int element, int number, int base_element)
9396 return (element != EL_EMPTY ? element :
9397 number != -1 ? base_element + number - 1 :
9401 static int getModifiedActionNumber(int value_old, int operator, int operand,
9402 int value_min, int value_max)
9404 int value_new = (operator == CA_MODE_SET ? operand :
9405 operator == CA_MODE_ADD ? value_old + operand :
9406 operator == CA_MODE_SUBTRACT ? value_old - operand :
9407 operator == CA_MODE_MULTIPLY ? value_old * operand :
9408 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9409 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9412 return (value_new < value_min ? value_min :
9413 value_new > value_max ? value_max :
9417 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9419 struct ElementInfo *ei = &element_info[element];
9420 struct ElementChangeInfo *change = &ei->change_page[page];
9421 int target_element = change->target_element;
9422 int action_type = change->action_type;
9423 int action_mode = change->action_mode;
9424 int action_arg = change->action_arg;
9427 if (!change->has_action)
9430 /* ---------- determine action paramater values -------------------------- */
9432 int level_time_value =
9433 (level.time > 0 ? TimeLeft :
9436 int action_arg_element =
9437 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9438 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9439 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9442 int action_arg_direction =
9443 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9444 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9445 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9446 change->actual_trigger_side :
9447 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9448 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9451 int action_arg_number_min =
9452 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9455 int action_arg_number_max =
9456 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9457 action_type == CA_SET_LEVEL_GEMS ? 999 :
9458 action_type == CA_SET_LEVEL_TIME ? 9999 :
9459 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9460 action_type == CA_SET_CE_VALUE ? 9999 :
9461 action_type == CA_SET_CE_SCORE ? 9999 :
9464 int action_arg_number_reset =
9465 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9466 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9467 action_type == CA_SET_LEVEL_TIME ? level.time :
9468 action_type == CA_SET_LEVEL_SCORE ? 0 :
9469 #if USE_NEW_CUSTOM_VALUE
9470 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9472 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9474 action_type == CA_SET_CE_SCORE ? 0 :
9477 int action_arg_number =
9478 (action_arg <= CA_ARG_MAX ? action_arg :
9479 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9480 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9481 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9482 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9483 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9484 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9485 #if USE_NEW_CUSTOM_VALUE
9486 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9488 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9490 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9491 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9492 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9493 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9494 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9495 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9496 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9497 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9498 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9499 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9500 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9503 int action_arg_number_old =
9504 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9505 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9506 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9507 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9508 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9511 int action_arg_number_new =
9512 getModifiedActionNumber(action_arg_number_old,
9513 action_mode, action_arg_number,
9514 action_arg_number_min, action_arg_number_max);
9516 int trigger_player_bits =
9517 (change->actual_trigger_player >= EL_PLAYER_1 &&
9518 change->actual_trigger_player <= EL_PLAYER_4 ?
9519 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9522 int action_arg_player_bits =
9523 (action_arg >= CA_ARG_PLAYER_1 &&
9524 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9525 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9528 /* ---------- execute action -------------------------------------------- */
9530 switch (action_type)
9537 /* ---------- level actions ------------------------------------------- */
9539 case CA_RESTART_LEVEL:
9541 game.restart_level = TRUE;
9546 case CA_SHOW_ENVELOPE:
9548 int element = getSpecialActionElement(action_arg_element,
9549 action_arg_number, EL_ENVELOPE_1);
9551 if (IS_ENVELOPE(element))
9552 local_player->show_envelope = element;
9557 case CA_SET_LEVEL_TIME:
9559 if (level.time > 0) /* only modify limited time value */
9561 TimeLeft = action_arg_number_new;
9564 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9566 DisplayGameControlValues();
9568 DrawGameValue_Time(TimeLeft);
9571 if (!TimeLeft && setup.time_limit)
9572 for (i = 0; i < MAX_PLAYERS; i++)
9573 KillPlayer(&stored_player[i]);
9579 case CA_SET_LEVEL_SCORE:
9581 local_player->score = action_arg_number_new;
9584 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9586 DisplayGameControlValues();
9588 DrawGameValue_Score(local_player->score);
9594 case CA_SET_LEVEL_GEMS:
9596 local_player->gems_still_needed = action_arg_number_new;
9599 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9601 DisplayGameControlValues();
9603 DrawGameValue_Emeralds(local_player->gems_still_needed);
9609 #if !USE_PLAYER_GRAVITY
9610 case CA_SET_LEVEL_GRAVITY:
9612 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9613 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9614 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9620 case CA_SET_LEVEL_WIND:
9622 game.wind_direction = action_arg_direction;
9627 /* ---------- player actions ------------------------------------------ */
9629 case CA_MOVE_PLAYER:
9631 /* automatically move to the next field in specified direction */
9632 for (i = 0; i < MAX_PLAYERS; i++)
9633 if (trigger_player_bits & (1 << i))
9634 stored_player[i].programmed_action = action_arg_direction;
9639 case CA_EXIT_PLAYER:
9641 for (i = 0; i < MAX_PLAYERS; i++)
9642 if (action_arg_player_bits & (1 << i))
9643 PlayerWins(&stored_player[i]);
9648 case CA_KILL_PLAYER:
9650 for (i = 0; i < MAX_PLAYERS; i++)
9651 if (action_arg_player_bits & (1 << i))
9652 KillPlayer(&stored_player[i]);
9657 case CA_SET_PLAYER_KEYS:
9659 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9660 int element = getSpecialActionElement(action_arg_element,
9661 action_arg_number, EL_KEY_1);
9663 if (IS_KEY(element))
9665 for (i = 0; i < MAX_PLAYERS; i++)
9667 if (trigger_player_bits & (1 << i))
9669 stored_player[i].key[KEY_NR(element)] = key_state;
9671 DrawGameDoorValues();
9679 case CA_SET_PLAYER_SPEED:
9681 for (i = 0; i < MAX_PLAYERS; i++)
9683 if (trigger_player_bits & (1 << i))
9685 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9687 if (action_arg == CA_ARG_SPEED_FASTER &&
9688 stored_player[i].cannot_move)
9690 action_arg_number = STEPSIZE_VERY_SLOW;
9692 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9693 action_arg == CA_ARG_SPEED_FASTER)
9695 action_arg_number = 2;
9696 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9699 else if (action_arg == CA_ARG_NUMBER_RESET)
9701 action_arg_number = level.initial_player_stepsize[i];
9705 getModifiedActionNumber(move_stepsize,
9708 action_arg_number_min,
9709 action_arg_number_max);
9711 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9718 case CA_SET_PLAYER_SHIELD:
9720 for (i = 0; i < MAX_PLAYERS; i++)
9722 if (trigger_player_bits & (1 << i))
9724 if (action_arg == CA_ARG_SHIELD_OFF)
9726 stored_player[i].shield_normal_time_left = 0;
9727 stored_player[i].shield_deadly_time_left = 0;
9729 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9731 stored_player[i].shield_normal_time_left = 999999;
9733 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9735 stored_player[i].shield_normal_time_left = 999999;
9736 stored_player[i].shield_deadly_time_left = 999999;
9744 #if USE_PLAYER_GRAVITY
9745 case CA_SET_PLAYER_GRAVITY:
9747 for (i = 0; i < MAX_PLAYERS; i++)
9749 if (trigger_player_bits & (1 << i))
9751 stored_player[i].gravity =
9752 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9753 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9754 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9755 stored_player[i].gravity);
9763 case CA_SET_PLAYER_ARTWORK:
9765 for (i = 0; i < MAX_PLAYERS; i++)
9767 if (trigger_player_bits & (1 << i))
9769 int artwork_element = action_arg_element;
9771 if (action_arg == CA_ARG_ELEMENT_RESET)
9773 (level.use_artwork_element[i] ? level.artwork_element[i] :
9774 stored_player[i].element_nr);
9776 #if USE_GFX_RESET_PLAYER_ARTWORK
9777 if (stored_player[i].artwork_element != artwork_element)
9778 stored_player[i].Frame = 0;
9781 stored_player[i].artwork_element = artwork_element;
9783 SetPlayerWaiting(&stored_player[i], FALSE);
9785 /* set number of special actions for bored and sleeping animation */
9786 stored_player[i].num_special_action_bored =
9787 get_num_special_action(artwork_element,
9788 ACTION_BORING_1, ACTION_BORING_LAST);
9789 stored_player[i].num_special_action_sleeping =
9790 get_num_special_action(artwork_element,
9791 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9798 /* ---------- CE actions ---------------------------------------------- */
9800 case CA_SET_CE_VALUE:
9802 #if USE_NEW_CUSTOM_VALUE
9803 int last_ce_value = CustomValue[x][y];
9805 CustomValue[x][y] = action_arg_number_new;
9807 if (CustomValue[x][y] != last_ce_value)
9809 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9810 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9812 if (CustomValue[x][y] == 0)
9814 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9815 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9823 case CA_SET_CE_SCORE:
9825 #if USE_NEW_CUSTOM_VALUE
9826 int last_ce_score = ei->collect_score;
9828 ei->collect_score = action_arg_number_new;
9830 if (ei->collect_score != last_ce_score)
9832 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9833 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9835 if (ei->collect_score == 0)
9839 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9840 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9843 This is a very special case that seems to be a mixture between
9844 CheckElementChange() and CheckTriggeredElementChange(): while
9845 the first one only affects single elements that are triggered
9846 directly, the second one affects multiple elements in the playfield
9847 that are triggered indirectly by another element. This is a third
9848 case: Changing the CE score always affects multiple identical CEs,
9849 so every affected CE must be checked, not only the single CE for
9850 which the CE score was changed in the first place (as every instance
9851 of that CE shares the same CE score, and therefore also can change)!
9853 SCAN_PLAYFIELD(xx, yy)
9855 if (Feld[xx][yy] == element)
9856 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9857 CE_SCORE_GETS_ZERO);
9866 /* ---------- engine actions ------------------------------------------ */
9868 case CA_SET_ENGINE_SCAN_MODE:
9870 InitPlayfieldScanMode(action_arg);
9880 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9882 int old_element = Feld[x][y];
9883 int new_element = GetElementFromGroupElement(element);
9884 int previous_move_direction = MovDir[x][y];
9885 #if USE_NEW_CUSTOM_VALUE
9886 int last_ce_value = CustomValue[x][y];
9888 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9889 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9890 boolean add_player_onto_element = (new_element_is_player &&
9891 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9892 /* this breaks SnakeBite when a snake is
9893 halfway through a door that closes */
9894 /* NOW FIXED AT LEVEL INIT IN files.c */
9895 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9897 IS_WALKABLE(old_element));
9900 /* check if element under the player changes from accessible to unaccessible
9901 (needed for special case of dropping element which then changes) */
9902 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9903 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9911 if (!add_player_onto_element)
9913 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9914 RemoveMovingField(x, y);
9918 Feld[x][y] = new_element;
9920 #if !USE_GFX_RESET_GFX_ANIMATION
9921 ResetGfxAnimation(x, y);
9922 ResetRandomAnimationValue(x, y);
9925 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9926 MovDir[x][y] = previous_move_direction;
9928 #if USE_NEW_CUSTOM_VALUE
9929 if (element_info[new_element].use_last_ce_value)
9930 CustomValue[x][y] = last_ce_value;
9933 InitField_WithBug1(x, y, FALSE);
9935 new_element = Feld[x][y]; /* element may have changed */
9937 #if USE_GFX_RESET_GFX_ANIMATION
9938 ResetGfxAnimation(x, y);
9939 ResetRandomAnimationValue(x, y);
9942 DrawLevelField(x, y);
9944 if (GFX_CRUMBLED(new_element))
9945 DrawLevelFieldCrumbledSandNeighbours(x, y);
9949 /* check if element under the player changes from accessible to unaccessible
9950 (needed for special case of dropping element which then changes) */
9951 /* (must be checked after creating new element for walkable group elements) */
9952 #if USE_FIX_KILLED_BY_NON_WALKABLE
9953 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9954 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9961 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9962 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9971 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9972 if (new_element_is_player)
9973 RelocatePlayer(x, y, new_element);
9976 ChangeCount[x][y]++; /* count number of changes in the same frame */
9978 TestIfBadThingTouchesPlayer(x, y);
9979 TestIfPlayerTouchesCustomElement(x, y);
9980 TestIfElementTouchesCustomElement(x, y);
9983 static void CreateField(int x, int y, int element)
9985 CreateFieldExt(x, y, element, FALSE);
9988 static void CreateElementFromChange(int x, int y, int element)
9990 element = GET_VALID_RUNTIME_ELEMENT(element);
9992 #if USE_STOP_CHANGED_ELEMENTS
9993 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9995 int old_element = Feld[x][y];
9997 /* prevent changed element from moving in same engine frame
9998 unless both old and new element can either fall or move */
9999 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10000 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10005 CreateFieldExt(x, y, element, TRUE);
10008 static boolean ChangeElement(int x, int y, int element, int page)
10010 struct ElementInfo *ei = &element_info[element];
10011 struct ElementChangeInfo *change = &ei->change_page[page];
10012 int ce_value = CustomValue[x][y];
10013 int ce_score = ei->collect_score;
10014 int target_element;
10015 int old_element = Feld[x][y];
10017 /* always use default change event to prevent running into a loop */
10018 if (ChangeEvent[x][y] == -1)
10019 ChangeEvent[x][y] = CE_DELAY;
10021 if (ChangeEvent[x][y] == CE_DELAY)
10023 /* reset actual trigger element, trigger player and action element */
10024 change->actual_trigger_element = EL_EMPTY;
10025 change->actual_trigger_player = EL_PLAYER_1;
10026 change->actual_trigger_side = CH_SIDE_NONE;
10027 change->actual_trigger_ce_value = 0;
10028 change->actual_trigger_ce_score = 0;
10031 /* do not change elements more than a specified maximum number of changes */
10032 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10035 ChangeCount[x][y]++; /* count number of changes in the same frame */
10037 if (change->explode)
10044 if (change->use_target_content)
10046 boolean complete_replace = TRUE;
10047 boolean can_replace[3][3];
10050 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10053 boolean is_walkable;
10054 boolean is_diggable;
10055 boolean is_collectible;
10056 boolean is_removable;
10057 boolean is_destructible;
10058 int ex = x + xx - 1;
10059 int ey = y + yy - 1;
10060 int content_element = change->target_content.e[xx][yy];
10063 can_replace[xx][yy] = TRUE;
10065 if (ex == x && ey == y) /* do not check changing element itself */
10068 if (content_element == EL_EMPTY_SPACE)
10070 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10075 if (!IN_LEV_FIELD(ex, ey))
10077 can_replace[xx][yy] = FALSE;
10078 complete_replace = FALSE;
10085 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10086 e = MovingOrBlocked2Element(ex, ey);
10088 is_empty = (IS_FREE(ex, ey) ||
10089 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10091 is_walkable = (is_empty || IS_WALKABLE(e));
10092 is_diggable = (is_empty || IS_DIGGABLE(e));
10093 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10094 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10095 is_removable = (is_diggable || is_collectible);
10097 can_replace[xx][yy] =
10098 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10099 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10100 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10101 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10102 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10103 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10104 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10106 if (!can_replace[xx][yy])
10107 complete_replace = FALSE;
10110 if (!change->only_if_complete || complete_replace)
10112 boolean something_has_changed = FALSE;
10114 if (change->only_if_complete && change->use_random_replace &&
10115 RND(100) < change->random_percentage)
10118 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10120 int ex = x + xx - 1;
10121 int ey = y + yy - 1;
10122 int content_element;
10124 if (can_replace[xx][yy] && (!change->use_random_replace ||
10125 RND(100) < change->random_percentage))
10127 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10128 RemoveMovingField(ex, ey);
10130 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10132 content_element = change->target_content.e[xx][yy];
10133 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10134 ce_value, ce_score);
10136 CreateElementFromChange(ex, ey, target_element);
10138 something_has_changed = TRUE;
10140 /* for symmetry reasons, freeze newly created border elements */
10141 if (ex != x || ey != y)
10142 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10146 if (something_has_changed)
10148 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10149 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10155 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10156 ce_value, ce_score);
10158 if (element == EL_DIAGONAL_GROWING ||
10159 element == EL_DIAGONAL_SHRINKING)
10161 target_element = Store[x][y];
10163 Store[x][y] = EL_EMPTY;
10166 CreateElementFromChange(x, y, target_element);
10168 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10169 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10172 /* this uses direct change before indirect change */
10173 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10178 #if USE_NEW_DELAYED_ACTION
10180 static void HandleElementChange(int x, int y, int page)
10182 int element = MovingOrBlocked2Element(x, y);
10183 struct ElementInfo *ei = &element_info[element];
10184 struct ElementChangeInfo *change = &ei->change_page[page];
10187 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10188 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10191 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10192 x, y, element, element_info[element].token_name);
10193 printf("HandleElementChange(): This should never happen!\n");
10198 /* this can happen with classic bombs on walkable, changing elements */
10199 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10202 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10203 ChangeDelay[x][y] = 0;
10209 if (ChangeDelay[x][y] == 0) /* initialize element change */
10211 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10213 if (change->can_change)
10216 /* !!! not clear why graphic animation should be reset at all here !!! */
10217 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10218 #if USE_GFX_RESET_WHEN_NOT_MOVING
10219 /* when a custom element is about to change (for example by change delay),
10220 do not reset graphic animation when the custom element is moving */
10221 if (!IS_MOVING(x, y))
10224 ResetGfxAnimation(x, y);
10225 ResetRandomAnimationValue(x, y);
10229 if (change->pre_change_function)
10230 change->pre_change_function(x, y);
10234 ChangeDelay[x][y]--;
10236 if (ChangeDelay[x][y] != 0) /* continue element change */
10238 if (change->can_change)
10240 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10242 if (IS_ANIMATED(graphic))
10243 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10245 if (change->change_function)
10246 change->change_function(x, y);
10249 else /* finish element change */
10251 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10253 page = ChangePage[x][y];
10254 ChangePage[x][y] = -1;
10256 change = &ei->change_page[page];
10259 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10261 ChangeDelay[x][y] = 1; /* try change after next move step */
10262 ChangePage[x][y] = page; /* remember page to use for change */
10267 if (change->can_change)
10269 if (ChangeElement(x, y, element, page))
10271 if (change->post_change_function)
10272 change->post_change_function(x, y);
10276 if (change->has_action)
10277 ExecuteCustomElementAction(x, y, element, page);
10283 static void HandleElementChange(int x, int y, int page)
10285 int element = MovingOrBlocked2Element(x, y);
10286 struct ElementInfo *ei = &element_info[element];
10287 struct ElementChangeInfo *change = &ei->change_page[page];
10290 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10293 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10294 x, y, element, element_info[element].token_name);
10295 printf("HandleElementChange(): This should never happen!\n");
10300 /* this can happen with classic bombs on walkable, changing elements */
10301 if (!CAN_CHANGE(element))
10304 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10305 ChangeDelay[x][y] = 0;
10311 if (ChangeDelay[x][y] == 0) /* initialize element change */
10313 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10315 ResetGfxAnimation(x, y);
10316 ResetRandomAnimationValue(x, y);
10318 if (change->pre_change_function)
10319 change->pre_change_function(x, y);
10322 ChangeDelay[x][y]--;
10324 if (ChangeDelay[x][y] != 0) /* continue element change */
10326 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10328 if (IS_ANIMATED(graphic))
10329 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10331 if (change->change_function)
10332 change->change_function(x, y);
10334 else /* finish element change */
10336 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10338 page = ChangePage[x][y];
10339 ChangePage[x][y] = -1;
10341 change = &ei->change_page[page];
10344 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10346 ChangeDelay[x][y] = 1; /* try change after next move step */
10347 ChangePage[x][y] = page; /* remember page to use for change */
10352 if (ChangeElement(x, y, element, page))
10354 if (change->post_change_function)
10355 change->post_change_function(x, y);
10362 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10363 int trigger_element,
10365 int trigger_player,
10369 boolean change_done_any = FALSE;
10370 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10373 if (!(trigger_events[trigger_element][trigger_event]))
10377 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10378 trigger_event, recursion_loop_depth, recursion_loop_detected,
10379 recursion_loop_element, EL_NAME(recursion_loop_element));
10382 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10384 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10386 int element = EL_CUSTOM_START + i;
10387 boolean change_done = FALSE;
10390 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10391 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10394 for (p = 0; p < element_info[element].num_change_pages; p++)
10396 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10398 if (change->can_change_or_has_action &&
10399 change->has_event[trigger_event] &&
10400 change->trigger_side & trigger_side &&
10401 change->trigger_player & trigger_player &&
10402 change->trigger_page & trigger_page_bits &&
10403 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10405 change->actual_trigger_element = trigger_element;
10406 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10407 change->actual_trigger_side = trigger_side;
10408 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10409 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10411 if ((change->can_change && !change_done) || change->has_action)
10415 SCAN_PLAYFIELD(x, y)
10417 if (Feld[x][y] == element)
10419 if (change->can_change && !change_done)
10421 ChangeDelay[x][y] = 1;
10422 ChangeEvent[x][y] = trigger_event;
10424 HandleElementChange(x, y, p);
10426 #if USE_NEW_DELAYED_ACTION
10427 else if (change->has_action)
10429 ExecuteCustomElementAction(x, y, element, p);
10430 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10433 if (change->has_action)
10435 ExecuteCustomElementAction(x, y, element, p);
10436 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10442 if (change->can_change)
10444 change_done = TRUE;
10445 change_done_any = TRUE;
10452 RECURSION_LOOP_DETECTION_END();
10454 return change_done_any;
10457 static boolean CheckElementChangeExt(int x, int y,
10459 int trigger_element,
10461 int trigger_player,
10464 boolean change_done = FALSE;
10467 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10468 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10471 if (Feld[x][y] == EL_BLOCKED)
10473 Blocked2Moving(x, y, &x, &y);
10474 element = Feld[x][y];
10478 /* check if element has already changed */
10479 if (Feld[x][y] != element)
10482 /* check if element has already changed or is about to change after moving */
10483 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10484 Feld[x][y] != element) ||
10486 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10487 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10488 ChangePage[x][y] != -1)))
10493 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10494 trigger_event, recursion_loop_depth, recursion_loop_detected,
10495 recursion_loop_element, EL_NAME(recursion_loop_element));
10498 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10500 for (p = 0; p < element_info[element].num_change_pages; p++)
10502 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10504 /* check trigger element for all events where the element that is checked
10505 for changing interacts with a directly adjacent element -- this is
10506 different to element changes that affect other elements to change on the
10507 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10508 boolean check_trigger_element =
10509 (trigger_event == CE_TOUCHING_X ||
10510 trigger_event == CE_HITTING_X ||
10511 trigger_event == CE_HIT_BY_X ||
10513 /* this one was forgotten until 3.2.3 */
10514 trigger_event == CE_DIGGING_X);
10517 if (change->can_change_or_has_action &&
10518 change->has_event[trigger_event] &&
10519 change->trigger_side & trigger_side &&
10520 change->trigger_player & trigger_player &&
10521 (!check_trigger_element ||
10522 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10524 change->actual_trigger_element = trigger_element;
10525 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10526 change->actual_trigger_side = trigger_side;
10527 change->actual_trigger_ce_value = CustomValue[x][y];
10528 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10530 /* special case: trigger element not at (x,y) position for some events */
10531 if (check_trigger_element)
10543 { 0, 0 }, { 0, 0 }, { 0, 0 },
10547 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10548 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10550 change->actual_trigger_ce_value = CustomValue[xx][yy];
10551 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10554 if (change->can_change && !change_done)
10556 ChangeDelay[x][y] = 1;
10557 ChangeEvent[x][y] = trigger_event;
10559 HandleElementChange(x, y, p);
10561 change_done = TRUE;
10563 #if USE_NEW_DELAYED_ACTION
10564 else if (change->has_action)
10566 ExecuteCustomElementAction(x, y, element, p);
10567 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10570 if (change->has_action)
10572 ExecuteCustomElementAction(x, y, element, p);
10573 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10579 RECURSION_LOOP_DETECTION_END();
10581 return change_done;
10584 static void PlayPlayerSound(struct PlayerInfo *player)
10586 int jx = player->jx, jy = player->jy;
10587 int sound_element = player->artwork_element;
10588 int last_action = player->last_action_waiting;
10589 int action = player->action_waiting;
10591 if (player->is_waiting)
10593 if (action != last_action)
10594 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10596 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10600 if (action != last_action)
10601 StopSound(element_info[sound_element].sound[last_action]);
10603 if (last_action == ACTION_SLEEPING)
10604 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10608 static void PlayAllPlayersSound()
10612 for (i = 0; i < MAX_PLAYERS; i++)
10613 if (stored_player[i].active)
10614 PlayPlayerSound(&stored_player[i]);
10617 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10619 boolean last_waiting = player->is_waiting;
10620 int move_dir = player->MovDir;
10622 player->dir_waiting = move_dir;
10623 player->last_action_waiting = player->action_waiting;
10627 if (!last_waiting) /* not waiting -> waiting */
10629 player->is_waiting = TRUE;
10631 player->frame_counter_bored =
10633 game.player_boring_delay_fixed +
10634 GetSimpleRandom(game.player_boring_delay_random);
10635 player->frame_counter_sleeping =
10637 game.player_sleeping_delay_fixed +
10638 GetSimpleRandom(game.player_sleeping_delay_random);
10640 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10643 if (game.player_sleeping_delay_fixed +
10644 game.player_sleeping_delay_random > 0 &&
10645 player->anim_delay_counter == 0 &&
10646 player->post_delay_counter == 0 &&
10647 FrameCounter >= player->frame_counter_sleeping)
10648 player->is_sleeping = TRUE;
10649 else if (game.player_boring_delay_fixed +
10650 game.player_boring_delay_random > 0 &&
10651 FrameCounter >= player->frame_counter_bored)
10652 player->is_bored = TRUE;
10654 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10655 player->is_bored ? ACTION_BORING :
10658 if (player->is_sleeping && player->use_murphy)
10660 /* special case for sleeping Murphy when leaning against non-free tile */
10662 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10663 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10664 !IS_MOVING(player->jx - 1, player->jy)))
10665 move_dir = MV_LEFT;
10666 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10667 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10668 !IS_MOVING(player->jx + 1, player->jy)))
10669 move_dir = MV_RIGHT;
10671 player->is_sleeping = FALSE;
10673 player->dir_waiting = move_dir;
10676 if (player->is_sleeping)
10678 if (player->num_special_action_sleeping > 0)
10680 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10682 int last_special_action = player->special_action_sleeping;
10683 int num_special_action = player->num_special_action_sleeping;
10684 int special_action =
10685 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10686 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10687 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10688 last_special_action + 1 : ACTION_SLEEPING);
10689 int special_graphic =
10690 el_act_dir2img(player->artwork_element, special_action, move_dir);
10692 player->anim_delay_counter =
10693 graphic_info[special_graphic].anim_delay_fixed +
10694 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10695 player->post_delay_counter =
10696 graphic_info[special_graphic].post_delay_fixed +
10697 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10699 player->special_action_sleeping = special_action;
10702 if (player->anim_delay_counter > 0)
10704 player->action_waiting = player->special_action_sleeping;
10705 player->anim_delay_counter--;
10707 else if (player->post_delay_counter > 0)
10709 player->post_delay_counter--;
10713 else if (player->is_bored)
10715 if (player->num_special_action_bored > 0)
10717 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10719 int special_action =
10720 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10721 int special_graphic =
10722 el_act_dir2img(player->artwork_element, special_action, move_dir);
10724 player->anim_delay_counter =
10725 graphic_info[special_graphic].anim_delay_fixed +
10726 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10727 player->post_delay_counter =
10728 graphic_info[special_graphic].post_delay_fixed +
10729 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10731 player->special_action_bored = special_action;
10734 if (player->anim_delay_counter > 0)
10736 player->action_waiting = player->special_action_bored;
10737 player->anim_delay_counter--;
10739 else if (player->post_delay_counter > 0)
10741 player->post_delay_counter--;
10746 else if (last_waiting) /* waiting -> not waiting */
10748 player->is_waiting = FALSE;
10749 player->is_bored = FALSE;
10750 player->is_sleeping = FALSE;
10752 player->frame_counter_bored = -1;
10753 player->frame_counter_sleeping = -1;
10755 player->anim_delay_counter = 0;
10756 player->post_delay_counter = 0;
10758 player->dir_waiting = player->MovDir;
10759 player->action_waiting = ACTION_DEFAULT;
10761 player->special_action_bored = ACTION_DEFAULT;
10762 player->special_action_sleeping = ACTION_DEFAULT;
10766 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10768 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10769 int left = player_action & JOY_LEFT;
10770 int right = player_action & JOY_RIGHT;
10771 int up = player_action & JOY_UP;
10772 int down = player_action & JOY_DOWN;
10773 int button1 = player_action & JOY_BUTTON_1;
10774 int button2 = player_action & JOY_BUTTON_2;
10775 int dx = (left ? -1 : right ? 1 : 0);
10776 int dy = (up ? -1 : down ? 1 : 0);
10778 if (!player->active || tape.pausing)
10784 snapped = SnapField(player, dx, dy);
10788 dropped = DropElement(player);
10790 moved = MovePlayer(player, dx, dy);
10793 if (tape.single_step && tape.recording && !tape.pausing)
10795 if (button1 || (dropped && !moved))
10797 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10798 SnapField(player, 0, 0); /* stop snapping */
10802 SetPlayerWaiting(player, FALSE);
10804 return player_action;
10808 /* no actions for this player (no input at player's configured device) */
10810 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10811 SnapField(player, 0, 0);
10812 CheckGravityMovementWhenNotMoving(player);
10814 if (player->MovPos == 0)
10815 SetPlayerWaiting(player, TRUE);
10817 if (player->MovPos == 0) /* needed for tape.playing */
10818 player->is_moving = FALSE;
10820 player->is_dropping = FALSE;
10821 player->is_dropping_pressed = FALSE;
10822 player->drop_pressed_delay = 0;
10828 static void CheckLevelTime()
10832 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10834 if (level.native_em_level->lev->home == 0) /* all players at home */
10836 PlayerWins(local_player);
10838 AllPlayersGone = TRUE;
10840 level.native_em_level->lev->home = -1;
10843 if (level.native_em_level->ply[0]->alive == 0 &&
10844 level.native_em_level->ply[1]->alive == 0 &&
10845 level.native_em_level->ply[2]->alive == 0 &&
10846 level.native_em_level->ply[3]->alive == 0) /* all dead */
10847 AllPlayersGone = TRUE;
10850 if (TimeFrames >= FRAMES_PER_SECOND)
10855 for (i = 0; i < MAX_PLAYERS; i++)
10857 struct PlayerInfo *player = &stored_player[i];
10859 if (SHIELD_ON(player))
10861 player->shield_normal_time_left--;
10863 if (player->shield_deadly_time_left > 0)
10864 player->shield_deadly_time_left--;
10868 if (!local_player->LevelSolved && !level.use_step_counter)
10876 if (TimeLeft <= 10 && setup.time_limit)
10877 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10880 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10882 DisplayGameControlValues();
10884 DrawGameValue_Time(TimeLeft);
10887 if (!TimeLeft && setup.time_limit)
10889 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10890 level.native_em_level->lev->killed_out_of_time = TRUE;
10892 for (i = 0; i < MAX_PLAYERS; i++)
10893 KillPlayer(&stored_player[i]);
10897 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10899 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10901 DisplayGameControlValues();
10904 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10905 DrawGameValue_Time(TimePlayed);
10908 level.native_em_level->lev->time =
10909 (level.time == 0 ? TimePlayed : TimeLeft);
10912 if (tape.recording || tape.playing)
10913 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10916 UpdateGameDoorValues();
10917 DrawGameDoorValues();
10920 void AdvanceFrameAndPlayerCounters(int player_nr)
10924 /* advance frame counters (global frame counter and time frame counter) */
10928 /* advance player counters (counters for move delay, move animation etc.) */
10929 for (i = 0; i < MAX_PLAYERS; i++)
10931 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10932 int move_delay_value = stored_player[i].move_delay_value;
10933 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10935 if (!advance_player_counters) /* not all players may be affected */
10938 #if USE_NEW_PLAYER_ANIM
10939 if (move_frames == 0) /* less than one move per game frame */
10941 int stepsize = TILEX / move_delay_value;
10942 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10943 int count = (stored_player[i].is_moving ?
10944 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10946 if (count % delay == 0)
10951 stored_player[i].Frame += move_frames;
10953 if (stored_player[i].MovPos != 0)
10954 stored_player[i].StepFrame += move_frames;
10956 if (stored_player[i].move_delay > 0)
10957 stored_player[i].move_delay--;
10959 /* due to bugs in previous versions, counter must count up, not down */
10960 if (stored_player[i].push_delay != -1)
10961 stored_player[i].push_delay++;
10963 if (stored_player[i].drop_delay > 0)
10964 stored_player[i].drop_delay--;
10966 if (stored_player[i].is_dropping_pressed)
10967 stored_player[i].drop_pressed_delay++;
10971 void StartGameActions(boolean init_network_game, boolean record_tape,
10974 unsigned long new_random_seed = InitRND(random_seed);
10977 TapeStartRecording(new_random_seed);
10979 #if defined(NETWORK_AVALIABLE)
10980 if (init_network_game)
10982 SendToServer_StartPlaying();
10993 static unsigned long game_frame_delay = 0;
10994 unsigned long game_frame_delay_value;
10995 byte *recorded_player_action;
10996 byte summarized_player_action = 0;
10997 byte tape_action[MAX_PLAYERS];
11000 /* detect endless loops, caused by custom element programming */
11001 if (recursion_loop_detected && recursion_loop_depth == 0)
11003 char *message = getStringCat3("Internal Error ! Element ",
11004 EL_NAME(recursion_loop_element),
11005 " caused endless loop ! Quit the game ?");
11007 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11008 EL_NAME(recursion_loop_element));
11010 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11012 recursion_loop_detected = FALSE; /* if game should be continued */
11019 if (game.restart_level)
11020 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11022 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11024 if (level.native_em_level->lev->home == 0) /* all players at home */
11026 PlayerWins(local_player);
11028 AllPlayersGone = TRUE;
11030 level.native_em_level->lev->home = -1;
11033 if (level.native_em_level->ply[0]->alive == 0 &&
11034 level.native_em_level->ply[1]->alive == 0 &&
11035 level.native_em_level->ply[2]->alive == 0 &&
11036 level.native_em_level->ply[3]->alive == 0) /* all dead */
11037 AllPlayersGone = TRUE;
11040 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11043 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11046 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11049 game_frame_delay_value =
11050 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11052 if (tape.playing && tape.warp_forward && !tape.pausing)
11053 game_frame_delay_value = 0;
11055 /* ---------- main game synchronization point ---------- */
11057 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11059 if (network_playing && !network_player_action_received)
11061 /* try to get network player actions in time */
11063 #if defined(NETWORK_AVALIABLE)
11064 /* last chance to get network player actions without main loop delay */
11065 HandleNetworking();
11068 /* game was quit by network peer */
11069 if (game_status != GAME_MODE_PLAYING)
11072 if (!network_player_action_received)
11073 return; /* failed to get network player actions in time */
11075 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11081 /* at this point we know that we really continue executing the game */
11083 network_player_action_received = FALSE;
11085 /* when playing tape, read previously recorded player input from tape data */
11086 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11089 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11094 if (tape.set_centered_player)
11096 game.centered_player_nr_next = tape.centered_player_nr_next;
11097 game.set_centered_player = TRUE;
11100 for (i = 0; i < MAX_PLAYERS; i++)
11102 summarized_player_action |= stored_player[i].action;
11104 if (!network_playing)
11105 stored_player[i].effective_action = stored_player[i].action;
11108 #if defined(NETWORK_AVALIABLE)
11109 if (network_playing)
11110 SendToServer_MovePlayer(summarized_player_action);
11113 if (!options.network && !setup.team_mode)
11114 local_player->effective_action = summarized_player_action;
11116 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11118 for (i = 0; i < MAX_PLAYERS; i++)
11119 stored_player[i].effective_action =
11120 (i == game.centered_player_nr ? summarized_player_action : 0);
11123 if (recorded_player_action != NULL)
11124 for (i = 0; i < MAX_PLAYERS; i++)
11125 stored_player[i].effective_action = recorded_player_action[i];
11127 for (i = 0; i < MAX_PLAYERS; i++)
11129 tape_action[i] = stored_player[i].effective_action;
11131 /* (this can only happen in the R'n'D game engine) */
11132 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11133 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11136 /* only record actions from input devices, but not programmed actions */
11137 if (tape.recording)
11138 TapeRecordAction(tape_action);
11140 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11142 GameActions_EM_Main();
11150 void GameActions_EM_Main()
11152 byte effective_action[MAX_PLAYERS];
11153 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11156 for (i = 0; i < MAX_PLAYERS; i++)
11157 effective_action[i] = stored_player[i].effective_action;
11159 GameActions_EM(effective_action, warp_mode);
11163 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11166 void GameActions_RND()
11168 int magic_wall_x = 0, magic_wall_y = 0;
11169 int i, x, y, element, graphic;
11171 InitPlayfieldScanModeVars();
11173 #if USE_ONE_MORE_CHANGE_PER_FRAME
11174 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11176 SCAN_PLAYFIELD(x, y)
11178 ChangeCount[x][y] = 0;
11179 ChangeEvent[x][y] = -1;
11184 if (game.set_centered_player)
11186 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11188 /* switching to "all players" only possible if all players fit to screen */
11189 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11191 game.centered_player_nr_next = game.centered_player_nr;
11192 game.set_centered_player = FALSE;
11195 /* do not switch focus to non-existing (or non-active) player */
11196 if (game.centered_player_nr_next >= 0 &&
11197 !stored_player[game.centered_player_nr_next].active)
11199 game.centered_player_nr_next = game.centered_player_nr;
11200 game.set_centered_player = FALSE;
11204 if (game.set_centered_player &&
11205 ScreenMovPos == 0) /* screen currently aligned at tile position */
11209 if (game.centered_player_nr_next == -1)
11211 setScreenCenteredToAllPlayers(&sx, &sy);
11215 sx = stored_player[game.centered_player_nr_next].jx;
11216 sy = stored_player[game.centered_player_nr_next].jy;
11219 game.centered_player_nr = game.centered_player_nr_next;
11220 game.set_centered_player = FALSE;
11222 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11223 DrawGameDoorValues();
11226 for (i = 0; i < MAX_PLAYERS; i++)
11228 int actual_player_action = stored_player[i].effective_action;
11231 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11232 - rnd_equinox_tetrachloride 048
11233 - rnd_equinox_tetrachloride_ii 096
11234 - rnd_emanuel_schmieg 002
11235 - doctor_sloan_ww 001, 020
11237 if (stored_player[i].MovPos == 0)
11238 CheckGravityMovement(&stored_player[i]);
11241 /* overwrite programmed action with tape action */
11242 if (stored_player[i].programmed_action)
11243 actual_player_action = stored_player[i].programmed_action;
11245 PlayerActions(&stored_player[i], actual_player_action);
11247 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11250 ScrollScreen(NULL, SCROLL_GO_ON);
11252 /* for backwards compatibility, the following code emulates a fixed bug that
11253 occured when pushing elements (causing elements that just made their last
11254 pushing step to already (if possible) make their first falling step in the
11255 same game frame, which is bad); this code is also needed to use the famous
11256 "spring push bug" which is used in older levels and might be wanted to be
11257 used also in newer levels, but in this case the buggy pushing code is only
11258 affecting the "spring" element and no other elements */
11260 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11262 for (i = 0; i < MAX_PLAYERS; i++)
11264 struct PlayerInfo *player = &stored_player[i];
11265 int x = player->jx;
11266 int y = player->jy;
11268 if (player->active && player->is_pushing && player->is_moving &&
11270 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11271 Feld[x][y] == EL_SPRING))
11273 ContinueMoving(x, y);
11275 /* continue moving after pushing (this is actually a bug) */
11276 if (!IS_MOVING(x, y))
11277 Stop[x][y] = FALSE;
11283 debug_print_timestamp(0, "start main loop profiling");
11286 SCAN_PLAYFIELD(x, y)
11288 ChangeCount[x][y] = 0;
11289 ChangeEvent[x][y] = -1;
11291 /* this must be handled before main playfield loop */
11292 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11295 if (MovDelay[x][y] <= 0)
11299 #if USE_NEW_SNAP_DELAY
11300 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11303 if (MovDelay[x][y] <= 0)
11306 DrawLevelField(x, y);
11308 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11314 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11316 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11317 printf("GameActions(): This should never happen!\n");
11319 ChangePage[x][y] = -1;
11323 Stop[x][y] = FALSE;
11324 if (WasJustMoving[x][y] > 0)
11325 WasJustMoving[x][y]--;
11326 if (WasJustFalling[x][y] > 0)
11327 WasJustFalling[x][y]--;
11328 if (CheckCollision[x][y] > 0)
11329 CheckCollision[x][y]--;
11330 if (CheckImpact[x][y] > 0)
11331 CheckImpact[x][y]--;
11335 /* reset finished pushing action (not done in ContinueMoving() to allow
11336 continuous pushing animation for elements with zero push delay) */
11337 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11339 ResetGfxAnimation(x, y);
11340 DrawLevelField(x, y);
11344 if (IS_BLOCKED(x, y))
11348 Blocked2Moving(x, y, &oldx, &oldy);
11349 if (!IS_MOVING(oldx, oldy))
11351 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11352 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11353 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11354 printf("GameActions(): This should never happen!\n");
11361 debug_print_timestamp(0, "- time for pre-main loop:");
11364 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11365 SCAN_PLAYFIELD(x, y)
11367 element = Feld[x][y];
11368 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11373 int element2 = element;
11374 int graphic2 = graphic;
11376 int element2 = Feld[x][y];
11377 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11379 int last_gfx_frame = GfxFrame[x][y];
11381 if (graphic_info[graphic2].anim_global_sync)
11382 GfxFrame[x][y] = FrameCounter;
11383 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11384 GfxFrame[x][y] = CustomValue[x][y];
11385 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11386 GfxFrame[x][y] = element_info[element2].collect_score;
11387 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11388 GfxFrame[x][y] = ChangeDelay[x][y];
11390 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11391 DrawLevelGraphicAnimation(x, y, graphic2);
11394 ResetGfxFrame(x, y, TRUE);
11398 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11399 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11400 ResetRandomAnimationValue(x, y);
11404 SetRandomAnimationValue(x, y);
11408 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11411 #endif // -------------------- !!! TEST ONLY !!! --------------------
11414 debug_print_timestamp(0, "- time for TEST loop: -->");
11417 SCAN_PLAYFIELD(x, y)
11419 element = Feld[x][y];
11420 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11422 ResetGfxFrame(x, y, TRUE);
11424 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11425 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11426 ResetRandomAnimationValue(x, y);
11428 SetRandomAnimationValue(x, y);
11430 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11432 if (IS_INACTIVE(element))
11434 if (IS_ANIMATED(graphic))
11435 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11440 /* this may take place after moving, so 'element' may have changed */
11441 if (IS_CHANGING(x, y) &&
11442 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11444 int page = element_info[element].event_page_nr[CE_DELAY];
11447 HandleElementChange(x, y, page);
11449 if (CAN_CHANGE(element))
11450 HandleElementChange(x, y, page);
11452 if (HAS_ACTION(element))
11453 ExecuteCustomElementAction(x, y, element, page);
11456 element = Feld[x][y];
11457 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11460 #if 0 // ---------------------------------------------------------------------
11462 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11466 element = Feld[x][y];
11467 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11469 if (IS_ANIMATED(graphic) &&
11470 !IS_MOVING(x, y) &&
11472 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11474 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11475 DrawTwinkleOnField(x, y);
11477 else if (IS_MOVING(x, y))
11478 ContinueMoving(x, y);
11485 case EL_EM_EXIT_OPEN:
11486 case EL_SP_EXIT_OPEN:
11487 case EL_STEEL_EXIT_OPEN:
11488 case EL_EM_STEEL_EXIT_OPEN:
11489 case EL_SP_TERMINAL:
11490 case EL_SP_TERMINAL_ACTIVE:
11491 case EL_EXTRA_TIME:
11492 case EL_SHIELD_NORMAL:
11493 case EL_SHIELD_DEADLY:
11494 if (IS_ANIMATED(graphic))
11495 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11498 case EL_DYNAMITE_ACTIVE:
11499 case EL_EM_DYNAMITE_ACTIVE:
11500 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11501 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11502 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11503 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11504 case EL_SP_DISK_RED_ACTIVE:
11505 CheckDynamite(x, y);
11508 case EL_AMOEBA_GROWING:
11509 AmoebeWaechst(x, y);
11512 case EL_AMOEBA_SHRINKING:
11513 AmoebaDisappearing(x, y);
11516 #if !USE_NEW_AMOEBA_CODE
11517 case EL_AMOEBA_WET:
11518 case EL_AMOEBA_DRY:
11519 case EL_AMOEBA_FULL:
11521 case EL_EMC_DRIPPER:
11522 AmoebeAbleger(x, y);
11526 case EL_GAME_OF_LIFE:
11531 case EL_EXIT_CLOSED:
11535 case EL_EM_EXIT_CLOSED:
11539 case EL_STEEL_EXIT_CLOSED:
11540 CheckExitSteel(x, y);
11543 case EL_EM_STEEL_EXIT_CLOSED:
11544 CheckExitSteelEM(x, y);
11547 case EL_SP_EXIT_CLOSED:
11551 case EL_EXPANDABLE_WALL_GROWING:
11552 case EL_EXPANDABLE_STEELWALL_GROWING:
11553 MauerWaechst(x, y);
11556 case EL_EXPANDABLE_WALL:
11557 case EL_EXPANDABLE_WALL_HORIZONTAL:
11558 case EL_EXPANDABLE_WALL_VERTICAL:
11559 case EL_EXPANDABLE_WALL_ANY:
11560 case EL_BD_EXPANDABLE_WALL:
11561 MauerAbleger(x, y);
11564 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11565 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11566 case EL_EXPANDABLE_STEELWALL_ANY:
11567 MauerAblegerStahl(x, y);
11571 CheckForDragon(x, y);
11577 case EL_ELEMENT_SNAPPING:
11578 case EL_DIAGONAL_SHRINKING:
11579 case EL_DIAGONAL_GROWING:
11582 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11584 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11589 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11590 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11595 #else // ---------------------------------------------------------------------
11597 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11601 element = Feld[x][y];
11602 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11604 if (IS_ANIMATED(graphic) &&
11605 !IS_MOVING(x, y) &&
11607 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11609 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11610 DrawTwinkleOnField(x, y);
11612 else if ((element == EL_ACID ||
11613 element == EL_EXIT_OPEN ||
11614 element == EL_EM_EXIT_OPEN ||
11615 element == EL_SP_EXIT_OPEN ||
11616 element == EL_STEEL_EXIT_OPEN ||
11617 element == EL_EM_STEEL_EXIT_OPEN ||
11618 element == EL_SP_TERMINAL ||
11619 element == EL_SP_TERMINAL_ACTIVE ||
11620 element == EL_EXTRA_TIME ||
11621 element == EL_SHIELD_NORMAL ||
11622 element == EL_SHIELD_DEADLY) &&
11623 IS_ANIMATED(graphic))
11624 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11625 else if (IS_MOVING(x, y))
11626 ContinueMoving(x, y);
11627 else if (IS_ACTIVE_BOMB(element))
11628 CheckDynamite(x, y);
11629 else if (element == EL_AMOEBA_GROWING)
11630 AmoebeWaechst(x, y);
11631 else if (element == EL_AMOEBA_SHRINKING)
11632 AmoebaDisappearing(x, y);
11634 #if !USE_NEW_AMOEBA_CODE
11635 else if (IS_AMOEBALIVE(element))
11636 AmoebeAbleger(x, y);
11639 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11641 else if (element == EL_EXIT_CLOSED)
11643 else if (element == EL_EM_EXIT_CLOSED)
11645 else if (element == EL_STEEL_EXIT_CLOSED)
11646 CheckExitSteel(x, y);
11647 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11648 CheckExitSteelEM(x, y);
11649 else if (element == EL_SP_EXIT_CLOSED)
11651 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11652 element == EL_EXPANDABLE_STEELWALL_GROWING)
11653 MauerWaechst(x, y);
11654 else if (element == EL_EXPANDABLE_WALL ||
11655 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11656 element == EL_EXPANDABLE_WALL_VERTICAL ||
11657 element == EL_EXPANDABLE_WALL_ANY ||
11658 element == EL_BD_EXPANDABLE_WALL)
11659 MauerAbleger(x, y);
11660 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11661 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11662 element == EL_EXPANDABLE_STEELWALL_ANY)
11663 MauerAblegerStahl(x, y);
11664 else if (element == EL_FLAMES)
11665 CheckForDragon(x, y);
11666 else if (element == EL_EXPLOSION)
11667 ; /* drawing of correct explosion animation is handled separately */
11668 else if (element == EL_ELEMENT_SNAPPING ||
11669 element == EL_DIAGONAL_SHRINKING ||
11670 element == EL_DIAGONAL_GROWING)
11672 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11674 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11676 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11677 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11679 #endif // ---------------------------------------------------------------------
11681 if (IS_BELT_ACTIVE(element))
11682 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11684 if (game.magic_wall_active)
11686 int jx = local_player->jx, jy = local_player->jy;
11688 /* play the element sound at the position nearest to the player */
11689 if ((element == EL_MAGIC_WALL_FULL ||
11690 element == EL_MAGIC_WALL_ACTIVE ||
11691 element == EL_MAGIC_WALL_EMPTYING ||
11692 element == EL_BD_MAGIC_WALL_FULL ||
11693 element == EL_BD_MAGIC_WALL_ACTIVE ||
11694 element == EL_BD_MAGIC_WALL_EMPTYING ||
11695 element == EL_DC_MAGIC_WALL_FULL ||
11696 element == EL_DC_MAGIC_WALL_ACTIVE ||
11697 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11698 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11707 debug_print_timestamp(0, "- time for MAIN loop: -->");
11710 #if USE_NEW_AMOEBA_CODE
11711 /* new experimental amoeba growth stuff */
11712 if (!(FrameCounter % 8))
11714 static unsigned long random = 1684108901;
11716 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11718 x = RND(lev_fieldx);
11719 y = RND(lev_fieldy);
11720 element = Feld[x][y];
11722 if (!IS_PLAYER(x,y) &&
11723 (element == EL_EMPTY ||
11724 CAN_GROW_INTO(element) ||
11725 element == EL_QUICKSAND_EMPTY ||
11726 element == EL_QUICKSAND_FAST_EMPTY ||
11727 element == EL_ACID_SPLASH_LEFT ||
11728 element == EL_ACID_SPLASH_RIGHT))
11730 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11731 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11732 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11733 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11734 Feld[x][y] = EL_AMOEBA_DROP;
11737 random = random * 129 + 1;
11743 if (game.explosions_delayed)
11746 game.explosions_delayed = FALSE;
11748 SCAN_PLAYFIELD(x, y)
11750 element = Feld[x][y];
11752 if (ExplodeField[x][y])
11753 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11754 else if (element == EL_EXPLOSION)
11755 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11757 ExplodeField[x][y] = EX_TYPE_NONE;
11760 game.explosions_delayed = TRUE;
11763 if (game.magic_wall_active)
11765 if (!(game.magic_wall_time_left % 4))
11767 int element = Feld[magic_wall_x][magic_wall_y];
11769 if (element == EL_BD_MAGIC_WALL_FULL ||
11770 element == EL_BD_MAGIC_WALL_ACTIVE ||
11771 element == EL_BD_MAGIC_WALL_EMPTYING)
11772 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11773 else if (element == EL_DC_MAGIC_WALL_FULL ||
11774 element == EL_DC_MAGIC_WALL_ACTIVE ||
11775 element == EL_DC_MAGIC_WALL_EMPTYING)
11776 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11778 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11781 if (game.magic_wall_time_left > 0)
11783 game.magic_wall_time_left--;
11785 if (!game.magic_wall_time_left)
11787 SCAN_PLAYFIELD(x, y)
11789 element = Feld[x][y];
11791 if (element == EL_MAGIC_WALL_ACTIVE ||
11792 element == EL_MAGIC_WALL_FULL)
11794 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11795 DrawLevelField(x, y);
11797 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11798 element == EL_BD_MAGIC_WALL_FULL)
11800 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11801 DrawLevelField(x, y);
11803 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11804 element == EL_DC_MAGIC_WALL_FULL)
11806 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11807 DrawLevelField(x, y);
11811 game.magic_wall_active = FALSE;
11816 if (game.light_time_left > 0)
11818 game.light_time_left--;
11820 if (game.light_time_left == 0)
11821 RedrawAllLightSwitchesAndInvisibleElements();
11824 if (game.timegate_time_left > 0)
11826 game.timegate_time_left--;
11828 if (game.timegate_time_left == 0)
11829 CloseAllOpenTimegates();
11832 if (game.lenses_time_left > 0)
11834 game.lenses_time_left--;
11836 if (game.lenses_time_left == 0)
11837 RedrawAllInvisibleElementsForLenses();
11840 if (game.magnify_time_left > 0)
11842 game.magnify_time_left--;
11844 if (game.magnify_time_left == 0)
11845 RedrawAllInvisibleElementsForMagnifier();
11848 for (i = 0; i < MAX_PLAYERS; i++)
11850 struct PlayerInfo *player = &stored_player[i];
11852 if (SHIELD_ON(player))
11854 if (player->shield_deadly_time_left)
11855 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11856 else if (player->shield_normal_time_left)
11857 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11864 PlayAllPlayersSound();
11866 if (options.debug) /* calculate frames per second */
11868 static unsigned long fps_counter = 0;
11869 static int fps_frames = 0;
11870 unsigned long fps_delay_ms = Counter() - fps_counter;
11874 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11876 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11879 fps_counter = Counter();
11882 redraw_mask |= REDRAW_FPS;
11885 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11887 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11889 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11891 local_player->show_envelope = 0;
11895 debug_print_timestamp(0, "stop main loop profiling ");
11896 printf("----------------------------------------------------------\n");
11899 /* use random number generator in every frame to make it less predictable */
11900 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11904 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11906 int min_x = x, min_y = y, max_x = x, max_y = y;
11909 for (i = 0; i < MAX_PLAYERS; i++)
11911 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11913 if (!stored_player[i].active || &stored_player[i] == player)
11916 min_x = MIN(min_x, jx);
11917 min_y = MIN(min_y, jy);
11918 max_x = MAX(max_x, jx);
11919 max_y = MAX(max_y, jy);
11922 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11925 static boolean AllPlayersInVisibleScreen()
11929 for (i = 0; i < MAX_PLAYERS; i++)
11931 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11933 if (!stored_player[i].active)
11936 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11943 void ScrollLevel(int dx, int dy)
11946 static Bitmap *bitmap_db_field2 = NULL;
11947 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11954 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11955 /* only horizontal XOR vertical scroll direction allowed */
11956 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11961 if (bitmap_db_field2 == NULL)
11962 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11964 /* needed when blitting directly to same bitmap -- should not be needed with
11965 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11966 BlitBitmap(drawto_field, bitmap_db_field2,
11967 FX + TILEX * (dx == -1) - softscroll_offset,
11968 FY + TILEY * (dy == -1) - softscroll_offset,
11969 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11970 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11971 FX + TILEX * (dx == 1) - softscroll_offset,
11972 FY + TILEY * (dy == 1) - softscroll_offset);
11973 BlitBitmap(bitmap_db_field2, drawto_field,
11974 FX + TILEX * (dx == 1) - softscroll_offset,
11975 FY + TILEY * (dy == 1) - softscroll_offset,
11976 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11977 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11978 FX + TILEX * (dx == 1) - softscroll_offset,
11979 FY + TILEY * (dy == 1) - softscroll_offset);
11984 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11985 int xsize = (BX2 - BX1 + 1);
11986 int ysize = (BY2 - BY1 + 1);
11987 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11988 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11989 int step = (start < end ? +1 : -1);
11991 for (i = start; i != end; i += step)
11993 BlitBitmap(drawto_field, drawto_field,
11994 FX + TILEX * (dx != 0 ? i + step : 0),
11995 FY + TILEY * (dy != 0 ? i + step : 0),
11996 TILEX * (dx != 0 ? 1 : xsize),
11997 TILEY * (dy != 0 ? 1 : ysize),
11998 FX + TILEX * (dx != 0 ? i : 0),
11999 FY + TILEY * (dy != 0 ? i : 0));
12004 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12006 BlitBitmap(drawto_field, drawto_field,
12007 FX + TILEX * (dx == -1) - softscroll_offset,
12008 FY + TILEY * (dy == -1) - softscroll_offset,
12009 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12010 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12011 FX + TILEX * (dx == 1) - softscroll_offset,
12012 FY + TILEY * (dy == 1) - softscroll_offset);
12018 x = (dx == 1 ? BX1 : BX2);
12019 for (y = BY1; y <= BY2; y++)
12020 DrawScreenField(x, y);
12025 y = (dy == 1 ? BY1 : BY2);
12026 for (x = BX1; x <= BX2; x++)
12027 DrawScreenField(x, y);
12030 redraw_mask |= REDRAW_FIELD;
12033 static boolean canFallDown(struct PlayerInfo *player)
12035 int jx = player->jx, jy = player->jy;
12037 return (IN_LEV_FIELD(jx, jy + 1) &&
12038 (IS_FREE(jx, jy + 1) ||
12039 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12040 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12041 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12044 static boolean canPassField(int x, int y, int move_dir)
12046 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12047 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12048 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12049 int nextx = x + dx;
12050 int nexty = y + dy;
12051 int element = Feld[x][y];
12053 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12054 !CAN_MOVE(element) &&
12055 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12056 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12057 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12060 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12062 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12063 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12064 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12068 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12069 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12070 (IS_DIGGABLE(Feld[newx][newy]) ||
12071 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12072 canPassField(newx, newy, move_dir)));
12075 static void CheckGravityMovement(struct PlayerInfo *player)
12077 #if USE_PLAYER_GRAVITY
12078 if (player->gravity && !player->programmed_action)
12080 if (game.gravity && !player->programmed_action)
12083 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12084 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12085 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12086 int jx = player->jx, jy = player->jy;
12087 boolean player_is_moving_to_valid_field =
12088 (!player_is_snapping &&
12089 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12090 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12091 boolean player_can_fall_down = canFallDown(player);
12093 if (player_can_fall_down &&
12094 !player_is_moving_to_valid_field)
12095 player->programmed_action = MV_DOWN;
12099 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12101 return CheckGravityMovement(player);
12103 #if USE_PLAYER_GRAVITY
12104 if (player->gravity && !player->programmed_action)
12106 if (game.gravity && !player->programmed_action)
12109 int jx = player->jx, jy = player->jy;
12110 boolean field_under_player_is_free =
12111 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12112 boolean player_is_standing_on_valid_field =
12113 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12114 (IS_WALKABLE(Feld[jx][jy]) &&
12115 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12117 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12118 player->programmed_action = MV_DOWN;
12123 MovePlayerOneStep()
12124 -----------------------------------------------------------------------------
12125 dx, dy: direction (non-diagonal) to try to move the player to
12126 real_dx, real_dy: direction as read from input device (can be diagonal)
12129 boolean MovePlayerOneStep(struct PlayerInfo *player,
12130 int dx, int dy, int real_dx, int real_dy)
12132 int jx = player->jx, jy = player->jy;
12133 int new_jx = jx + dx, new_jy = jy + dy;
12134 #if !USE_FIXED_DONT_RUN_INTO
12138 boolean player_can_move = !player->cannot_move;
12140 if (!player->active || (!dx && !dy))
12141 return MP_NO_ACTION;
12143 player->MovDir = (dx < 0 ? MV_LEFT :
12144 dx > 0 ? MV_RIGHT :
12146 dy > 0 ? MV_DOWN : MV_NONE);
12148 if (!IN_LEV_FIELD(new_jx, new_jy))
12149 return MP_NO_ACTION;
12151 if (!player_can_move)
12153 if (player->MovPos == 0)
12155 player->is_moving = FALSE;
12156 player->is_digging = FALSE;
12157 player->is_collecting = FALSE;
12158 player->is_snapping = FALSE;
12159 player->is_pushing = FALSE;
12164 if (!options.network && game.centered_player_nr == -1 &&
12165 !AllPlayersInSight(player, new_jx, new_jy))
12166 return MP_NO_ACTION;
12168 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12169 return MP_NO_ACTION;
12172 #if !USE_FIXED_DONT_RUN_INTO
12173 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12175 /* (moved to DigField()) */
12176 if (player_can_move && DONT_RUN_INTO(element))
12178 if (element == EL_ACID && dx == 0 && dy == 1)
12180 SplashAcid(new_jx, new_jy);
12181 Feld[jx][jy] = EL_PLAYER_1;
12182 InitMovingField(jx, jy, MV_DOWN);
12183 Store[jx][jy] = EL_ACID;
12184 ContinueMoving(jx, jy);
12185 BuryPlayer(player);
12188 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12194 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12195 if (can_move != MP_MOVING)
12198 /* check if DigField() has caused relocation of the player */
12199 if (player->jx != jx || player->jy != jy)
12200 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12202 StorePlayer[jx][jy] = 0;
12203 player->last_jx = jx;
12204 player->last_jy = jy;
12205 player->jx = new_jx;
12206 player->jy = new_jy;
12207 StorePlayer[new_jx][new_jy] = player->element_nr;
12209 if (player->move_delay_value_next != -1)
12211 player->move_delay_value = player->move_delay_value_next;
12212 player->move_delay_value_next = -1;
12216 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12218 player->step_counter++;
12220 PlayerVisit[jx][jy] = FrameCounter;
12222 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12223 player->is_moving = TRUE;
12227 /* should better be called in MovePlayer(), but this breaks some tapes */
12228 ScrollPlayer(player, SCROLL_INIT);
12234 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12236 int jx = player->jx, jy = player->jy;
12237 int old_jx = jx, old_jy = jy;
12238 int moved = MP_NO_ACTION;
12240 if (!player->active)
12245 if (player->MovPos == 0)
12247 player->is_moving = FALSE;
12248 player->is_digging = FALSE;
12249 player->is_collecting = FALSE;
12250 player->is_snapping = FALSE;
12251 player->is_pushing = FALSE;
12257 if (player->move_delay > 0)
12260 player->move_delay = -1; /* set to "uninitialized" value */
12262 /* store if player is automatically moved to next field */
12263 player->is_auto_moving = (player->programmed_action != MV_NONE);
12265 /* remove the last programmed player action */
12266 player->programmed_action = 0;
12268 if (player->MovPos)
12270 /* should only happen if pre-1.2 tape recordings are played */
12271 /* this is only for backward compatibility */
12273 int original_move_delay_value = player->move_delay_value;
12276 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12280 /* scroll remaining steps with finest movement resolution */
12281 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12283 while (player->MovPos)
12285 ScrollPlayer(player, SCROLL_GO_ON);
12286 ScrollScreen(NULL, SCROLL_GO_ON);
12288 AdvanceFrameAndPlayerCounters(player->index_nr);
12294 player->move_delay_value = original_move_delay_value;
12297 player->is_active = FALSE;
12299 if (player->last_move_dir & MV_HORIZONTAL)
12301 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12302 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12306 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12307 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12310 #if USE_FIXED_BORDER_RUNNING_GFX
12311 if (!moved && !player->is_active)
12313 player->is_moving = FALSE;
12314 player->is_digging = FALSE;
12315 player->is_collecting = FALSE;
12316 player->is_snapping = FALSE;
12317 player->is_pushing = FALSE;
12325 if (moved & MP_MOVING && !ScreenMovPos &&
12326 (player->index_nr == game.centered_player_nr ||
12327 game.centered_player_nr == -1))
12329 if (moved & MP_MOVING && !ScreenMovPos &&
12330 (player == local_player || !options.network))
12333 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12334 int offset = game.scroll_delay_value;
12336 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12338 /* actual player has left the screen -- scroll in that direction */
12339 if (jx != old_jx) /* player has moved horizontally */
12340 scroll_x += (jx - old_jx);
12341 else /* player has moved vertically */
12342 scroll_y += (jy - old_jy);
12346 if (jx != old_jx) /* player has moved horizontally */
12348 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12349 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12350 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12352 /* don't scroll over playfield boundaries */
12353 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12354 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12356 /* don't scroll more than one field at a time */
12357 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12359 /* don't scroll against the player's moving direction */
12360 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12361 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12362 scroll_x = old_scroll_x;
12364 else /* player has moved vertically */
12366 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12367 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12368 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12370 /* don't scroll over playfield boundaries */
12371 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12372 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12374 /* don't scroll more than one field at a time */
12375 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12377 /* don't scroll against the player's moving direction */
12378 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12379 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12380 scroll_y = old_scroll_y;
12384 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12387 if (!options.network && game.centered_player_nr == -1 &&
12388 !AllPlayersInVisibleScreen())
12390 scroll_x = old_scroll_x;
12391 scroll_y = old_scroll_y;
12395 if (!options.network && !AllPlayersInVisibleScreen())
12397 scroll_x = old_scroll_x;
12398 scroll_y = old_scroll_y;
12403 ScrollScreen(player, SCROLL_INIT);
12404 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12409 player->StepFrame = 0;
12411 if (moved & MP_MOVING)
12413 if (old_jx != jx && old_jy == jy)
12414 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12415 else if (old_jx == jx && old_jy != jy)
12416 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12418 DrawLevelField(jx, jy); /* for "crumbled sand" */
12420 player->last_move_dir = player->MovDir;
12421 player->is_moving = TRUE;
12422 player->is_snapping = FALSE;
12423 player->is_switching = FALSE;
12424 player->is_dropping = FALSE;
12425 player->is_dropping_pressed = FALSE;
12426 player->drop_pressed_delay = 0;
12429 /* should better be called here than above, but this breaks some tapes */
12430 ScrollPlayer(player, SCROLL_INIT);
12435 CheckGravityMovementWhenNotMoving(player);
12437 player->is_moving = FALSE;
12439 /* at this point, the player is allowed to move, but cannot move right now
12440 (e.g. because of something blocking the way) -- ensure that the player
12441 is also allowed to move in the next frame (in old versions before 3.1.1,
12442 the player was forced to wait again for eight frames before next try) */
12444 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12445 player->move_delay = 0; /* allow direct movement in the next frame */
12448 if (player->move_delay == -1) /* not yet initialized by DigField() */
12449 player->move_delay = player->move_delay_value;
12451 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12453 TestIfPlayerTouchesBadThing(jx, jy);
12454 TestIfPlayerTouchesCustomElement(jx, jy);
12457 if (!player->active)
12458 RemovePlayer(player);
12463 void ScrollPlayer(struct PlayerInfo *player, int mode)
12465 int jx = player->jx, jy = player->jy;
12466 int last_jx = player->last_jx, last_jy = player->last_jy;
12467 int move_stepsize = TILEX / player->move_delay_value;
12469 #if USE_NEW_PLAYER_SPEED
12470 if (!player->active)
12473 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12476 if (!player->active || player->MovPos == 0)
12480 if (mode == SCROLL_INIT)
12482 player->actual_frame_counter = FrameCounter;
12483 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12485 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12486 Feld[last_jx][last_jy] == EL_EMPTY)
12488 int last_field_block_delay = 0; /* start with no blocking at all */
12489 int block_delay_adjustment = player->block_delay_adjustment;
12491 /* if player blocks last field, add delay for exactly one move */
12492 if (player->block_last_field)
12494 last_field_block_delay += player->move_delay_value;
12496 /* when blocking enabled, prevent moving up despite gravity */
12497 #if USE_PLAYER_GRAVITY
12498 if (player->gravity && player->MovDir == MV_UP)
12499 block_delay_adjustment = -1;
12501 if (game.gravity && player->MovDir == MV_UP)
12502 block_delay_adjustment = -1;
12506 /* add block delay adjustment (also possible when not blocking) */
12507 last_field_block_delay += block_delay_adjustment;
12509 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12510 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12513 #if USE_NEW_PLAYER_SPEED
12514 if (player->MovPos != 0) /* player has not yet reached destination */
12520 else if (!FrameReached(&player->actual_frame_counter, 1))
12523 #if USE_NEW_PLAYER_SPEED
12524 if (player->MovPos != 0)
12526 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12527 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12529 /* before DrawPlayer() to draw correct player graphic for this case */
12530 if (player->MovPos == 0)
12531 CheckGravityMovement(player);
12534 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12535 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12537 /* before DrawPlayer() to draw correct player graphic for this case */
12538 if (player->MovPos == 0)
12539 CheckGravityMovement(player);
12542 if (player->MovPos == 0) /* player reached destination field */
12544 if (player->move_delay_reset_counter > 0)
12546 player->move_delay_reset_counter--;
12548 if (player->move_delay_reset_counter == 0)
12550 /* continue with normal speed after quickly moving through gate */
12551 HALVE_PLAYER_SPEED(player);
12553 /* be able to make the next move without delay */
12554 player->move_delay = 0;
12558 player->last_jx = jx;
12559 player->last_jy = jy;
12561 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12562 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12563 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12564 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12565 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12566 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12568 DrawPlayer(player); /* needed here only to cleanup last field */
12569 RemovePlayer(player);
12571 if (local_player->friends_still_needed == 0 ||
12572 IS_SP_ELEMENT(Feld[jx][jy]))
12573 PlayerWins(player);
12576 /* this breaks one level: "machine", level 000 */
12578 int move_direction = player->MovDir;
12579 int enter_side = MV_DIR_OPPOSITE(move_direction);
12580 int leave_side = move_direction;
12581 int old_jx = last_jx;
12582 int old_jy = last_jy;
12583 int old_element = Feld[old_jx][old_jy];
12584 int new_element = Feld[jx][jy];
12586 if (IS_CUSTOM_ELEMENT(old_element))
12587 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12589 player->index_bit, leave_side);
12591 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12592 CE_PLAYER_LEAVES_X,
12593 player->index_bit, leave_side);
12595 if (IS_CUSTOM_ELEMENT(new_element))
12596 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12597 player->index_bit, enter_side);
12599 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12600 CE_PLAYER_ENTERS_X,
12601 player->index_bit, enter_side);
12603 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12604 CE_MOVE_OF_X, move_direction);
12607 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12609 TestIfPlayerTouchesBadThing(jx, jy);
12610 TestIfPlayerTouchesCustomElement(jx, jy);
12612 /* needed because pushed element has not yet reached its destination,
12613 so it would trigger a change event at its previous field location */
12614 if (!player->is_pushing)
12615 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12617 if (!player->active)
12618 RemovePlayer(player);
12621 if (!local_player->LevelSolved && level.use_step_counter)
12631 if (TimeLeft <= 10 && setup.time_limit)
12632 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12635 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12637 DisplayGameControlValues();
12639 DrawGameValue_Time(TimeLeft);
12642 if (!TimeLeft && setup.time_limit)
12643 for (i = 0; i < MAX_PLAYERS; i++)
12644 KillPlayer(&stored_player[i]);
12647 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12649 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12651 DisplayGameControlValues();
12654 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12655 DrawGameValue_Time(TimePlayed);
12659 if (tape.single_step && tape.recording && !tape.pausing &&
12660 !player->programmed_action)
12661 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12665 void ScrollScreen(struct PlayerInfo *player, int mode)
12667 static unsigned long screen_frame_counter = 0;
12669 if (mode == SCROLL_INIT)
12671 /* set scrolling step size according to actual player's moving speed */
12672 ScrollStepSize = TILEX / player->move_delay_value;
12674 screen_frame_counter = FrameCounter;
12675 ScreenMovDir = player->MovDir;
12676 ScreenMovPos = player->MovPos;
12677 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12680 else if (!FrameReached(&screen_frame_counter, 1))
12685 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12686 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12687 redraw_mask |= REDRAW_FIELD;
12690 ScreenMovDir = MV_NONE;
12693 void TestIfPlayerTouchesCustomElement(int x, int y)
12695 static int xy[4][2] =
12702 static int trigger_sides[4][2] =
12704 /* center side border side */
12705 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12706 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12707 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12708 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12710 static int touch_dir[4] =
12712 MV_LEFT | MV_RIGHT,
12717 int center_element = Feld[x][y]; /* should always be non-moving! */
12720 for (i = 0; i < NUM_DIRECTIONS; i++)
12722 int xx = x + xy[i][0];
12723 int yy = y + xy[i][1];
12724 int center_side = trigger_sides[i][0];
12725 int border_side = trigger_sides[i][1];
12726 int border_element;
12728 if (!IN_LEV_FIELD(xx, yy))
12731 if (IS_PLAYER(x, y))
12733 struct PlayerInfo *player = PLAYERINFO(x, y);
12735 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12736 border_element = Feld[xx][yy]; /* may be moving! */
12737 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12738 border_element = Feld[xx][yy];
12739 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12740 border_element = MovingOrBlocked2Element(xx, yy);
12742 continue; /* center and border element do not touch */
12744 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12745 player->index_bit, border_side);
12746 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12747 CE_PLAYER_TOUCHES_X,
12748 player->index_bit, border_side);
12750 else if (IS_PLAYER(xx, yy))
12752 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12754 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12756 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12757 continue; /* center and border element do not touch */
12760 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12761 player->index_bit, center_side);
12762 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12763 CE_PLAYER_TOUCHES_X,
12764 player->index_bit, center_side);
12770 #if USE_ELEMENT_TOUCHING_BUGFIX
12772 void TestIfElementTouchesCustomElement(int x, int y)
12774 static int xy[4][2] =
12781 static int trigger_sides[4][2] =
12783 /* center side border side */
12784 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12785 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12786 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12787 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12789 static int touch_dir[4] =
12791 MV_LEFT | MV_RIGHT,
12796 boolean change_center_element = FALSE;
12797 int center_element = Feld[x][y]; /* should always be non-moving! */
12798 int border_element_old[NUM_DIRECTIONS];
12801 for (i = 0; i < NUM_DIRECTIONS; i++)
12803 int xx = x + xy[i][0];
12804 int yy = y + xy[i][1];
12805 int border_element;
12807 border_element_old[i] = -1;
12809 if (!IN_LEV_FIELD(xx, yy))
12812 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12813 border_element = Feld[xx][yy]; /* may be moving! */
12814 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12815 border_element = Feld[xx][yy];
12816 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12817 border_element = MovingOrBlocked2Element(xx, yy);
12819 continue; /* center and border element do not touch */
12821 border_element_old[i] = border_element;
12824 for (i = 0; i < NUM_DIRECTIONS; i++)
12826 int xx = x + xy[i][0];
12827 int yy = y + xy[i][1];
12828 int center_side = trigger_sides[i][0];
12829 int border_element = border_element_old[i];
12831 if (border_element == -1)
12834 /* check for change of border element */
12835 CheckElementChangeBySide(xx, yy, border_element, center_element,
12836 CE_TOUCHING_X, center_side);
12839 for (i = 0; i < NUM_DIRECTIONS; i++)
12841 int border_side = trigger_sides[i][1];
12842 int border_element = border_element_old[i];
12844 if (border_element == -1)
12847 /* check for change of center element (but change it only once) */
12848 if (!change_center_element)
12849 change_center_element =
12850 CheckElementChangeBySide(x, y, center_element, border_element,
12851 CE_TOUCHING_X, border_side);
12857 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12859 static int xy[4][2] =
12866 static int trigger_sides[4][2] =
12868 /* center side border side */
12869 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12870 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12871 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12872 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12874 static int touch_dir[4] =
12876 MV_LEFT | MV_RIGHT,
12881 boolean change_center_element = FALSE;
12882 int center_element = Feld[x][y]; /* should always be non-moving! */
12885 for (i = 0; i < NUM_DIRECTIONS; i++)
12887 int xx = x + xy[i][0];
12888 int yy = y + xy[i][1];
12889 int center_side = trigger_sides[i][0];
12890 int border_side = trigger_sides[i][1];
12891 int border_element;
12893 if (!IN_LEV_FIELD(xx, yy))
12896 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12897 border_element = Feld[xx][yy]; /* may be moving! */
12898 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12899 border_element = Feld[xx][yy];
12900 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12901 border_element = MovingOrBlocked2Element(xx, yy);
12903 continue; /* center and border element do not touch */
12905 /* check for change of center element (but change it only once) */
12906 if (!change_center_element)
12907 change_center_element =
12908 CheckElementChangeBySide(x, y, center_element, border_element,
12909 CE_TOUCHING_X, border_side);
12911 /* check for change of border element */
12912 CheckElementChangeBySide(xx, yy, border_element, center_element,
12913 CE_TOUCHING_X, center_side);
12919 void TestIfElementHitsCustomElement(int x, int y, int direction)
12921 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12922 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12923 int hitx = x + dx, hity = y + dy;
12924 int hitting_element = Feld[x][y];
12925 int touched_element;
12927 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12930 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12931 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12933 if (IN_LEV_FIELD(hitx, hity))
12935 int opposite_direction = MV_DIR_OPPOSITE(direction);
12936 int hitting_side = direction;
12937 int touched_side = opposite_direction;
12938 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12939 MovDir[hitx][hity] != direction ||
12940 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12946 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12947 CE_HITTING_X, touched_side);
12949 CheckElementChangeBySide(hitx, hity, touched_element,
12950 hitting_element, CE_HIT_BY_X, hitting_side);
12952 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12953 CE_HIT_BY_SOMETHING, opposite_direction);
12957 /* "hitting something" is also true when hitting the playfield border */
12958 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12959 CE_HITTING_SOMETHING, direction);
12963 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12965 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12966 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12967 int hitx = x + dx, hity = y + dy;
12968 int hitting_element = Feld[x][y];
12969 int touched_element;
12971 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12972 !IS_FREE(hitx, hity) &&
12973 (!IS_MOVING(hitx, hity) ||
12974 MovDir[hitx][hity] != direction ||
12975 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12978 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12982 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12986 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12987 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12989 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12990 EP_CAN_SMASH_EVERYTHING, direction);
12992 if (IN_LEV_FIELD(hitx, hity))
12994 int opposite_direction = MV_DIR_OPPOSITE(direction);
12995 int hitting_side = direction;
12996 int touched_side = opposite_direction;
12998 int touched_element = MovingOrBlocked2Element(hitx, hity);
13001 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13002 MovDir[hitx][hity] != direction ||
13003 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13012 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13013 CE_SMASHED_BY_SOMETHING, opposite_direction);
13015 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13016 CE_OTHER_IS_SMASHING, touched_side);
13018 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13019 CE_OTHER_GETS_SMASHED, hitting_side);
13025 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13027 int i, kill_x = -1, kill_y = -1;
13029 int bad_element = -1;
13030 static int test_xy[4][2] =
13037 static int test_dir[4] =
13045 for (i = 0; i < NUM_DIRECTIONS; i++)
13047 int test_x, test_y, test_move_dir, test_element;
13049 test_x = good_x + test_xy[i][0];
13050 test_y = good_y + test_xy[i][1];
13052 if (!IN_LEV_FIELD(test_x, test_y))
13056 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13058 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13060 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13061 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13063 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13064 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13068 bad_element = test_element;
13074 if (kill_x != -1 || kill_y != -1)
13076 if (IS_PLAYER(good_x, good_y))
13078 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13080 if (player->shield_deadly_time_left > 0 &&
13081 !IS_INDESTRUCTIBLE(bad_element))
13082 Bang(kill_x, kill_y);
13083 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13084 KillPlayer(player);
13087 Bang(good_x, good_y);
13091 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13093 int i, kill_x = -1, kill_y = -1;
13094 int bad_element = Feld[bad_x][bad_y];
13095 static int test_xy[4][2] =
13102 static int touch_dir[4] =
13104 MV_LEFT | MV_RIGHT,
13109 static int test_dir[4] =
13117 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13120 for (i = 0; i < NUM_DIRECTIONS; i++)
13122 int test_x, test_y, test_move_dir, test_element;
13124 test_x = bad_x + test_xy[i][0];
13125 test_y = bad_y + test_xy[i][1];
13126 if (!IN_LEV_FIELD(test_x, test_y))
13130 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13132 test_element = Feld[test_x][test_y];
13134 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13135 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13137 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13138 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13140 /* good thing is player or penguin that does not move away */
13141 if (IS_PLAYER(test_x, test_y))
13143 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13145 if (bad_element == EL_ROBOT && player->is_moving)
13146 continue; /* robot does not kill player if he is moving */
13148 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13150 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13151 continue; /* center and border element do not touch */
13158 else if (test_element == EL_PENGUIN)
13167 if (kill_x != -1 || kill_y != -1)
13169 if (IS_PLAYER(kill_x, kill_y))
13171 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13173 if (player->shield_deadly_time_left > 0 &&
13174 !IS_INDESTRUCTIBLE(bad_element))
13175 Bang(bad_x, bad_y);
13176 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13177 KillPlayer(player);
13180 Bang(kill_x, kill_y);
13184 void TestIfPlayerTouchesBadThing(int x, int y)
13186 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13189 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13191 TestIfGoodThingHitsBadThing(x, y, move_dir);
13194 void TestIfBadThingTouchesPlayer(int x, int y)
13196 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13199 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13201 TestIfBadThingHitsGoodThing(x, y, move_dir);
13204 void TestIfFriendTouchesBadThing(int x, int y)
13206 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13209 void TestIfBadThingTouchesFriend(int x, int y)
13211 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13214 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13216 int i, kill_x = bad_x, kill_y = bad_y;
13217 static int xy[4][2] =
13225 for (i = 0; i < NUM_DIRECTIONS; i++)
13229 x = bad_x + xy[i][0];
13230 y = bad_y + xy[i][1];
13231 if (!IN_LEV_FIELD(x, y))
13234 element = Feld[x][y];
13235 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13236 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13244 if (kill_x != bad_x || kill_y != bad_y)
13245 Bang(bad_x, bad_y);
13248 void KillPlayer(struct PlayerInfo *player)
13250 int jx = player->jx, jy = player->jy;
13252 if (!player->active)
13255 /* the following code was introduced to prevent an infinite loop when calling
13257 -> CheckTriggeredElementChangeExt()
13258 -> ExecuteCustomElementAction()
13260 -> (infinitely repeating the above sequence of function calls)
13261 which occurs when killing the player while having a CE with the setting
13262 "kill player X when explosion of <player X>"; the solution using a new
13263 field "player->killed" was chosen for backwards compatibility, although
13264 clever use of the fields "player->active" etc. would probably also work */
13266 if (player->killed)
13270 player->killed = TRUE;
13272 /* remove accessible field at the player's position */
13273 Feld[jx][jy] = EL_EMPTY;
13275 /* deactivate shield (else Bang()/Explode() would not work right) */
13276 player->shield_normal_time_left = 0;
13277 player->shield_deadly_time_left = 0;
13280 BuryPlayer(player);
13283 static void KillPlayerUnlessEnemyProtected(int x, int y)
13285 if (!PLAYER_ENEMY_PROTECTED(x, y))
13286 KillPlayer(PLAYERINFO(x, y));
13289 static void KillPlayerUnlessExplosionProtected(int x, int y)
13291 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13292 KillPlayer(PLAYERINFO(x, y));
13295 void BuryPlayer(struct PlayerInfo *player)
13297 int jx = player->jx, jy = player->jy;
13299 if (!player->active)
13302 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13303 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13305 player->GameOver = TRUE;
13306 RemovePlayer(player);
13309 void RemovePlayer(struct PlayerInfo *player)
13311 int jx = player->jx, jy = player->jy;
13312 int i, found = FALSE;
13314 player->present = FALSE;
13315 player->active = FALSE;
13317 if (!ExplodeField[jx][jy])
13318 StorePlayer[jx][jy] = 0;
13320 if (player->is_moving)
13321 DrawLevelField(player->last_jx, player->last_jy);
13323 for (i = 0; i < MAX_PLAYERS; i++)
13324 if (stored_player[i].active)
13328 AllPlayersGone = TRUE;
13334 #if USE_NEW_SNAP_DELAY
13335 static void setFieldForSnapping(int x, int y, int element, int direction)
13337 struct ElementInfo *ei = &element_info[element];
13338 int direction_bit = MV_DIR_TO_BIT(direction);
13339 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13340 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13341 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13343 Feld[x][y] = EL_ELEMENT_SNAPPING;
13344 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13346 ResetGfxAnimation(x, y);
13348 GfxElement[x][y] = element;
13349 GfxAction[x][y] = action;
13350 GfxDir[x][y] = direction;
13351 GfxFrame[x][y] = -1;
13356 =============================================================================
13357 checkDiagonalPushing()
13358 -----------------------------------------------------------------------------
13359 check if diagonal input device direction results in pushing of object
13360 (by checking if the alternative direction is walkable, diggable, ...)
13361 =============================================================================
13364 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13365 int x, int y, int real_dx, int real_dy)
13367 int jx, jy, dx, dy, xx, yy;
13369 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13372 /* diagonal direction: check alternative direction */
13377 xx = jx + (dx == 0 ? real_dx : 0);
13378 yy = jy + (dy == 0 ? real_dy : 0);
13380 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13384 =============================================================================
13386 -----------------------------------------------------------------------------
13387 x, y: field next to player (non-diagonal) to try to dig to
13388 real_dx, real_dy: direction as read from input device (can be diagonal)
13389 =============================================================================
13392 int DigField(struct PlayerInfo *player,
13393 int oldx, int oldy, int x, int y,
13394 int real_dx, int real_dy, int mode)
13396 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13397 boolean player_was_pushing = player->is_pushing;
13398 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13399 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13400 int jx = oldx, jy = oldy;
13401 int dx = x - jx, dy = y - jy;
13402 int nextx = x + dx, nexty = y + dy;
13403 int move_direction = (dx == -1 ? MV_LEFT :
13404 dx == +1 ? MV_RIGHT :
13406 dy == +1 ? MV_DOWN : MV_NONE);
13407 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13408 int dig_side = MV_DIR_OPPOSITE(move_direction);
13409 int old_element = Feld[jx][jy];
13410 #if USE_FIXED_DONT_RUN_INTO
13411 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13417 if (is_player) /* function can also be called by EL_PENGUIN */
13419 if (player->MovPos == 0)
13421 player->is_digging = FALSE;
13422 player->is_collecting = FALSE;
13425 if (player->MovPos == 0) /* last pushing move finished */
13426 player->is_pushing = FALSE;
13428 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13430 player->is_switching = FALSE;
13431 player->push_delay = -1;
13433 return MP_NO_ACTION;
13437 #if !USE_FIXED_DONT_RUN_INTO
13438 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13439 return MP_NO_ACTION;
13442 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13443 old_element = Back[jx][jy];
13445 /* in case of element dropped at player position, check background */
13446 else if (Back[jx][jy] != EL_EMPTY &&
13447 game.engine_version >= VERSION_IDENT(2,2,0,0))
13448 old_element = Back[jx][jy];
13450 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13451 return MP_NO_ACTION; /* field has no opening in this direction */
13453 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13454 return MP_NO_ACTION; /* field has no opening in this direction */
13456 #if USE_FIXED_DONT_RUN_INTO
13457 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13461 Feld[jx][jy] = player->artwork_element;
13462 InitMovingField(jx, jy, MV_DOWN);
13463 Store[jx][jy] = EL_ACID;
13464 ContinueMoving(jx, jy);
13465 BuryPlayer(player);
13467 return MP_DONT_RUN_INTO;
13471 #if USE_FIXED_DONT_RUN_INTO
13472 if (player_can_move && DONT_RUN_INTO(element))
13474 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13476 return MP_DONT_RUN_INTO;
13480 #if USE_FIXED_DONT_RUN_INTO
13481 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13482 return MP_NO_ACTION;
13485 #if !USE_FIXED_DONT_RUN_INTO
13486 element = Feld[x][y];
13489 collect_count = element_info[element].collect_count_initial;
13491 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13492 return MP_NO_ACTION;
13494 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13495 player_can_move = player_can_move_or_snap;
13497 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13498 game.engine_version >= VERSION_IDENT(2,2,0,0))
13500 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13501 player->index_bit, dig_side);
13502 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13503 player->index_bit, dig_side);
13505 if (element == EL_DC_LANDMINE)
13508 if (Feld[x][y] != element) /* field changed by snapping */
13511 return MP_NO_ACTION;
13514 #if USE_PLAYER_GRAVITY
13515 if (player->gravity && is_player && !player->is_auto_moving &&
13516 canFallDown(player) && move_direction != MV_DOWN &&
13517 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13518 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13520 if (game.gravity && is_player && !player->is_auto_moving &&
13521 canFallDown(player) && move_direction != MV_DOWN &&
13522 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13523 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13526 if (player_can_move &&
13527 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13529 int sound_element = SND_ELEMENT(element);
13530 int sound_action = ACTION_WALKING;
13532 if (IS_RND_GATE(element))
13534 if (!player->key[RND_GATE_NR(element)])
13535 return MP_NO_ACTION;
13537 else if (IS_RND_GATE_GRAY(element))
13539 if (!player->key[RND_GATE_GRAY_NR(element)])
13540 return MP_NO_ACTION;
13542 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13544 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13545 return MP_NO_ACTION;
13547 else if (element == EL_EXIT_OPEN ||
13548 element == EL_EM_EXIT_OPEN ||
13549 element == EL_STEEL_EXIT_OPEN ||
13550 element == EL_EM_STEEL_EXIT_OPEN ||
13551 element == EL_SP_EXIT_OPEN ||
13552 element == EL_SP_EXIT_OPENING)
13554 sound_action = ACTION_PASSING; /* player is passing exit */
13556 else if (element == EL_EMPTY)
13558 sound_action = ACTION_MOVING; /* nothing to walk on */
13561 /* play sound from background or player, whatever is available */
13562 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13563 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13565 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13567 else if (player_can_move &&
13568 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13570 if (!ACCESS_FROM(element, opposite_direction))
13571 return MP_NO_ACTION; /* field not accessible from this direction */
13573 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13574 return MP_NO_ACTION;
13576 if (IS_EM_GATE(element))
13578 if (!player->key[EM_GATE_NR(element)])
13579 return MP_NO_ACTION;
13581 else if (IS_EM_GATE_GRAY(element))
13583 if (!player->key[EM_GATE_GRAY_NR(element)])
13584 return MP_NO_ACTION;
13586 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13588 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13589 return MP_NO_ACTION;
13591 else if (IS_EMC_GATE(element))
13593 if (!player->key[EMC_GATE_NR(element)])
13594 return MP_NO_ACTION;
13596 else if (IS_EMC_GATE_GRAY(element))
13598 if (!player->key[EMC_GATE_GRAY_NR(element)])
13599 return MP_NO_ACTION;
13601 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13603 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13604 return MP_NO_ACTION;
13606 else if (element == EL_DC_GATE_WHITE ||
13607 element == EL_DC_GATE_WHITE_GRAY ||
13608 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13610 if (player->num_white_keys == 0)
13611 return MP_NO_ACTION;
13613 player->num_white_keys--;
13615 else if (IS_SP_PORT(element))
13617 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13618 element == EL_SP_GRAVITY_PORT_RIGHT ||
13619 element == EL_SP_GRAVITY_PORT_UP ||
13620 element == EL_SP_GRAVITY_PORT_DOWN)
13621 #if USE_PLAYER_GRAVITY
13622 player->gravity = !player->gravity;
13624 game.gravity = !game.gravity;
13626 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13627 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13628 element == EL_SP_GRAVITY_ON_PORT_UP ||
13629 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13630 #if USE_PLAYER_GRAVITY
13631 player->gravity = TRUE;
13633 game.gravity = TRUE;
13635 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13636 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13637 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13638 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13639 #if USE_PLAYER_GRAVITY
13640 player->gravity = FALSE;
13642 game.gravity = FALSE;
13646 /* automatically move to the next field with double speed */
13647 player->programmed_action = move_direction;
13649 if (player->move_delay_reset_counter == 0)
13651 player->move_delay_reset_counter = 2; /* two double speed steps */
13653 DOUBLE_PLAYER_SPEED(player);
13656 PlayLevelSoundAction(x, y, ACTION_PASSING);
13658 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13662 if (mode != DF_SNAP)
13664 GfxElement[x][y] = GFX_ELEMENT(element);
13665 player->is_digging = TRUE;
13668 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13670 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13671 player->index_bit, dig_side);
13673 if (mode == DF_SNAP)
13675 #if USE_NEW_SNAP_DELAY
13676 if (level.block_snap_field)
13677 setFieldForSnapping(x, y, element, move_direction);
13679 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13681 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13684 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13685 player->index_bit, dig_side);
13688 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13692 if (is_player && mode != DF_SNAP)
13694 GfxElement[x][y] = element;
13695 player->is_collecting = TRUE;
13698 if (element == EL_SPEED_PILL)
13700 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13702 else if (element == EL_EXTRA_TIME && level.time > 0)
13704 TimeLeft += level.extra_time;
13707 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13709 DisplayGameControlValues();
13711 DrawGameValue_Time(TimeLeft);
13714 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13716 player->shield_normal_time_left += level.shield_normal_time;
13717 if (element == EL_SHIELD_DEADLY)
13718 player->shield_deadly_time_left += level.shield_deadly_time;
13720 else if (element == EL_DYNAMITE ||
13721 element == EL_EM_DYNAMITE ||
13722 element == EL_SP_DISK_RED)
13724 if (player->inventory_size < MAX_INVENTORY_SIZE)
13725 player->inventory_element[player->inventory_size++] = element;
13727 DrawGameDoorValues();
13729 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13731 player->dynabomb_count++;
13732 player->dynabombs_left++;
13734 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13736 player->dynabomb_size++;
13738 else if (element == EL_DYNABOMB_INCREASE_POWER)
13740 player->dynabomb_xl = TRUE;
13742 else if (IS_KEY(element))
13744 player->key[KEY_NR(element)] = TRUE;
13746 DrawGameDoorValues();
13748 else if (element == EL_DC_KEY_WHITE)
13750 player->num_white_keys++;
13752 /* display white keys? */
13753 /* DrawGameDoorValues(); */
13755 else if (IS_ENVELOPE(element))
13757 player->show_envelope = element;
13759 else if (element == EL_EMC_LENSES)
13761 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13763 RedrawAllInvisibleElementsForLenses();
13765 else if (element == EL_EMC_MAGNIFIER)
13767 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13769 RedrawAllInvisibleElementsForMagnifier();
13771 else if (IS_DROPPABLE(element) ||
13772 IS_THROWABLE(element)) /* can be collected and dropped */
13776 if (collect_count == 0)
13777 player->inventory_infinite_element = element;
13779 for (i = 0; i < collect_count; i++)
13780 if (player->inventory_size < MAX_INVENTORY_SIZE)
13781 player->inventory_element[player->inventory_size++] = element;
13783 DrawGameDoorValues();
13785 else if (collect_count > 0)
13787 local_player->gems_still_needed -= collect_count;
13788 if (local_player->gems_still_needed < 0)
13789 local_player->gems_still_needed = 0;
13792 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13794 DisplayGameControlValues();
13796 DrawGameValue_Emeralds(local_player->gems_still_needed);
13800 RaiseScoreElement(element);
13801 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13804 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13805 player->index_bit, dig_side);
13807 if (mode == DF_SNAP)
13809 #if USE_NEW_SNAP_DELAY
13810 if (level.block_snap_field)
13811 setFieldForSnapping(x, y, element, move_direction);
13813 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13815 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13818 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13819 player->index_bit, dig_side);
13822 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13824 if (mode == DF_SNAP && element != EL_BD_ROCK)
13825 return MP_NO_ACTION;
13827 if (CAN_FALL(element) && dy)
13828 return MP_NO_ACTION;
13830 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13831 !(element == EL_SPRING && level.use_spring_bug))
13832 return MP_NO_ACTION;
13834 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13835 ((move_direction & MV_VERTICAL &&
13836 ((element_info[element].move_pattern & MV_LEFT &&
13837 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13838 (element_info[element].move_pattern & MV_RIGHT &&
13839 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13840 (move_direction & MV_HORIZONTAL &&
13841 ((element_info[element].move_pattern & MV_UP &&
13842 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13843 (element_info[element].move_pattern & MV_DOWN &&
13844 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13845 return MP_NO_ACTION;
13847 /* do not push elements already moving away faster than player */
13848 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13849 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13850 return MP_NO_ACTION;
13852 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13854 if (player->push_delay_value == -1 || !player_was_pushing)
13855 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13857 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13859 if (player->push_delay_value == -1)
13860 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13862 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13864 if (!player->is_pushing)
13865 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13868 player->is_pushing = TRUE;
13869 player->is_active = TRUE;
13871 if (!(IN_LEV_FIELD(nextx, nexty) &&
13872 (IS_FREE(nextx, nexty) ||
13873 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13874 IS_SB_ELEMENT(element)))))
13875 return MP_NO_ACTION;
13877 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13878 return MP_NO_ACTION;
13880 if (player->push_delay == -1) /* new pushing; restart delay */
13881 player->push_delay = 0;
13883 if (player->push_delay < player->push_delay_value &&
13884 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13885 element != EL_SPRING && element != EL_BALLOON)
13887 /* make sure that there is no move delay before next try to push */
13888 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13889 player->move_delay = 0;
13891 return MP_NO_ACTION;
13894 if (IS_SB_ELEMENT(element))
13896 if (element == EL_SOKOBAN_FIELD_FULL)
13898 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13899 local_player->sokobanfields_still_needed++;
13902 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13904 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13905 local_player->sokobanfields_still_needed--;
13908 Feld[x][y] = EL_SOKOBAN_OBJECT;
13910 if (Back[x][y] == Back[nextx][nexty])
13911 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13912 else if (Back[x][y] != 0)
13913 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13916 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13919 if (local_player->sokobanfields_still_needed == 0 &&
13920 game.emulation == EMU_SOKOBAN)
13922 PlayerWins(player);
13924 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13928 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13930 InitMovingField(x, y, move_direction);
13931 GfxAction[x][y] = ACTION_PUSHING;
13933 if (mode == DF_SNAP)
13934 ContinueMoving(x, y);
13936 MovPos[x][y] = (dx != 0 ? dx : dy);
13938 Pushed[x][y] = TRUE;
13939 Pushed[nextx][nexty] = TRUE;
13941 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13942 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13944 player->push_delay_value = -1; /* get new value later */
13946 /* check for element change _after_ element has been pushed */
13947 if (game.use_change_when_pushing_bug)
13949 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13950 player->index_bit, dig_side);
13951 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13952 player->index_bit, dig_side);
13955 else if (IS_SWITCHABLE(element))
13957 if (PLAYER_SWITCHING(player, x, y))
13959 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13960 player->index_bit, dig_side);
13965 player->is_switching = TRUE;
13966 player->switch_x = x;
13967 player->switch_y = y;
13969 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13971 if (element == EL_ROBOT_WHEEL)
13973 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13977 game.robot_wheel_active = TRUE;
13979 DrawLevelField(x, y);
13981 else if (element == EL_SP_TERMINAL)
13985 SCAN_PLAYFIELD(xx, yy)
13987 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13989 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13990 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13993 else if (IS_BELT_SWITCH(element))
13995 ToggleBeltSwitch(x, y);
13997 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13998 element == EL_SWITCHGATE_SWITCH_DOWN ||
13999 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14000 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14002 ToggleSwitchgateSwitch(x, y);
14004 else if (element == EL_LIGHT_SWITCH ||
14005 element == EL_LIGHT_SWITCH_ACTIVE)
14007 ToggleLightSwitch(x, y);
14009 else if (element == EL_TIMEGATE_SWITCH ||
14010 element == EL_DC_TIMEGATE_SWITCH)
14012 ActivateTimegateSwitch(x, y);
14014 else if (element == EL_BALLOON_SWITCH_LEFT ||
14015 element == EL_BALLOON_SWITCH_RIGHT ||
14016 element == EL_BALLOON_SWITCH_UP ||
14017 element == EL_BALLOON_SWITCH_DOWN ||
14018 element == EL_BALLOON_SWITCH_NONE ||
14019 element == EL_BALLOON_SWITCH_ANY)
14021 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14022 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14023 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14024 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14025 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14028 else if (element == EL_LAMP)
14030 Feld[x][y] = EL_LAMP_ACTIVE;
14031 local_player->lights_still_needed--;
14033 ResetGfxAnimation(x, y);
14034 DrawLevelField(x, y);
14036 else if (element == EL_TIME_ORB_FULL)
14038 Feld[x][y] = EL_TIME_ORB_EMPTY;
14040 if (level.time > 0 || level.use_time_orb_bug)
14042 TimeLeft += level.time_orb_time;
14045 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14047 DisplayGameControlValues();
14049 DrawGameValue_Time(TimeLeft);
14053 ResetGfxAnimation(x, y);
14054 DrawLevelField(x, y);
14056 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14057 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14061 game.ball_state = !game.ball_state;
14063 SCAN_PLAYFIELD(xx, yy)
14065 int e = Feld[xx][yy];
14067 if (game.ball_state)
14069 if (e == EL_EMC_MAGIC_BALL)
14070 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14071 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14072 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14076 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14077 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14078 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14079 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14084 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14085 player->index_bit, dig_side);
14087 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14088 player->index_bit, dig_side);
14090 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14091 player->index_bit, dig_side);
14097 if (!PLAYER_SWITCHING(player, x, y))
14099 player->is_switching = TRUE;
14100 player->switch_x = x;
14101 player->switch_y = y;
14103 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14104 player->index_bit, dig_side);
14105 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14106 player->index_bit, dig_side);
14108 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14109 player->index_bit, dig_side);
14110 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14111 player->index_bit, dig_side);
14114 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14115 player->index_bit, dig_side);
14116 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14117 player->index_bit, dig_side);
14119 return MP_NO_ACTION;
14122 player->push_delay = -1;
14124 if (is_player) /* function can also be called by EL_PENGUIN */
14126 if (Feld[x][y] != element) /* really digged/collected something */
14128 player->is_collecting = !player->is_digging;
14129 player->is_active = TRUE;
14136 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14138 int jx = player->jx, jy = player->jy;
14139 int x = jx + dx, y = jy + dy;
14140 int snap_direction = (dx == -1 ? MV_LEFT :
14141 dx == +1 ? MV_RIGHT :
14143 dy == +1 ? MV_DOWN : MV_NONE);
14144 boolean can_continue_snapping = (level.continuous_snapping &&
14145 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14147 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14150 if (!player->active || !IN_LEV_FIELD(x, y))
14158 if (player->MovPos == 0)
14159 player->is_pushing = FALSE;
14161 player->is_snapping = FALSE;
14163 if (player->MovPos == 0)
14165 player->is_moving = FALSE;
14166 player->is_digging = FALSE;
14167 player->is_collecting = FALSE;
14173 #if USE_NEW_CONTINUOUS_SNAPPING
14174 /* prevent snapping with already pressed snap key when not allowed */
14175 if (player->is_snapping && !can_continue_snapping)
14178 if (player->is_snapping)
14182 player->MovDir = snap_direction;
14184 if (player->MovPos == 0)
14186 player->is_moving = FALSE;
14187 player->is_digging = FALSE;
14188 player->is_collecting = FALSE;
14191 player->is_dropping = FALSE;
14192 player->is_dropping_pressed = FALSE;
14193 player->drop_pressed_delay = 0;
14195 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14198 player->is_snapping = TRUE;
14199 player->is_active = TRUE;
14201 if (player->MovPos == 0)
14203 player->is_moving = FALSE;
14204 player->is_digging = FALSE;
14205 player->is_collecting = FALSE;
14208 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14209 DrawLevelField(player->last_jx, player->last_jy);
14211 DrawLevelField(x, y);
14216 boolean DropElement(struct PlayerInfo *player)
14218 int old_element, new_element;
14219 int dropx = player->jx, dropy = player->jy;
14220 int drop_direction = player->MovDir;
14221 int drop_side = drop_direction;
14223 int drop_element = get_next_dropped_element(player);
14225 int drop_element = (player->inventory_size > 0 ?
14226 player->inventory_element[player->inventory_size - 1] :
14227 player->inventory_infinite_element != EL_UNDEFINED ?
14228 player->inventory_infinite_element :
14229 player->dynabombs_left > 0 ?
14230 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14234 player->is_dropping_pressed = TRUE;
14236 /* do not drop an element on top of another element; when holding drop key
14237 pressed without moving, dropped element must move away before the next
14238 element can be dropped (this is especially important if the next element
14239 is dynamite, which can be placed on background for historical reasons) */
14240 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14243 if (IS_THROWABLE(drop_element))
14245 dropx += GET_DX_FROM_DIR(drop_direction);
14246 dropy += GET_DY_FROM_DIR(drop_direction);
14248 if (!IN_LEV_FIELD(dropx, dropy))
14252 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14253 new_element = drop_element; /* default: no change when dropping */
14255 /* check if player is active, not moving and ready to drop */
14256 if (!player->active || player->MovPos || player->drop_delay > 0)
14259 /* check if player has anything that can be dropped */
14260 if (new_element == EL_UNDEFINED)
14263 /* check if drop key was pressed long enough for EM style dynamite */
14264 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14267 /* check if anything can be dropped at the current position */
14268 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14271 /* collected custom elements can only be dropped on empty fields */
14272 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14275 if (old_element != EL_EMPTY)
14276 Back[dropx][dropy] = old_element; /* store old element on this field */
14278 ResetGfxAnimation(dropx, dropy);
14279 ResetRandomAnimationValue(dropx, dropy);
14281 if (player->inventory_size > 0 ||
14282 player->inventory_infinite_element != EL_UNDEFINED)
14284 if (player->inventory_size > 0)
14286 player->inventory_size--;
14288 DrawGameDoorValues();
14290 if (new_element == EL_DYNAMITE)
14291 new_element = EL_DYNAMITE_ACTIVE;
14292 else if (new_element == EL_EM_DYNAMITE)
14293 new_element = EL_EM_DYNAMITE_ACTIVE;
14294 else if (new_element == EL_SP_DISK_RED)
14295 new_element = EL_SP_DISK_RED_ACTIVE;
14298 Feld[dropx][dropy] = new_element;
14300 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14301 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14302 el2img(Feld[dropx][dropy]), 0);
14304 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14306 /* needed if previous element just changed to "empty" in the last frame */
14307 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14309 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14310 player->index_bit, drop_side);
14311 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14313 player->index_bit, drop_side);
14315 TestIfElementTouchesCustomElement(dropx, dropy);
14317 else /* player is dropping a dyna bomb */
14319 player->dynabombs_left--;
14321 Feld[dropx][dropy] = new_element;
14323 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14324 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14325 el2img(Feld[dropx][dropy]), 0);
14327 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14330 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14331 InitField_WithBug1(dropx, dropy, FALSE);
14333 new_element = Feld[dropx][dropy]; /* element might have changed */
14335 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14336 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14338 int move_direction, nextx, nexty;
14340 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14341 MovDir[dropx][dropy] = drop_direction;
14343 move_direction = MovDir[dropx][dropy];
14344 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14345 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14347 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14349 #if USE_FIX_IMPACT_COLLISION
14350 /* do not cause impact style collision by dropping elements that can fall */
14351 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14353 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14357 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14358 player->is_dropping = TRUE;
14360 player->drop_pressed_delay = 0;
14361 player->is_dropping_pressed = FALSE;
14363 player->drop_x = dropx;
14364 player->drop_y = dropy;
14369 /* ------------------------------------------------------------------------- */
14370 /* game sound playing functions */
14371 /* ------------------------------------------------------------------------- */
14373 static int *loop_sound_frame = NULL;
14374 static int *loop_sound_volume = NULL;
14376 void InitPlayLevelSound()
14378 int num_sounds = getSoundListSize();
14380 checked_free(loop_sound_frame);
14381 checked_free(loop_sound_volume);
14383 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14384 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14387 static void PlayLevelSound(int x, int y, int nr)
14389 int sx = SCREENX(x), sy = SCREENY(y);
14390 int volume, stereo_position;
14391 int max_distance = 8;
14392 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14394 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14395 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14398 if (!IN_LEV_FIELD(x, y) ||
14399 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14400 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14403 volume = SOUND_MAX_VOLUME;
14405 if (!IN_SCR_FIELD(sx, sy))
14407 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14408 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14410 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14413 stereo_position = (SOUND_MAX_LEFT +
14414 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14415 (SCR_FIELDX + 2 * max_distance));
14417 if (IS_LOOP_SOUND(nr))
14419 /* This assures that quieter loop sounds do not overwrite louder ones,
14420 while restarting sound volume comparison with each new game frame. */
14422 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14425 loop_sound_volume[nr] = volume;
14426 loop_sound_frame[nr] = FrameCounter;
14429 PlaySoundExt(nr, volume, stereo_position, type);
14432 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14434 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14435 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14436 y < LEVELY(BY1) ? LEVELY(BY1) :
14437 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14441 static void PlayLevelSoundAction(int x, int y, int action)
14443 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14446 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14448 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14450 if (sound_effect != SND_UNDEFINED)
14451 PlayLevelSound(x, y, sound_effect);
14454 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14457 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14459 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14460 PlayLevelSound(x, y, sound_effect);
14463 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14465 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14467 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14468 PlayLevelSound(x, y, sound_effect);
14471 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14473 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14475 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14476 StopSound(sound_effect);
14479 static void PlayLevelMusic()
14481 if (levelset.music[level_nr] != MUS_UNDEFINED)
14482 PlayMusic(levelset.music[level_nr]); /* from config file */
14484 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14487 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14489 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14490 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14491 int x = xx - 1 - offset;
14492 int y = yy - 1 - offset;
14497 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14501 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14505 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14509 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14513 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14517 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14521 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14524 case SAMPLE_android_clone:
14525 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14528 case SAMPLE_android_move:
14529 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14532 case SAMPLE_spring:
14533 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14537 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14541 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14544 case SAMPLE_eater_eat:
14545 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14549 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14552 case SAMPLE_collect:
14553 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14556 case SAMPLE_diamond:
14557 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14560 case SAMPLE_squash:
14561 /* !!! CHECK THIS !!! */
14563 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14565 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14569 case SAMPLE_wonderfall:
14570 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14574 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14578 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14582 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14586 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14590 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14594 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14597 case SAMPLE_wonder:
14598 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14602 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14605 case SAMPLE_exit_open:
14606 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14609 case SAMPLE_exit_leave:
14610 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14613 case SAMPLE_dynamite:
14614 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14618 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14622 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14626 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14630 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14634 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14638 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14642 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14648 void ChangeTime(int value)
14650 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14654 /* EMC game engine uses value from time counter of RND game engine */
14655 level.native_em_level->lev->time = *time;
14657 DrawGameValue_Time(*time);
14660 void RaiseScore(int value)
14662 /* EMC game engine and RND game engine have separate score counters */
14663 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14664 &level.native_em_level->lev->score : &local_player->score);
14668 DrawGameValue_Score(*score);
14672 void RaiseScore(int value)
14674 local_player->score += value;
14677 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14679 DisplayGameControlValues();
14681 DrawGameValue_Score(local_player->score);
14685 void RaiseScoreElement(int element)
14690 case EL_BD_DIAMOND:
14691 case EL_EMERALD_YELLOW:
14692 case EL_EMERALD_RED:
14693 case EL_EMERALD_PURPLE:
14694 case EL_SP_INFOTRON:
14695 RaiseScore(level.score[SC_EMERALD]);
14698 RaiseScore(level.score[SC_DIAMOND]);
14701 RaiseScore(level.score[SC_CRYSTAL]);
14704 RaiseScore(level.score[SC_PEARL]);
14707 case EL_BD_BUTTERFLY:
14708 case EL_SP_ELECTRON:
14709 RaiseScore(level.score[SC_BUG]);
14712 case EL_BD_FIREFLY:
14713 case EL_SP_SNIKSNAK:
14714 RaiseScore(level.score[SC_SPACESHIP]);
14717 case EL_DARK_YAMYAM:
14718 RaiseScore(level.score[SC_YAMYAM]);
14721 RaiseScore(level.score[SC_ROBOT]);
14724 RaiseScore(level.score[SC_PACMAN]);
14727 RaiseScore(level.score[SC_NUT]);
14730 case EL_EM_DYNAMITE:
14731 case EL_SP_DISK_RED:
14732 case EL_DYNABOMB_INCREASE_NUMBER:
14733 case EL_DYNABOMB_INCREASE_SIZE:
14734 case EL_DYNABOMB_INCREASE_POWER:
14735 RaiseScore(level.score[SC_DYNAMITE]);
14737 case EL_SHIELD_NORMAL:
14738 case EL_SHIELD_DEADLY:
14739 RaiseScore(level.score[SC_SHIELD]);
14741 case EL_EXTRA_TIME:
14742 RaiseScore(level.extra_time_score);
14756 case EL_DC_KEY_WHITE:
14757 RaiseScore(level.score[SC_KEY]);
14760 RaiseScore(element_info[element].collect_score);
14765 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14767 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14769 #if defined(NETWORK_AVALIABLE)
14770 if (options.network)
14771 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14780 FadeSkipNextFadeIn();
14782 fading = fading_none;
14786 OpenDoor(DOOR_CLOSE_1);
14789 game_status = GAME_MODE_MAIN;
14792 DrawAndFadeInMainMenu(REDRAW_FIELD);
14800 FadeOut(REDRAW_FIELD);
14803 game_status = GAME_MODE_MAIN;
14805 DrawAndFadeInMainMenu(REDRAW_FIELD);
14809 else /* continue playing the game */
14811 if (tape.playing && tape.deactivate_display)
14812 TapeDeactivateDisplayOff(TRUE);
14814 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14816 if (tape.playing && tape.deactivate_display)
14817 TapeDeactivateDisplayOn();
14821 void RequestQuitGame(boolean ask_if_really_quit)
14823 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14824 boolean skip_request = AllPlayersGone || quick_quit;
14826 RequestQuitGameExt(skip_request, quick_quit,
14827 "Do you really want to quit the game ?");
14831 /* ------------------------------------------------------------------------- */
14832 /* random generator functions */
14833 /* ------------------------------------------------------------------------- */
14835 unsigned int InitEngineRandom_RND(long seed)
14837 game.num_random_calls = 0;
14840 unsigned int rnd_seed = InitEngineRandom(seed);
14842 printf("::: START RND: %d\n", rnd_seed);
14847 return InitEngineRandom(seed);
14853 unsigned int RND(int max)
14857 game.num_random_calls++;
14859 return GetEngineRandom(max);
14866 /* ------------------------------------------------------------------------- */
14867 /* game engine snapshot handling functions */
14868 /* ------------------------------------------------------------------------- */
14870 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14872 struct EngineSnapshotInfo
14874 /* runtime values for custom element collect score */
14875 int collect_score[NUM_CUSTOM_ELEMENTS];
14877 /* runtime values for group element choice position */
14878 int choice_pos[NUM_GROUP_ELEMENTS];
14880 /* runtime values for belt position animations */
14881 int belt_graphic[4 * NUM_BELT_PARTS];
14882 int belt_anim_mode[4 * NUM_BELT_PARTS];
14885 struct EngineSnapshotNodeInfo
14892 static struct EngineSnapshotInfo engine_snapshot_rnd;
14893 static ListNode *engine_snapshot_list = NULL;
14894 static char *snapshot_level_identifier = NULL;
14895 static int snapshot_level_nr = -1;
14897 void FreeEngineSnapshot()
14899 while (engine_snapshot_list != NULL)
14900 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14903 setString(&snapshot_level_identifier, NULL);
14904 snapshot_level_nr = -1;
14907 static void SaveEngineSnapshotValues_RND()
14909 static int belt_base_active_element[4] =
14911 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14912 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14913 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14914 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14918 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14920 int element = EL_CUSTOM_START + i;
14922 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14925 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14927 int element = EL_GROUP_START + i;
14929 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14932 for (i = 0; i < 4; i++)
14934 for (j = 0; j < NUM_BELT_PARTS; j++)
14936 int element = belt_base_active_element[i] + j;
14937 int graphic = el2img(element);
14938 int anim_mode = graphic_info[graphic].anim_mode;
14940 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14941 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14946 static void LoadEngineSnapshotValues_RND()
14948 unsigned long num_random_calls = game.num_random_calls;
14951 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14953 int element = EL_CUSTOM_START + i;
14955 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14958 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14960 int element = EL_GROUP_START + i;
14962 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14965 for (i = 0; i < 4; i++)
14967 for (j = 0; j < NUM_BELT_PARTS; j++)
14969 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14970 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14972 graphic_info[graphic].anim_mode = anim_mode;
14976 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14978 InitRND(tape.random_seed);
14979 for (i = 0; i < num_random_calls; i++)
14983 if (game.num_random_calls != num_random_calls)
14985 Error(ERR_INFO, "number of random calls out of sync");
14986 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14987 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14988 Error(ERR_EXIT, "this should not happen -- please debug");
14992 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14994 struct EngineSnapshotNodeInfo *bi =
14995 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14997 bi->buffer_orig = buffer;
14998 bi->buffer_copy = checked_malloc(size);
15001 memcpy(bi->buffer_copy, buffer, size);
15003 addNodeToList(&engine_snapshot_list, NULL, bi);
15006 void SaveEngineSnapshot()
15008 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15010 if (level_editor_test_game) /* do not save snapshots from editor */
15013 /* copy some special values to a structure better suited for the snapshot */
15015 SaveEngineSnapshotValues_RND();
15016 SaveEngineSnapshotValues_EM();
15018 /* save values stored in special snapshot structure */
15020 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15021 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15023 /* save further RND engine values */
15025 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15026 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15027 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15029 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15030 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15031 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15032 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15034 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15035 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15036 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15037 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15038 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15040 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15041 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15042 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15044 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15046 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15048 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15049 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15051 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15052 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15053 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15054 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15055 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15056 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15057 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15058 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15059 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15060 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15061 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15062 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15063 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15064 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15065 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15066 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15067 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15068 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15070 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15071 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15073 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15074 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15075 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15077 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15078 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15080 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15081 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15082 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15083 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15084 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15086 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15087 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15089 /* save level identification information */
15091 setString(&snapshot_level_identifier, leveldir_current->identifier);
15092 snapshot_level_nr = level_nr;
15095 ListNode *node = engine_snapshot_list;
15098 while (node != NULL)
15100 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15105 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15109 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15111 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15114 void LoadEngineSnapshot()
15116 ListNode *node = engine_snapshot_list;
15118 if (engine_snapshot_list == NULL)
15121 while (node != NULL)
15123 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15128 /* restore special values from snapshot structure */
15130 LoadEngineSnapshotValues_RND();
15131 LoadEngineSnapshotValues_EM();
15134 boolean CheckEngineSnapshot()
15136 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15137 snapshot_level_nr == level_nr);
15141 /* ---------- new game button stuff ---------------------------------------- */
15143 /* graphic position values for game buttons */
15144 #define GAME_BUTTON_XSIZE 30
15145 #define GAME_BUTTON_YSIZE 30
15146 #define GAME_BUTTON_XPOS 5
15147 #define GAME_BUTTON_YPOS 215
15148 #define SOUND_BUTTON_XPOS 5
15149 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15151 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15152 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15153 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15154 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15155 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15156 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15164 } gamebutton_info[NUM_GAME_BUTTONS] =
15168 &game.button.stop.x, &game.button.stop.y,
15169 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15174 &game.button.pause.x, &game.button.pause.y,
15175 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15176 GAME_CTRL_ID_PAUSE,
15180 &game.button.play.x, &game.button.play.y,
15181 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15186 &game.button.sound_music.x, &game.button.sound_music.y,
15187 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15188 SOUND_CTRL_ID_MUSIC,
15189 "background music on/off"
15192 &game.button.sound_loops.x, &game.button.sound_loops.y,
15193 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15194 SOUND_CTRL_ID_LOOPS,
15195 "sound loops on/off"
15198 &game.button.sound_simple.x,&game.button.sound_simple.y,
15199 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15200 SOUND_CTRL_ID_SIMPLE,
15201 "normal sounds on/off"
15205 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15210 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15211 GAME_CTRL_ID_PAUSE,
15215 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15220 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15221 SOUND_CTRL_ID_MUSIC,
15222 "background music on/off"
15225 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15226 SOUND_CTRL_ID_LOOPS,
15227 "sound loops on/off"
15230 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15231 SOUND_CTRL_ID_SIMPLE,
15232 "normal sounds on/off"
15237 void CreateGameButtons()
15241 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15243 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15244 struct GadgetInfo *gi;
15247 unsigned long event_mask;
15249 int gd_xoffset, gd_yoffset;
15250 int gd_x1, gd_x2, gd_y1, gd_y2;
15253 x = DX + *gamebutton_info[i].x;
15254 y = DY + *gamebutton_info[i].y;
15255 gd_xoffset = gamebutton_info[i].gd_x;
15256 gd_yoffset = gamebutton_info[i].gd_y;
15257 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15258 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15260 if (id == GAME_CTRL_ID_STOP ||
15261 id == GAME_CTRL_ID_PAUSE ||
15262 id == GAME_CTRL_ID_PLAY)
15264 button_type = GD_TYPE_NORMAL_BUTTON;
15266 event_mask = GD_EVENT_RELEASED;
15267 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15268 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15272 button_type = GD_TYPE_CHECK_BUTTON;
15274 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15275 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15276 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15277 event_mask = GD_EVENT_PRESSED;
15278 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15279 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15282 gi = CreateGadget(GDI_CUSTOM_ID, id,
15283 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15288 GDI_X, DX + gd_xoffset,
15289 GDI_Y, DY + gd_yoffset,
15291 GDI_WIDTH, GAME_BUTTON_XSIZE,
15292 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15293 GDI_TYPE, button_type,
15294 GDI_STATE, GD_BUTTON_UNPRESSED,
15295 GDI_CHECKED, checked,
15296 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15297 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15298 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15299 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15300 GDI_EVENT_MASK, event_mask,
15301 GDI_CALLBACK_ACTION, HandleGameButtons,
15305 Error(ERR_EXIT, "cannot create gadget");
15307 game_gadget[id] = gi;
15311 void FreeGameButtons()
15315 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15316 FreeGadget(game_gadget[i]);
15319 static void MapGameButtons()
15323 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15324 MapGadget(game_gadget[i]);
15327 void UnmapGameButtons()
15331 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15332 UnmapGadget(game_gadget[i]);
15335 static void HandleGameButtons(struct GadgetInfo *gi)
15337 int id = gi->custom_id;
15339 if (game_status != GAME_MODE_PLAYING)
15344 case GAME_CTRL_ID_STOP:
15348 RequestQuitGame(TRUE);
15351 case GAME_CTRL_ID_PAUSE:
15352 if (options.network)
15354 #if defined(NETWORK_AVALIABLE)
15356 SendToServer_ContinuePlaying();
15358 SendToServer_PausePlaying();
15362 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15365 case GAME_CTRL_ID_PLAY:
15368 #if defined(NETWORK_AVALIABLE)
15369 if (options.network)
15370 SendToServer_ContinuePlaying();
15374 tape.pausing = FALSE;
15375 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15380 case SOUND_CTRL_ID_MUSIC:
15381 if (setup.sound_music)
15383 setup.sound_music = FALSE;
15386 else if (audio.music_available)
15388 setup.sound = setup.sound_music = TRUE;
15390 SetAudioMode(setup.sound);
15396 case SOUND_CTRL_ID_LOOPS:
15397 if (setup.sound_loops)
15398 setup.sound_loops = FALSE;
15399 else if (audio.loops_available)
15401 setup.sound = setup.sound_loops = TRUE;
15402 SetAudioMode(setup.sound);
15406 case SOUND_CTRL_ID_SIMPLE:
15407 if (setup.sound_simple)
15408 setup.sound_simple = FALSE;
15409 else if (audio.sound_available)
15411 setup.sound = setup.sound_simple = TRUE;
15412 SetAudioMode(setup.sound);