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_1 = el2img(element);
5469 int graphic_2 = el2panelimg(element);
5471 if (game.belt_dir[i] == MV_LEFT)
5473 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5474 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5478 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5479 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5484 SCAN_PLAYFIELD(x, y)
5486 int element = Feld[x][y];
5488 for (i = 0; i < NUM_BELTS; i++)
5490 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5492 int e_belt_nr = getBeltNrFromBeltElement(element);
5495 if (e_belt_nr == belt_nr)
5497 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5499 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5506 static void ToggleBeltSwitch(int x, int y)
5508 static int belt_base_element[4] =
5510 EL_CONVEYOR_BELT_1_LEFT,
5511 EL_CONVEYOR_BELT_2_LEFT,
5512 EL_CONVEYOR_BELT_3_LEFT,
5513 EL_CONVEYOR_BELT_4_LEFT
5515 static int belt_base_active_element[4] =
5517 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5518 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5519 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5520 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5522 static int belt_base_switch_element[4] =
5524 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5525 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5526 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5527 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5529 static int belt_move_dir[4] =
5537 int element = Feld[x][y];
5538 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5539 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5540 int belt_dir = belt_move_dir[belt_dir_nr];
5543 if (!IS_BELT_SWITCH(element))
5546 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5547 game.belt_dir[belt_nr] = belt_dir;
5549 if (belt_dir_nr == 3)
5552 /* set frame order for belt animation graphic according to belt direction */
5553 for (i = 0; i < NUM_BELT_PARTS; i++)
5555 int element = belt_base_active_element[belt_nr] + i;
5556 int graphic_1 = el2img(element);
5557 int graphic_2 = el2panelimg(element);
5559 if (belt_dir == MV_LEFT)
5561 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5562 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5566 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5567 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5571 SCAN_PLAYFIELD(xx, yy)
5573 int element = Feld[xx][yy];
5575 if (IS_BELT_SWITCH(element))
5577 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5579 if (e_belt_nr == belt_nr)
5581 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5582 DrawLevelField(xx, yy);
5585 else if (IS_BELT(element) && belt_dir != MV_NONE)
5587 int e_belt_nr = getBeltNrFromBeltElement(element);
5589 if (e_belt_nr == belt_nr)
5591 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5593 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5594 DrawLevelField(xx, yy);
5597 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5599 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5601 if (e_belt_nr == belt_nr)
5603 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5605 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5606 DrawLevelField(xx, yy);
5612 static void ToggleSwitchgateSwitch(int x, int y)
5616 game.switchgate_pos = !game.switchgate_pos;
5618 SCAN_PLAYFIELD(xx, yy)
5620 int element = Feld[xx][yy];
5622 #if !USE_BOTH_SWITCHGATE_SWITCHES
5623 if (element == EL_SWITCHGATE_SWITCH_UP ||
5624 element == EL_SWITCHGATE_SWITCH_DOWN)
5626 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5627 DrawLevelField(xx, yy);
5629 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5630 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5632 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5633 DrawLevelField(xx, yy);
5636 if (element == EL_SWITCHGATE_SWITCH_UP)
5638 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5639 DrawLevelField(xx, yy);
5641 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5643 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5644 DrawLevelField(xx, yy);
5646 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5648 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5649 DrawLevelField(xx, yy);
5651 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5653 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5654 DrawLevelField(xx, yy);
5657 else if (element == EL_SWITCHGATE_OPEN ||
5658 element == EL_SWITCHGATE_OPENING)
5660 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5662 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5664 else if (element == EL_SWITCHGATE_CLOSED ||
5665 element == EL_SWITCHGATE_CLOSING)
5667 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5669 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5674 static int getInvisibleActiveFromInvisibleElement(int element)
5676 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5677 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5678 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5682 static int getInvisibleFromInvisibleActiveElement(int element)
5684 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5685 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5686 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5690 static void RedrawAllLightSwitchesAndInvisibleElements()
5694 SCAN_PLAYFIELD(x, y)
5696 int element = Feld[x][y];
5698 if (element == EL_LIGHT_SWITCH &&
5699 game.light_time_left > 0)
5701 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5702 DrawLevelField(x, y);
5704 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5705 game.light_time_left == 0)
5707 Feld[x][y] = EL_LIGHT_SWITCH;
5708 DrawLevelField(x, y);
5710 else if (element == EL_EMC_DRIPPER &&
5711 game.light_time_left > 0)
5713 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5714 DrawLevelField(x, y);
5716 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5717 game.light_time_left == 0)
5719 Feld[x][y] = EL_EMC_DRIPPER;
5720 DrawLevelField(x, y);
5722 else if (element == EL_INVISIBLE_STEELWALL ||
5723 element == EL_INVISIBLE_WALL ||
5724 element == EL_INVISIBLE_SAND)
5726 if (game.light_time_left > 0)
5727 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5729 DrawLevelField(x, y);
5731 /* uncrumble neighbour fields, if needed */
5732 if (element == EL_INVISIBLE_SAND)
5733 DrawLevelFieldCrumbledSandNeighbours(x, y);
5735 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5736 element == EL_INVISIBLE_WALL_ACTIVE ||
5737 element == EL_INVISIBLE_SAND_ACTIVE)
5739 if (game.light_time_left == 0)
5740 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5742 DrawLevelField(x, y);
5744 /* re-crumble neighbour fields, if needed */
5745 if (element == EL_INVISIBLE_SAND)
5746 DrawLevelFieldCrumbledSandNeighbours(x, y);
5751 static void RedrawAllInvisibleElementsForLenses()
5755 SCAN_PLAYFIELD(x, y)
5757 int element = Feld[x][y];
5759 if (element == EL_EMC_DRIPPER &&
5760 game.lenses_time_left > 0)
5762 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5763 DrawLevelField(x, y);
5765 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5766 game.lenses_time_left == 0)
5768 Feld[x][y] = EL_EMC_DRIPPER;
5769 DrawLevelField(x, y);
5771 else if (element == EL_INVISIBLE_STEELWALL ||
5772 element == EL_INVISIBLE_WALL ||
5773 element == EL_INVISIBLE_SAND)
5775 if (game.lenses_time_left > 0)
5776 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5778 DrawLevelField(x, y);
5780 /* uncrumble neighbour fields, if needed */
5781 if (element == EL_INVISIBLE_SAND)
5782 DrawLevelFieldCrumbledSandNeighbours(x, y);
5784 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5785 element == EL_INVISIBLE_WALL_ACTIVE ||
5786 element == EL_INVISIBLE_SAND_ACTIVE)
5788 if (game.lenses_time_left == 0)
5789 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5791 DrawLevelField(x, y);
5793 /* re-crumble neighbour fields, if needed */
5794 if (element == EL_INVISIBLE_SAND)
5795 DrawLevelFieldCrumbledSandNeighbours(x, y);
5800 static void RedrawAllInvisibleElementsForMagnifier()
5804 SCAN_PLAYFIELD(x, y)
5806 int element = Feld[x][y];
5808 if (element == EL_EMC_FAKE_GRASS &&
5809 game.magnify_time_left > 0)
5811 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5812 DrawLevelField(x, y);
5814 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5815 game.magnify_time_left == 0)
5817 Feld[x][y] = EL_EMC_FAKE_GRASS;
5818 DrawLevelField(x, y);
5820 else if (IS_GATE_GRAY(element) &&
5821 game.magnify_time_left > 0)
5823 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5824 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5825 IS_EM_GATE_GRAY(element) ?
5826 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5827 IS_EMC_GATE_GRAY(element) ?
5828 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5830 DrawLevelField(x, y);
5832 else if (IS_GATE_GRAY_ACTIVE(element) &&
5833 game.magnify_time_left == 0)
5835 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5836 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5837 IS_EM_GATE_GRAY_ACTIVE(element) ?
5838 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5839 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5840 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5842 DrawLevelField(x, y);
5847 static void ToggleLightSwitch(int x, int y)
5849 int element = Feld[x][y];
5851 game.light_time_left =
5852 (element == EL_LIGHT_SWITCH ?
5853 level.time_light * FRAMES_PER_SECOND : 0);
5855 RedrawAllLightSwitchesAndInvisibleElements();
5858 static void ActivateTimegateSwitch(int x, int y)
5862 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5864 SCAN_PLAYFIELD(xx, yy)
5866 int element = Feld[xx][yy];
5868 if (element == EL_TIMEGATE_CLOSED ||
5869 element == EL_TIMEGATE_CLOSING)
5871 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5872 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5876 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5878 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5879 DrawLevelField(xx, yy);
5886 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5887 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5889 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5893 void Impact(int x, int y)
5895 boolean last_line = (y == lev_fieldy - 1);
5896 boolean object_hit = FALSE;
5897 boolean impact = (last_line || object_hit);
5898 int element = Feld[x][y];
5899 int smashed = EL_STEELWALL;
5901 if (!last_line) /* check if element below was hit */
5903 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5906 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5907 MovDir[x][y + 1] != MV_DOWN ||
5908 MovPos[x][y + 1] <= TILEY / 2));
5910 /* do not smash moving elements that left the smashed field in time */
5911 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5912 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5915 #if USE_QUICKSAND_IMPACT_BUGFIX
5916 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5918 RemoveMovingField(x, y + 1);
5919 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5920 Feld[x][y + 2] = EL_ROCK;
5921 DrawLevelField(x, y + 2);
5926 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5928 RemoveMovingField(x, y + 1);
5929 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5930 Feld[x][y + 2] = EL_ROCK;
5931 DrawLevelField(x, y + 2);
5938 smashed = MovingOrBlocked2Element(x, y + 1);
5940 impact = (last_line || object_hit);
5943 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5945 SplashAcid(x, y + 1);
5949 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5950 /* only reset graphic animation if graphic really changes after impact */
5952 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5954 ResetGfxAnimation(x, y);
5955 DrawLevelField(x, y);
5958 if (impact && CAN_EXPLODE_IMPACT(element))
5963 else if (impact && element == EL_PEARL &&
5964 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5966 ResetGfxAnimation(x, y);
5968 Feld[x][y] = EL_PEARL_BREAKING;
5969 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5972 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5974 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5979 if (impact && element == EL_AMOEBA_DROP)
5981 if (object_hit && IS_PLAYER(x, y + 1))
5982 KillPlayerUnlessEnemyProtected(x, y + 1);
5983 else if (object_hit && smashed == EL_PENGUIN)
5987 Feld[x][y] = EL_AMOEBA_GROWING;
5988 Store[x][y] = EL_AMOEBA_WET;
5990 ResetRandomAnimationValue(x, y);
5995 if (object_hit) /* check which object was hit */
5997 if ((CAN_PASS_MAGIC_WALL(element) &&
5998 (smashed == EL_MAGIC_WALL ||
5999 smashed == EL_BD_MAGIC_WALL)) ||
6000 (CAN_PASS_DC_MAGIC_WALL(element) &&
6001 smashed == EL_DC_MAGIC_WALL))
6004 int activated_magic_wall =
6005 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6006 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6007 EL_DC_MAGIC_WALL_ACTIVE);
6009 /* activate magic wall / mill */
6010 SCAN_PLAYFIELD(xx, yy)
6012 if (Feld[xx][yy] == smashed)
6013 Feld[xx][yy] = activated_magic_wall;
6016 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6017 game.magic_wall_active = TRUE;
6019 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6020 SND_MAGIC_WALL_ACTIVATING :
6021 smashed == EL_BD_MAGIC_WALL ?
6022 SND_BD_MAGIC_WALL_ACTIVATING :
6023 SND_DC_MAGIC_WALL_ACTIVATING));
6026 if (IS_PLAYER(x, y + 1))
6028 if (CAN_SMASH_PLAYER(element))
6030 KillPlayerUnlessEnemyProtected(x, y + 1);
6034 else if (smashed == EL_PENGUIN)
6036 if (CAN_SMASH_PLAYER(element))
6042 else if (element == EL_BD_DIAMOND)
6044 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6050 else if (((element == EL_SP_INFOTRON ||
6051 element == EL_SP_ZONK) &&
6052 (smashed == EL_SP_SNIKSNAK ||
6053 smashed == EL_SP_ELECTRON ||
6054 smashed == EL_SP_DISK_ORANGE)) ||
6055 (element == EL_SP_INFOTRON &&
6056 smashed == EL_SP_DISK_YELLOW))
6061 else if (CAN_SMASH_EVERYTHING(element))
6063 if (IS_CLASSIC_ENEMY(smashed) ||
6064 CAN_EXPLODE_SMASHED(smashed))
6069 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6071 if (smashed == EL_LAMP ||
6072 smashed == EL_LAMP_ACTIVE)
6077 else if (smashed == EL_NUT)
6079 Feld[x][y + 1] = EL_NUT_BREAKING;
6080 PlayLevelSound(x, y, SND_NUT_BREAKING);
6081 RaiseScoreElement(EL_NUT);
6084 else if (smashed == EL_PEARL)
6086 ResetGfxAnimation(x, y);
6088 Feld[x][y + 1] = EL_PEARL_BREAKING;
6089 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6092 else if (smashed == EL_DIAMOND)
6094 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6095 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6098 else if (IS_BELT_SWITCH(smashed))
6100 ToggleBeltSwitch(x, y + 1);
6102 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6103 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6104 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6105 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6107 ToggleSwitchgateSwitch(x, y + 1);
6109 else if (smashed == EL_LIGHT_SWITCH ||
6110 smashed == EL_LIGHT_SWITCH_ACTIVE)
6112 ToggleLightSwitch(x, y + 1);
6117 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6120 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6122 CheckElementChangeBySide(x, y + 1, smashed, element,
6123 CE_SWITCHED, CH_SIDE_TOP);
6124 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6130 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6135 /* play sound of magic wall / mill */
6137 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6138 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6139 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6141 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6142 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6143 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6144 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6145 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6146 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6151 /* play sound of object that hits the ground */
6152 if (last_line || object_hit)
6153 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6156 inline static void TurnRoundExt(int x, int y)
6168 { 0, 0 }, { 0, 0 }, { 0, 0 },
6173 int left, right, back;
6177 { MV_DOWN, MV_UP, MV_RIGHT },
6178 { MV_UP, MV_DOWN, MV_LEFT },
6180 { MV_LEFT, MV_RIGHT, MV_DOWN },
6184 { MV_RIGHT, MV_LEFT, MV_UP }
6187 int element = Feld[x][y];
6188 int move_pattern = element_info[element].move_pattern;
6190 int old_move_dir = MovDir[x][y];
6191 int left_dir = turn[old_move_dir].left;
6192 int right_dir = turn[old_move_dir].right;
6193 int back_dir = turn[old_move_dir].back;
6195 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6196 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6197 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6198 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6200 int left_x = x + left_dx, left_y = y + left_dy;
6201 int right_x = x + right_dx, right_y = y + right_dy;
6202 int move_x = x + move_dx, move_y = y + move_dy;
6206 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6208 TestIfBadThingTouchesOtherBadThing(x, y);
6210 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6211 MovDir[x][y] = right_dir;
6212 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6213 MovDir[x][y] = left_dir;
6215 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6217 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6220 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6222 TestIfBadThingTouchesOtherBadThing(x, y);
6224 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6225 MovDir[x][y] = left_dir;
6226 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6227 MovDir[x][y] = right_dir;
6229 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6231 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6234 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6236 TestIfBadThingTouchesOtherBadThing(x, y);
6238 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6239 MovDir[x][y] = left_dir;
6240 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6241 MovDir[x][y] = right_dir;
6243 if (MovDir[x][y] != old_move_dir)
6246 else if (element == EL_YAMYAM)
6248 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6249 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6251 if (can_turn_left && can_turn_right)
6252 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6253 else if (can_turn_left)
6254 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6255 else if (can_turn_right)
6256 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6258 MovDir[x][y] = back_dir;
6260 MovDelay[x][y] = 16 + 16 * RND(3);
6262 else if (element == EL_DARK_YAMYAM)
6264 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6266 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6269 if (can_turn_left && can_turn_right)
6270 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6271 else if (can_turn_left)
6272 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6273 else if (can_turn_right)
6274 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6276 MovDir[x][y] = back_dir;
6278 MovDelay[x][y] = 16 + 16 * RND(3);
6280 else if (element == EL_PACMAN)
6282 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6283 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6285 if (can_turn_left && can_turn_right)
6286 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6287 else if (can_turn_left)
6288 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6289 else if (can_turn_right)
6290 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6292 MovDir[x][y] = back_dir;
6294 MovDelay[x][y] = 6 + RND(40);
6296 else if (element == EL_PIG)
6298 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6299 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6300 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6301 boolean should_turn_left, should_turn_right, should_move_on;
6303 int rnd = RND(rnd_value);
6305 should_turn_left = (can_turn_left &&
6307 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6308 y + back_dy + left_dy)));
6309 should_turn_right = (can_turn_right &&
6311 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6312 y + back_dy + right_dy)));
6313 should_move_on = (can_move_on &&
6316 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6317 y + move_dy + left_dy) ||
6318 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6319 y + move_dy + right_dy)));
6321 if (should_turn_left || should_turn_right || should_move_on)
6323 if (should_turn_left && should_turn_right && should_move_on)
6324 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6325 rnd < 2 * rnd_value / 3 ? right_dir :
6327 else if (should_turn_left && should_turn_right)
6328 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6329 else if (should_turn_left && should_move_on)
6330 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6331 else if (should_turn_right && should_move_on)
6332 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6333 else if (should_turn_left)
6334 MovDir[x][y] = left_dir;
6335 else if (should_turn_right)
6336 MovDir[x][y] = right_dir;
6337 else if (should_move_on)
6338 MovDir[x][y] = old_move_dir;
6340 else if (can_move_on && rnd > rnd_value / 8)
6341 MovDir[x][y] = old_move_dir;
6342 else if (can_turn_left && can_turn_right)
6343 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6344 else if (can_turn_left && rnd > rnd_value / 8)
6345 MovDir[x][y] = left_dir;
6346 else if (can_turn_right && rnd > rnd_value/8)
6347 MovDir[x][y] = right_dir;
6349 MovDir[x][y] = back_dir;
6351 xx = x + move_xy[MovDir[x][y]].dx;
6352 yy = y + move_xy[MovDir[x][y]].dy;
6354 if (!IN_LEV_FIELD(xx, yy) ||
6355 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6356 MovDir[x][y] = old_move_dir;
6360 else if (element == EL_DRAGON)
6362 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6363 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6364 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6366 int rnd = RND(rnd_value);
6368 if (can_move_on && rnd > rnd_value / 8)
6369 MovDir[x][y] = old_move_dir;
6370 else if (can_turn_left && can_turn_right)
6371 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6372 else if (can_turn_left && rnd > rnd_value / 8)
6373 MovDir[x][y] = left_dir;
6374 else if (can_turn_right && rnd > rnd_value / 8)
6375 MovDir[x][y] = right_dir;
6377 MovDir[x][y] = back_dir;
6379 xx = x + move_xy[MovDir[x][y]].dx;
6380 yy = y + move_xy[MovDir[x][y]].dy;
6382 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6383 MovDir[x][y] = old_move_dir;
6387 else if (element == EL_MOLE)
6389 boolean can_move_on =
6390 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6391 IS_AMOEBOID(Feld[move_x][move_y]) ||
6392 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6395 boolean can_turn_left =
6396 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6397 IS_AMOEBOID(Feld[left_x][left_y])));
6399 boolean can_turn_right =
6400 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6401 IS_AMOEBOID(Feld[right_x][right_y])));
6403 if (can_turn_left && can_turn_right)
6404 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6405 else if (can_turn_left)
6406 MovDir[x][y] = left_dir;
6408 MovDir[x][y] = right_dir;
6411 if (MovDir[x][y] != old_move_dir)
6414 else if (element == EL_BALLOON)
6416 MovDir[x][y] = game.wind_direction;
6419 else if (element == EL_SPRING)
6421 #if USE_NEW_SPRING_BUMPER
6422 if (MovDir[x][y] & MV_HORIZONTAL)
6424 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6425 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6427 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6428 ResetGfxAnimation(move_x, move_y);
6429 DrawLevelField(move_x, move_y);
6431 MovDir[x][y] = back_dir;
6433 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6434 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6435 MovDir[x][y] = MV_NONE;
6438 if (MovDir[x][y] & MV_HORIZONTAL &&
6439 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6440 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6441 MovDir[x][y] = MV_NONE;
6446 else if (element == EL_ROBOT ||
6447 element == EL_SATELLITE ||
6448 element == EL_PENGUIN ||
6449 element == EL_EMC_ANDROID)
6451 int attr_x = -1, attr_y = -1;
6462 for (i = 0; i < MAX_PLAYERS; i++)
6464 struct PlayerInfo *player = &stored_player[i];
6465 int jx = player->jx, jy = player->jy;
6467 if (!player->active)
6471 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6479 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6480 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6481 game.engine_version < VERSION_IDENT(3,1,0,0)))
6487 if (element == EL_PENGUIN)
6490 static int xy[4][2] =
6498 for (i = 0; i < NUM_DIRECTIONS; i++)
6500 int ex = x + xy[i][0];
6501 int ey = y + xy[i][1];
6503 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6504 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6505 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6506 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6515 MovDir[x][y] = MV_NONE;
6517 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6518 else if (attr_x > x)
6519 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6521 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6522 else if (attr_y > y)
6523 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6525 if (element == EL_ROBOT)
6529 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6530 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6531 Moving2Blocked(x, y, &newx, &newy);
6533 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6534 MovDelay[x][y] = 8 + 8 * !RND(3);
6536 MovDelay[x][y] = 16;
6538 else if (element == EL_PENGUIN)
6544 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6546 boolean first_horiz = RND(2);
6547 int new_move_dir = MovDir[x][y];
6550 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6551 Moving2Blocked(x, y, &newx, &newy);
6553 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6557 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6558 Moving2Blocked(x, y, &newx, &newy);
6560 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6563 MovDir[x][y] = old_move_dir;
6567 else if (element == EL_SATELLITE)
6573 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6575 boolean first_horiz = RND(2);
6576 int new_move_dir = MovDir[x][y];
6579 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6580 Moving2Blocked(x, y, &newx, &newy);
6582 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6586 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6587 Moving2Blocked(x, y, &newx, &newy);
6589 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6592 MovDir[x][y] = old_move_dir;
6596 else if (element == EL_EMC_ANDROID)
6598 static int check_pos[16] =
6600 -1, /* 0 => (invalid) */
6601 7, /* 1 => MV_LEFT */
6602 3, /* 2 => MV_RIGHT */
6603 -1, /* 3 => (invalid) */
6605 0, /* 5 => MV_LEFT | MV_UP */
6606 2, /* 6 => MV_RIGHT | MV_UP */
6607 -1, /* 7 => (invalid) */
6608 5, /* 8 => MV_DOWN */
6609 6, /* 9 => MV_LEFT | MV_DOWN */
6610 4, /* 10 => MV_RIGHT | MV_DOWN */
6611 -1, /* 11 => (invalid) */
6612 -1, /* 12 => (invalid) */
6613 -1, /* 13 => (invalid) */
6614 -1, /* 14 => (invalid) */
6615 -1, /* 15 => (invalid) */
6623 { -1, -1, MV_LEFT | MV_UP },
6625 { +1, -1, MV_RIGHT | MV_UP },
6626 { +1, 0, MV_RIGHT },
6627 { +1, +1, MV_RIGHT | MV_DOWN },
6629 { -1, +1, MV_LEFT | MV_DOWN },
6632 int start_pos, check_order;
6633 boolean can_clone = FALSE;
6636 /* check if there is any free field around current position */
6637 for (i = 0; i < 8; i++)
6639 int newx = x + check_xy[i].dx;
6640 int newy = y + check_xy[i].dy;
6642 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6650 if (can_clone) /* randomly find an element to clone */
6654 start_pos = check_pos[RND(8)];
6655 check_order = (RND(2) ? -1 : +1);
6657 for (i = 0; i < 8; i++)
6659 int pos_raw = start_pos + i * check_order;
6660 int pos = (pos_raw + 8) % 8;
6661 int newx = x + check_xy[pos].dx;
6662 int newy = y + check_xy[pos].dy;
6664 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6666 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6667 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6669 Store[x][y] = Feld[newx][newy];
6678 if (can_clone) /* randomly find a direction to move */
6682 start_pos = check_pos[RND(8)];
6683 check_order = (RND(2) ? -1 : +1);
6685 for (i = 0; i < 8; i++)
6687 int pos_raw = start_pos + i * check_order;
6688 int pos = (pos_raw + 8) % 8;
6689 int newx = x + check_xy[pos].dx;
6690 int newy = y + check_xy[pos].dy;
6691 int new_move_dir = check_xy[pos].dir;
6693 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6695 MovDir[x][y] = new_move_dir;
6696 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6705 if (can_clone) /* cloning and moving successful */
6708 /* cannot clone -- try to move towards player */
6710 start_pos = check_pos[MovDir[x][y] & 0x0f];
6711 check_order = (RND(2) ? -1 : +1);
6713 for (i = 0; i < 3; i++)
6715 /* first check start_pos, then previous/next or (next/previous) pos */
6716 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6717 int pos = (pos_raw + 8) % 8;
6718 int newx = x + check_xy[pos].dx;
6719 int newy = y + check_xy[pos].dy;
6720 int new_move_dir = check_xy[pos].dir;
6722 if (IS_PLAYER(newx, newy))
6725 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6727 MovDir[x][y] = new_move_dir;
6728 MovDelay[x][y] = level.android_move_time * 8 + 1;
6735 else if (move_pattern == MV_TURNING_LEFT ||
6736 move_pattern == MV_TURNING_RIGHT ||
6737 move_pattern == MV_TURNING_LEFT_RIGHT ||
6738 move_pattern == MV_TURNING_RIGHT_LEFT ||
6739 move_pattern == MV_TURNING_RANDOM ||
6740 move_pattern == MV_ALL_DIRECTIONS)
6742 boolean can_turn_left =
6743 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6744 boolean can_turn_right =
6745 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6747 if (element_info[element].move_stepsize == 0) /* "not moving" */
6750 if (move_pattern == MV_TURNING_LEFT)
6751 MovDir[x][y] = left_dir;
6752 else if (move_pattern == MV_TURNING_RIGHT)
6753 MovDir[x][y] = right_dir;
6754 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6755 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6756 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6757 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6758 else if (move_pattern == MV_TURNING_RANDOM)
6759 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6760 can_turn_right && !can_turn_left ? right_dir :
6761 RND(2) ? left_dir : right_dir);
6762 else if (can_turn_left && can_turn_right)
6763 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6764 else if (can_turn_left)
6765 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6766 else if (can_turn_right)
6767 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6769 MovDir[x][y] = back_dir;
6771 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6773 else if (move_pattern == MV_HORIZONTAL ||
6774 move_pattern == MV_VERTICAL)
6776 if (move_pattern & old_move_dir)
6777 MovDir[x][y] = back_dir;
6778 else if (move_pattern == MV_HORIZONTAL)
6779 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6780 else if (move_pattern == MV_VERTICAL)
6781 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6783 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6785 else if (move_pattern & MV_ANY_DIRECTION)
6787 MovDir[x][y] = move_pattern;
6788 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6790 else if (move_pattern & MV_WIND_DIRECTION)
6792 MovDir[x][y] = game.wind_direction;
6793 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6795 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6797 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6798 MovDir[x][y] = left_dir;
6799 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6800 MovDir[x][y] = right_dir;
6802 if (MovDir[x][y] != old_move_dir)
6803 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6805 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6807 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6808 MovDir[x][y] = right_dir;
6809 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6810 MovDir[x][y] = left_dir;
6812 if (MovDir[x][y] != old_move_dir)
6813 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6815 else if (move_pattern == MV_TOWARDS_PLAYER ||
6816 move_pattern == MV_AWAY_FROM_PLAYER)
6818 int attr_x = -1, attr_y = -1;
6820 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6831 for (i = 0; i < MAX_PLAYERS; i++)
6833 struct PlayerInfo *player = &stored_player[i];
6834 int jx = player->jx, jy = player->jy;
6836 if (!player->active)
6840 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6848 MovDir[x][y] = MV_NONE;
6850 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6851 else if (attr_x > x)
6852 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6854 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6855 else if (attr_y > y)
6856 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6858 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6860 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6862 boolean first_horiz = RND(2);
6863 int new_move_dir = MovDir[x][y];
6865 if (element_info[element].move_stepsize == 0) /* "not moving" */
6867 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6868 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6874 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6875 Moving2Blocked(x, y, &newx, &newy);
6877 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6881 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6882 Moving2Blocked(x, y, &newx, &newy);
6884 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6887 MovDir[x][y] = old_move_dir;
6890 else if (move_pattern == MV_WHEN_PUSHED ||
6891 move_pattern == MV_WHEN_DROPPED)
6893 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6894 MovDir[x][y] = MV_NONE;
6898 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6900 static int test_xy[7][2] =
6910 static int test_dir[7] =
6920 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6921 int move_preference = -1000000; /* start with very low preference */
6922 int new_move_dir = MV_NONE;
6923 int start_test = RND(4);
6926 for (i = 0; i < NUM_DIRECTIONS; i++)
6928 int move_dir = test_dir[start_test + i];
6929 int move_dir_preference;
6931 xx = x + test_xy[start_test + i][0];
6932 yy = y + test_xy[start_test + i][1];
6934 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6935 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6937 new_move_dir = move_dir;
6942 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6945 move_dir_preference = -1 * RunnerVisit[xx][yy];
6946 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6947 move_dir_preference = PlayerVisit[xx][yy];
6949 if (move_dir_preference > move_preference)
6951 /* prefer field that has not been visited for the longest time */
6952 move_preference = move_dir_preference;
6953 new_move_dir = move_dir;
6955 else if (move_dir_preference == move_preference &&
6956 move_dir == old_move_dir)
6958 /* prefer last direction when all directions are preferred equally */
6959 move_preference = move_dir_preference;
6960 new_move_dir = move_dir;
6964 MovDir[x][y] = new_move_dir;
6965 if (old_move_dir != new_move_dir)
6966 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6970 static void TurnRound(int x, int y)
6972 int direction = MovDir[x][y];
6976 GfxDir[x][y] = MovDir[x][y];
6978 if (direction != MovDir[x][y])
6982 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6984 ResetGfxFrame(x, y, FALSE);
6987 static boolean JustBeingPushed(int x, int y)
6991 for (i = 0; i < MAX_PLAYERS; i++)
6993 struct PlayerInfo *player = &stored_player[i];
6995 if (player->active && player->is_pushing && player->MovPos)
6997 int next_jx = player->jx + (player->jx - player->last_jx);
6998 int next_jy = player->jy + (player->jy - player->last_jy);
7000 if (x == next_jx && y == next_jy)
7008 void StartMoving(int x, int y)
7010 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7011 int element = Feld[x][y];
7016 if (MovDelay[x][y] == 0)
7017 GfxAction[x][y] = ACTION_DEFAULT;
7019 if (CAN_FALL(element) && y < lev_fieldy - 1)
7021 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7022 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7023 if (JustBeingPushed(x, y))
7026 if (element == EL_QUICKSAND_FULL)
7028 if (IS_FREE(x, y + 1))
7030 InitMovingField(x, y, MV_DOWN);
7031 started_moving = TRUE;
7033 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7034 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7035 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7036 Store[x][y] = EL_ROCK;
7038 Store[x][y] = EL_ROCK;
7041 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7043 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7045 if (!MovDelay[x][y])
7046 MovDelay[x][y] = TILEY + 1;
7055 Feld[x][y] = EL_QUICKSAND_EMPTY;
7056 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7057 Store[x][y + 1] = Store[x][y];
7060 PlayLevelSoundAction(x, y, ACTION_FILLING);
7063 else if (element == EL_QUICKSAND_FAST_FULL)
7065 if (IS_FREE(x, y + 1))
7067 InitMovingField(x, y, MV_DOWN);
7068 started_moving = TRUE;
7070 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7071 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7072 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7073 Store[x][y] = EL_ROCK;
7075 Store[x][y] = EL_ROCK;
7078 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7080 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7082 if (!MovDelay[x][y])
7083 MovDelay[x][y] = TILEY + 1;
7092 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7093 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7094 Store[x][y + 1] = Store[x][y];
7097 PlayLevelSoundAction(x, y, ACTION_FILLING);
7100 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7101 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7103 InitMovingField(x, y, MV_DOWN);
7104 started_moving = TRUE;
7106 Feld[x][y] = EL_QUICKSAND_FILLING;
7107 Store[x][y] = element;
7109 PlayLevelSoundAction(x, y, ACTION_FILLING);
7111 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7112 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7114 InitMovingField(x, y, MV_DOWN);
7115 started_moving = TRUE;
7117 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7118 Store[x][y] = element;
7120 PlayLevelSoundAction(x, y, ACTION_FILLING);
7122 else if (element == EL_MAGIC_WALL_FULL)
7124 if (IS_FREE(x, y + 1))
7126 InitMovingField(x, y, MV_DOWN);
7127 started_moving = TRUE;
7129 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7130 Store[x][y] = EL_CHANGED(Store[x][y]);
7132 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7134 if (!MovDelay[x][y])
7135 MovDelay[x][y] = TILEY/4 + 1;
7144 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7145 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7146 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7150 else if (element == EL_BD_MAGIC_WALL_FULL)
7152 if (IS_FREE(x, y + 1))
7154 InitMovingField(x, y, MV_DOWN);
7155 started_moving = TRUE;
7157 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7158 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7160 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7162 if (!MovDelay[x][y])
7163 MovDelay[x][y] = TILEY/4 + 1;
7172 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7173 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7174 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7178 else if (element == EL_DC_MAGIC_WALL_FULL)
7180 if (IS_FREE(x, y + 1))
7182 InitMovingField(x, y, MV_DOWN);
7183 started_moving = TRUE;
7185 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7186 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7188 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7190 if (!MovDelay[x][y])
7191 MovDelay[x][y] = TILEY/4 + 1;
7200 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7201 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7202 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7206 else if ((CAN_PASS_MAGIC_WALL(element) &&
7207 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7208 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7209 (CAN_PASS_DC_MAGIC_WALL(element) &&
7210 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7213 InitMovingField(x, y, MV_DOWN);
7214 started_moving = TRUE;
7217 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7218 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7219 EL_DC_MAGIC_WALL_FILLING);
7220 Store[x][y] = element;
7222 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7224 SplashAcid(x, y + 1);
7226 InitMovingField(x, y, MV_DOWN);
7227 started_moving = TRUE;
7229 Store[x][y] = EL_ACID;
7232 #if USE_FIX_IMPACT_COLLISION
7233 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7234 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7236 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7237 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7239 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7240 CAN_FALL(element) && WasJustFalling[x][y] &&
7241 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7243 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7244 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7245 (Feld[x][y + 1] == EL_BLOCKED)))
7247 /* this is needed for a special case not covered by calling "Impact()"
7248 from "ContinueMoving()": if an element moves to a tile directly below
7249 another element which was just falling on that tile (which was empty
7250 in the previous frame), the falling element above would just stop
7251 instead of smashing the element below (in previous version, the above
7252 element was just checked for "moving" instead of "falling", resulting
7253 in incorrect smashes caused by horizontal movement of the above
7254 element; also, the case of the player being the element to smash was
7255 simply not covered here... :-/ ) */
7257 CheckCollision[x][y] = 0;
7258 CheckImpact[x][y] = 0;
7262 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7264 if (MovDir[x][y] == MV_NONE)
7266 InitMovingField(x, y, MV_DOWN);
7267 started_moving = TRUE;
7270 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7272 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7273 MovDir[x][y] = MV_DOWN;
7275 InitMovingField(x, y, MV_DOWN);
7276 started_moving = TRUE;
7278 else if (element == EL_AMOEBA_DROP)
7280 Feld[x][y] = EL_AMOEBA_GROWING;
7281 Store[x][y] = EL_AMOEBA_WET;
7283 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7284 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7285 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7286 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7288 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7289 (IS_FREE(x - 1, y + 1) ||
7290 Feld[x - 1][y + 1] == EL_ACID));
7291 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7292 (IS_FREE(x + 1, y + 1) ||
7293 Feld[x + 1][y + 1] == EL_ACID));
7294 boolean can_fall_any = (can_fall_left || can_fall_right);
7295 boolean can_fall_both = (can_fall_left && can_fall_right);
7296 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7298 #if USE_NEW_ALL_SLIPPERY
7299 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7301 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7302 can_fall_right = FALSE;
7303 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7304 can_fall_left = FALSE;
7305 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7306 can_fall_right = FALSE;
7307 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7308 can_fall_left = FALSE;
7310 can_fall_any = (can_fall_left || can_fall_right);
7311 can_fall_both = FALSE;
7314 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7316 if (slippery_type == SLIPPERY_ONLY_LEFT)
7317 can_fall_right = FALSE;
7318 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7319 can_fall_left = FALSE;
7320 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7321 can_fall_right = FALSE;
7322 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7323 can_fall_left = FALSE;
7325 can_fall_any = (can_fall_left || can_fall_right);
7326 can_fall_both = (can_fall_left && can_fall_right);
7330 #if USE_NEW_ALL_SLIPPERY
7332 #if USE_NEW_SP_SLIPPERY
7333 /* !!! better use the same properties as for custom elements here !!! */
7334 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7335 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7337 can_fall_right = FALSE; /* slip down on left side */
7338 can_fall_both = FALSE;
7343 #if USE_NEW_ALL_SLIPPERY
7346 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7347 can_fall_right = FALSE; /* slip down on left side */
7349 can_fall_left = !(can_fall_right = RND(2));
7351 can_fall_both = FALSE;
7356 if (game.emulation == EMU_BOULDERDASH ||
7357 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7358 can_fall_right = FALSE; /* slip down on left side */
7360 can_fall_left = !(can_fall_right = RND(2));
7362 can_fall_both = FALSE;
7368 /* if not determined otherwise, prefer left side for slipping down */
7369 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7370 started_moving = TRUE;
7374 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7376 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7379 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7380 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7381 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7382 int belt_dir = game.belt_dir[belt_nr];
7384 if ((belt_dir == MV_LEFT && left_is_free) ||
7385 (belt_dir == MV_RIGHT && right_is_free))
7387 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7389 InitMovingField(x, y, belt_dir);
7390 started_moving = TRUE;
7392 Pushed[x][y] = TRUE;
7393 Pushed[nextx][y] = TRUE;
7395 GfxAction[x][y] = ACTION_DEFAULT;
7399 MovDir[x][y] = 0; /* if element was moving, stop it */
7404 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7406 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7408 if (CAN_MOVE(element) && !started_moving)
7411 int move_pattern = element_info[element].move_pattern;
7416 if (MovDir[x][y] == MV_NONE)
7418 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7419 x, y, element, element_info[element].token_name);
7420 printf("StartMoving(): This should never happen!\n");
7425 Moving2Blocked(x, y, &newx, &newy);
7427 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7430 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7431 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7433 WasJustMoving[x][y] = 0;
7434 CheckCollision[x][y] = 0;
7436 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7438 if (Feld[x][y] != element) /* element has changed */
7442 if (!MovDelay[x][y]) /* start new movement phase */
7444 /* all objects that can change their move direction after each step
7445 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7447 if (element != EL_YAMYAM &&
7448 element != EL_DARK_YAMYAM &&
7449 element != EL_PACMAN &&
7450 !(move_pattern & MV_ANY_DIRECTION) &&
7451 move_pattern != MV_TURNING_LEFT &&
7452 move_pattern != MV_TURNING_RIGHT &&
7453 move_pattern != MV_TURNING_LEFT_RIGHT &&
7454 move_pattern != MV_TURNING_RIGHT_LEFT &&
7455 move_pattern != MV_TURNING_RANDOM)
7459 if (MovDelay[x][y] && (element == EL_BUG ||
7460 element == EL_SPACESHIP ||
7461 element == EL_SP_SNIKSNAK ||
7462 element == EL_SP_ELECTRON ||
7463 element == EL_MOLE))
7464 DrawLevelField(x, y);
7468 if (MovDelay[x][y]) /* wait some time before next movement */
7472 if (element == EL_ROBOT ||
7473 element == EL_YAMYAM ||
7474 element == EL_DARK_YAMYAM)
7476 DrawLevelElementAnimationIfNeeded(x, y, element);
7477 PlayLevelSoundAction(x, y, ACTION_WAITING);
7479 else if (element == EL_SP_ELECTRON)
7480 DrawLevelElementAnimationIfNeeded(x, y, element);
7481 else if (element == EL_DRAGON)
7484 int dir = MovDir[x][y];
7485 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7486 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7487 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7488 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7489 dir == MV_UP ? IMG_FLAMES_1_UP :
7490 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7491 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7493 GfxAction[x][y] = ACTION_ATTACKING;
7495 if (IS_PLAYER(x, y))
7496 DrawPlayerField(x, y);
7498 DrawLevelField(x, y);
7500 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7502 for (i = 1; i <= 3; i++)
7504 int xx = x + i * dx;
7505 int yy = y + i * dy;
7506 int sx = SCREENX(xx);
7507 int sy = SCREENY(yy);
7508 int flame_graphic = graphic + (i - 1);
7510 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7515 int flamed = MovingOrBlocked2Element(xx, yy);
7519 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7521 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7522 RemoveMovingField(xx, yy);
7524 RemoveField(xx, yy);
7526 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7529 RemoveMovingField(xx, yy);
7532 ChangeDelay[xx][yy] = 0;
7534 Feld[xx][yy] = EL_FLAMES;
7536 if (IN_SCR_FIELD(sx, sy))
7538 DrawLevelFieldCrumbledSand(xx, yy);
7539 DrawGraphic(sx, sy, flame_graphic, frame);
7544 if (Feld[xx][yy] == EL_FLAMES)
7545 Feld[xx][yy] = EL_EMPTY;
7546 DrawLevelField(xx, yy);
7551 if (MovDelay[x][y]) /* element still has to wait some time */
7553 PlayLevelSoundAction(x, y, ACTION_WAITING);
7559 /* now make next step */
7561 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7563 if (DONT_COLLIDE_WITH(element) &&
7564 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7565 !PLAYER_ENEMY_PROTECTED(newx, newy))
7567 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7572 else if (CAN_MOVE_INTO_ACID(element) &&
7573 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7574 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7575 (MovDir[x][y] == MV_DOWN ||
7576 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7578 SplashAcid(newx, newy);
7579 Store[x][y] = EL_ACID;
7581 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7583 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7584 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7585 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7586 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7589 DrawLevelField(x, y);
7591 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7592 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7593 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7595 local_player->friends_still_needed--;
7596 if (!local_player->friends_still_needed &&
7597 !local_player->GameOver && AllPlayersGone)
7598 PlayerWins(local_player);
7602 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7604 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7605 DrawLevelField(newx, newy);
7607 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7609 else if (!IS_FREE(newx, newy))
7611 GfxAction[x][y] = ACTION_WAITING;
7613 if (IS_PLAYER(x, y))
7614 DrawPlayerField(x, y);
7616 DrawLevelField(x, y);
7621 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7623 if (IS_FOOD_PIG(Feld[newx][newy]))
7625 if (IS_MOVING(newx, newy))
7626 RemoveMovingField(newx, newy);
7629 Feld[newx][newy] = EL_EMPTY;
7630 DrawLevelField(newx, newy);
7633 PlayLevelSound(x, y, SND_PIG_DIGGING);
7635 else if (!IS_FREE(newx, newy))
7637 if (IS_PLAYER(x, y))
7638 DrawPlayerField(x, y);
7640 DrawLevelField(x, y);
7645 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7647 if (Store[x][y] != EL_EMPTY)
7649 boolean can_clone = FALSE;
7652 /* check if element to clone is still there */
7653 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7655 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7663 /* cannot clone or target field not free anymore -- do not clone */
7664 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7665 Store[x][y] = EL_EMPTY;
7668 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7670 if (IS_MV_DIAGONAL(MovDir[x][y]))
7672 int diagonal_move_dir = MovDir[x][y];
7673 int stored = Store[x][y];
7674 int change_delay = 8;
7677 /* android is moving diagonally */
7679 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7681 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7682 GfxElement[x][y] = EL_EMC_ANDROID;
7683 GfxAction[x][y] = ACTION_SHRINKING;
7684 GfxDir[x][y] = diagonal_move_dir;
7685 ChangeDelay[x][y] = change_delay;
7687 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7690 DrawLevelGraphicAnimation(x, y, graphic);
7691 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7693 if (Feld[newx][newy] == EL_ACID)
7695 SplashAcid(newx, newy);
7700 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7702 Store[newx][newy] = EL_EMC_ANDROID;
7703 GfxElement[newx][newy] = EL_EMC_ANDROID;
7704 GfxAction[newx][newy] = ACTION_GROWING;
7705 GfxDir[newx][newy] = diagonal_move_dir;
7706 ChangeDelay[newx][newy] = change_delay;
7708 graphic = el_act_dir2img(GfxElement[newx][newy],
7709 GfxAction[newx][newy], GfxDir[newx][newy]);
7711 DrawLevelGraphicAnimation(newx, newy, graphic);
7712 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7718 Feld[newx][newy] = EL_EMPTY;
7719 DrawLevelField(newx, newy);
7721 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7724 else if (!IS_FREE(newx, newy))
7727 if (IS_PLAYER(x, y))
7728 DrawPlayerField(x, y);
7730 DrawLevelField(x, y);
7736 else if (IS_CUSTOM_ELEMENT(element) &&
7737 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7739 int new_element = Feld[newx][newy];
7741 if (!IS_FREE(newx, newy))
7743 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7744 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7747 /* no element can dig solid indestructible elements */
7748 if (IS_INDESTRUCTIBLE(new_element) &&
7749 !IS_DIGGABLE(new_element) &&
7750 !IS_COLLECTIBLE(new_element))
7753 if (AmoebaNr[newx][newy] &&
7754 (new_element == EL_AMOEBA_FULL ||
7755 new_element == EL_BD_AMOEBA ||
7756 new_element == EL_AMOEBA_GROWING))
7758 AmoebaCnt[AmoebaNr[newx][newy]]--;
7759 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7762 if (IS_MOVING(newx, newy))
7763 RemoveMovingField(newx, newy);
7766 RemoveField(newx, newy);
7767 DrawLevelField(newx, newy);
7770 /* if digged element was about to explode, prevent the explosion */
7771 ExplodeField[newx][newy] = EX_TYPE_NONE;
7773 PlayLevelSoundAction(x, y, action);
7776 Store[newx][newy] = EL_EMPTY;
7778 /* this makes it possible to leave the removed element again */
7779 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7780 Store[newx][newy] = new_element;
7782 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7784 int move_leave_element = element_info[element].move_leave_element;
7786 /* this makes it possible to leave the removed element again */
7787 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7788 new_element : move_leave_element);
7792 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7794 RunnerVisit[x][y] = FrameCounter;
7795 PlayerVisit[x][y] /= 8; /* expire player visit path */
7798 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7800 if (!IS_FREE(newx, newy))
7802 if (IS_PLAYER(x, y))
7803 DrawPlayerField(x, y);
7805 DrawLevelField(x, y);
7811 boolean wanna_flame = !RND(10);
7812 int dx = newx - x, dy = newy - y;
7813 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7814 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7815 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7816 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7817 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7818 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7821 IS_CLASSIC_ENEMY(element1) ||
7822 IS_CLASSIC_ENEMY(element2)) &&
7823 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7824 element1 != EL_FLAMES && element2 != EL_FLAMES)
7826 ResetGfxAnimation(x, y);
7827 GfxAction[x][y] = ACTION_ATTACKING;
7829 if (IS_PLAYER(x, y))
7830 DrawPlayerField(x, y);
7832 DrawLevelField(x, y);
7834 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7836 MovDelay[x][y] = 50;
7840 RemoveField(newx, newy);
7842 Feld[newx][newy] = EL_FLAMES;
7843 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7846 RemoveField(newx1, newy1);
7848 Feld[newx1][newy1] = EL_FLAMES;
7850 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7853 RemoveField(newx2, newy2);
7855 Feld[newx2][newy2] = EL_FLAMES;
7862 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7863 Feld[newx][newy] == EL_DIAMOND)
7865 if (IS_MOVING(newx, newy))
7866 RemoveMovingField(newx, newy);
7869 Feld[newx][newy] = EL_EMPTY;
7870 DrawLevelField(newx, newy);
7873 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7875 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7876 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7878 if (AmoebaNr[newx][newy])
7880 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7881 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7882 Feld[newx][newy] == EL_BD_AMOEBA)
7883 AmoebaCnt[AmoebaNr[newx][newy]]--;
7888 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7890 RemoveMovingField(newx, newy);
7893 if (IS_MOVING(newx, newy))
7895 RemoveMovingField(newx, newy);
7900 Feld[newx][newy] = EL_EMPTY;
7901 DrawLevelField(newx, newy);
7904 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7906 else if ((element == EL_PACMAN || element == EL_MOLE)
7907 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7909 if (AmoebaNr[newx][newy])
7911 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7912 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7913 Feld[newx][newy] == EL_BD_AMOEBA)
7914 AmoebaCnt[AmoebaNr[newx][newy]]--;
7917 if (element == EL_MOLE)
7919 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7920 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7922 ResetGfxAnimation(x, y);
7923 GfxAction[x][y] = ACTION_DIGGING;
7924 DrawLevelField(x, y);
7926 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7928 return; /* wait for shrinking amoeba */
7930 else /* element == EL_PACMAN */
7932 Feld[newx][newy] = EL_EMPTY;
7933 DrawLevelField(newx, newy);
7934 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7937 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7938 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7939 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7941 /* wait for shrinking amoeba to completely disappear */
7944 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7946 /* object was running against a wall */
7951 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7952 if (move_pattern & MV_ANY_DIRECTION &&
7953 move_pattern == MovDir[x][y])
7955 int blocking_element =
7956 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7958 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7961 element = Feld[x][y]; /* element might have changed */
7965 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7966 DrawLevelElementAnimation(x, y, element);
7968 if (DONT_TOUCH(element))
7969 TestIfBadThingTouchesPlayer(x, y);
7974 InitMovingField(x, y, MovDir[x][y]);
7976 PlayLevelSoundAction(x, y, ACTION_MOVING);
7980 ContinueMoving(x, y);
7983 void ContinueMoving(int x, int y)
7985 int element = Feld[x][y];
7986 struct ElementInfo *ei = &element_info[element];
7987 int direction = MovDir[x][y];
7988 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7989 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7990 int newx = x + dx, newy = y + dy;
7991 int stored = Store[x][y];
7992 int stored_new = Store[newx][newy];
7993 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7994 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7995 boolean last_line = (newy == lev_fieldy - 1);
7997 MovPos[x][y] += getElementMoveStepsize(x, y);
7999 if (pushed_by_player) /* special case: moving object pushed by player */
8000 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8002 if (ABS(MovPos[x][y]) < TILEX)
8005 int ee = Feld[x][y];
8006 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8007 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8009 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8010 x, y, ABS(MovPos[x][y]),
8012 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8015 DrawLevelField(x, y);
8017 return; /* element is still moving */
8020 /* element reached destination field */
8022 Feld[x][y] = EL_EMPTY;
8023 Feld[newx][newy] = element;
8024 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8026 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8028 element = Feld[newx][newy] = EL_ACID;
8030 else if (element == EL_MOLE)
8032 Feld[x][y] = EL_SAND;
8034 DrawLevelFieldCrumbledSandNeighbours(x, y);
8036 else if (element == EL_QUICKSAND_FILLING)
8038 element = Feld[newx][newy] = get_next_element(element);
8039 Store[newx][newy] = Store[x][y];
8041 else if (element == EL_QUICKSAND_EMPTYING)
8043 Feld[x][y] = get_next_element(element);
8044 element = Feld[newx][newy] = Store[x][y];
8046 else if (element == EL_QUICKSAND_FAST_FILLING)
8048 element = Feld[newx][newy] = get_next_element(element);
8049 Store[newx][newy] = Store[x][y];
8051 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8053 Feld[x][y] = get_next_element(element);
8054 element = Feld[newx][newy] = Store[x][y];
8056 else if (element == EL_MAGIC_WALL_FILLING)
8058 element = Feld[newx][newy] = get_next_element(element);
8059 if (!game.magic_wall_active)
8060 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8061 Store[newx][newy] = Store[x][y];
8063 else if (element == EL_MAGIC_WALL_EMPTYING)
8065 Feld[x][y] = get_next_element(element);
8066 if (!game.magic_wall_active)
8067 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8068 element = Feld[newx][newy] = Store[x][y];
8070 #if USE_NEW_CUSTOM_VALUE
8071 InitField(newx, newy, FALSE);
8074 else if (element == EL_BD_MAGIC_WALL_FILLING)
8076 element = Feld[newx][newy] = get_next_element(element);
8077 if (!game.magic_wall_active)
8078 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8079 Store[newx][newy] = Store[x][y];
8081 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8083 Feld[x][y] = get_next_element(element);
8084 if (!game.magic_wall_active)
8085 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8086 element = Feld[newx][newy] = Store[x][y];
8088 #if USE_NEW_CUSTOM_VALUE
8089 InitField(newx, newy, FALSE);
8092 else if (element == EL_DC_MAGIC_WALL_FILLING)
8094 element = Feld[newx][newy] = get_next_element(element);
8095 if (!game.magic_wall_active)
8096 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8097 Store[newx][newy] = Store[x][y];
8099 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8101 Feld[x][y] = get_next_element(element);
8102 if (!game.magic_wall_active)
8103 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8104 element = Feld[newx][newy] = Store[x][y];
8106 #if USE_NEW_CUSTOM_VALUE
8107 InitField(newx, newy, FALSE);
8110 else if (element == EL_AMOEBA_DROPPING)
8112 Feld[x][y] = get_next_element(element);
8113 element = Feld[newx][newy] = Store[x][y];
8115 else if (element == EL_SOKOBAN_OBJECT)
8118 Feld[x][y] = Back[x][y];
8120 if (Back[newx][newy])
8121 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8123 Back[x][y] = Back[newx][newy] = 0;
8126 Store[x][y] = EL_EMPTY;
8131 MovDelay[newx][newy] = 0;
8133 if (CAN_CHANGE_OR_HAS_ACTION(element))
8135 /* copy element change control values to new field */
8136 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8137 ChangePage[newx][newy] = ChangePage[x][y];
8138 ChangeCount[newx][newy] = ChangeCount[x][y];
8139 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8142 #if USE_NEW_CUSTOM_VALUE
8143 CustomValue[newx][newy] = CustomValue[x][y];
8146 ChangeDelay[x][y] = 0;
8147 ChangePage[x][y] = -1;
8148 ChangeCount[x][y] = 0;
8149 ChangeEvent[x][y] = -1;
8151 #if USE_NEW_CUSTOM_VALUE
8152 CustomValue[x][y] = 0;
8155 /* copy animation control values to new field */
8156 GfxFrame[newx][newy] = GfxFrame[x][y];
8157 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8158 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8159 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8161 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8163 /* some elements can leave other elements behind after moving */
8165 if (ei->move_leave_element != EL_EMPTY &&
8166 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8167 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8169 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8170 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8171 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8174 int move_leave_element = ei->move_leave_element;
8178 /* this makes it possible to leave the removed element again */
8179 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8180 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8182 /* this makes it possible to leave the removed element again */
8183 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8184 move_leave_element = stored;
8187 /* this makes it possible to leave the removed element again */
8188 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8189 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8190 move_leave_element = stored;
8193 Feld[x][y] = move_leave_element;
8195 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8196 MovDir[x][y] = direction;
8198 InitField(x, y, FALSE);
8200 if (GFX_CRUMBLED(Feld[x][y]))
8201 DrawLevelFieldCrumbledSandNeighbours(x, y);
8203 if (ELEM_IS_PLAYER(move_leave_element))
8204 RelocatePlayer(x, y, move_leave_element);
8207 /* do this after checking for left-behind element */
8208 ResetGfxAnimation(x, y); /* reset animation values for old field */
8210 if (!CAN_MOVE(element) ||
8211 (CAN_FALL(element) && direction == MV_DOWN &&
8212 (element == EL_SPRING ||
8213 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8214 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8215 GfxDir[x][y] = MovDir[newx][newy] = 0;
8217 DrawLevelField(x, y);
8218 DrawLevelField(newx, newy);
8220 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8222 /* prevent pushed element from moving on in pushed direction */
8223 if (pushed_by_player && CAN_MOVE(element) &&
8224 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8225 !(element_info[element].move_pattern & direction))
8226 TurnRound(newx, newy);
8228 /* prevent elements on conveyor belt from moving on in last direction */
8229 if (pushed_by_conveyor && CAN_FALL(element) &&
8230 direction & MV_HORIZONTAL)
8231 MovDir[newx][newy] = 0;
8233 if (!pushed_by_player)
8235 int nextx = newx + dx, nexty = newy + dy;
8236 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8238 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8240 if (CAN_FALL(element) && direction == MV_DOWN)
8241 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8243 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8244 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8246 #if USE_FIX_IMPACT_COLLISION
8247 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8248 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8252 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8254 TestIfBadThingTouchesPlayer(newx, newy);
8255 TestIfBadThingTouchesFriend(newx, newy);
8257 if (!IS_CUSTOM_ELEMENT(element))
8258 TestIfBadThingTouchesOtherBadThing(newx, newy);
8260 else if (element == EL_PENGUIN)
8261 TestIfFriendTouchesBadThing(newx, newy);
8263 /* give the player one last chance (one more frame) to move away */
8264 if (CAN_FALL(element) && direction == MV_DOWN &&
8265 (last_line || (!IS_FREE(x, newy + 1) &&
8266 (!IS_PLAYER(x, newy + 1) ||
8267 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8270 if (pushed_by_player && !game.use_change_when_pushing_bug)
8272 int push_side = MV_DIR_OPPOSITE(direction);
8273 struct PlayerInfo *player = PLAYERINFO(x, y);
8275 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8276 player->index_bit, push_side);
8277 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8278 player->index_bit, push_side);
8281 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8282 MovDelay[newx][newy] = 1;
8284 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8286 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8289 if (ChangePage[newx][newy] != -1) /* delayed change */
8291 int page = ChangePage[newx][newy];
8292 struct ElementChangeInfo *change = &ei->change_page[page];
8294 ChangePage[newx][newy] = -1;
8296 if (change->can_change)
8298 if (ChangeElement(newx, newy, element, page))
8300 if (change->post_change_function)
8301 change->post_change_function(newx, newy);
8305 if (change->has_action)
8306 ExecuteCustomElementAction(newx, newy, element, page);
8310 TestIfElementHitsCustomElement(newx, newy, direction);
8311 TestIfPlayerTouchesCustomElement(newx, newy);
8312 TestIfElementTouchesCustomElement(newx, newy);
8314 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8315 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8316 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8317 MV_DIR_OPPOSITE(direction));
8320 int AmoebeNachbarNr(int ax, int ay)
8323 int element = Feld[ax][ay];
8325 static int xy[4][2] =
8333 for (i = 0; i < NUM_DIRECTIONS; i++)
8335 int x = ax + xy[i][0];
8336 int y = ay + xy[i][1];
8338 if (!IN_LEV_FIELD(x, y))
8341 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8342 group_nr = AmoebaNr[x][y];
8348 void AmoebenVereinigen(int ax, int ay)
8350 int i, x, y, xx, yy;
8351 int new_group_nr = AmoebaNr[ax][ay];
8352 static int xy[4][2] =
8360 if (new_group_nr == 0)
8363 for (i = 0; i < NUM_DIRECTIONS; i++)
8368 if (!IN_LEV_FIELD(x, y))
8371 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8372 Feld[x][y] == EL_BD_AMOEBA ||
8373 Feld[x][y] == EL_AMOEBA_DEAD) &&
8374 AmoebaNr[x][y] != new_group_nr)
8376 int old_group_nr = AmoebaNr[x][y];
8378 if (old_group_nr == 0)
8381 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8382 AmoebaCnt[old_group_nr] = 0;
8383 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8384 AmoebaCnt2[old_group_nr] = 0;
8386 SCAN_PLAYFIELD(xx, yy)
8388 if (AmoebaNr[xx][yy] == old_group_nr)
8389 AmoebaNr[xx][yy] = new_group_nr;
8395 void AmoebeUmwandeln(int ax, int ay)
8399 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8401 int group_nr = AmoebaNr[ax][ay];
8406 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8407 printf("AmoebeUmwandeln(): This should never happen!\n");
8412 SCAN_PLAYFIELD(x, y)
8414 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8417 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8421 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8422 SND_AMOEBA_TURNING_TO_GEM :
8423 SND_AMOEBA_TURNING_TO_ROCK));
8428 static int xy[4][2] =
8436 for (i = 0; i < NUM_DIRECTIONS; i++)
8441 if (!IN_LEV_FIELD(x, y))
8444 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8446 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8447 SND_AMOEBA_TURNING_TO_GEM :
8448 SND_AMOEBA_TURNING_TO_ROCK));
8455 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8458 int group_nr = AmoebaNr[ax][ay];
8459 boolean done = FALSE;
8464 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8465 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8470 SCAN_PLAYFIELD(x, y)
8472 if (AmoebaNr[x][y] == group_nr &&
8473 (Feld[x][y] == EL_AMOEBA_DEAD ||
8474 Feld[x][y] == EL_BD_AMOEBA ||
8475 Feld[x][y] == EL_AMOEBA_GROWING))
8478 Feld[x][y] = new_element;
8479 InitField(x, y, FALSE);
8480 DrawLevelField(x, y);
8486 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8487 SND_BD_AMOEBA_TURNING_TO_ROCK :
8488 SND_BD_AMOEBA_TURNING_TO_GEM));
8491 void AmoebeWaechst(int x, int y)
8493 static unsigned long sound_delay = 0;
8494 static unsigned long sound_delay_value = 0;
8496 if (!MovDelay[x][y]) /* start new growing cycle */
8500 if (DelayReached(&sound_delay, sound_delay_value))
8502 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8503 sound_delay_value = 30;
8507 if (MovDelay[x][y]) /* wait some time before growing bigger */
8510 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8512 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8513 6 - MovDelay[x][y]);
8515 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8518 if (!MovDelay[x][y])
8520 Feld[x][y] = Store[x][y];
8522 DrawLevelField(x, y);
8527 void AmoebaDisappearing(int x, int y)
8529 static unsigned long sound_delay = 0;
8530 static unsigned long sound_delay_value = 0;
8532 if (!MovDelay[x][y]) /* start new shrinking cycle */
8536 if (DelayReached(&sound_delay, sound_delay_value))
8537 sound_delay_value = 30;
8540 if (MovDelay[x][y]) /* wait some time before shrinking */
8543 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8545 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8546 6 - MovDelay[x][y]);
8548 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8551 if (!MovDelay[x][y])
8553 Feld[x][y] = EL_EMPTY;
8554 DrawLevelField(x, y);
8556 /* don't let mole enter this field in this cycle;
8557 (give priority to objects falling to this field from above) */
8563 void AmoebeAbleger(int ax, int ay)
8566 int element = Feld[ax][ay];
8567 int graphic = el2img(element);
8568 int newax = ax, neway = ay;
8569 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8570 static int xy[4][2] =
8578 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8580 Feld[ax][ay] = EL_AMOEBA_DEAD;
8581 DrawLevelField(ax, ay);
8585 if (IS_ANIMATED(graphic))
8586 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8588 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8589 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8591 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8594 if (MovDelay[ax][ay])
8598 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8601 int x = ax + xy[start][0];
8602 int y = ay + xy[start][1];
8604 if (!IN_LEV_FIELD(x, y))
8607 if (IS_FREE(x, y) ||
8608 CAN_GROW_INTO(Feld[x][y]) ||
8609 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8610 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8616 if (newax == ax && neway == ay)
8619 else /* normal or "filled" (BD style) amoeba */
8622 boolean waiting_for_player = FALSE;
8624 for (i = 0; i < NUM_DIRECTIONS; i++)
8626 int j = (start + i) % 4;
8627 int x = ax + xy[j][0];
8628 int y = ay + xy[j][1];
8630 if (!IN_LEV_FIELD(x, y))
8633 if (IS_FREE(x, y) ||
8634 CAN_GROW_INTO(Feld[x][y]) ||
8635 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8636 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8642 else if (IS_PLAYER(x, y))
8643 waiting_for_player = TRUE;
8646 if (newax == ax && neway == ay) /* amoeba cannot grow */
8648 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8650 Feld[ax][ay] = EL_AMOEBA_DEAD;
8651 DrawLevelField(ax, ay);
8652 AmoebaCnt[AmoebaNr[ax][ay]]--;
8654 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8656 if (element == EL_AMOEBA_FULL)
8657 AmoebeUmwandeln(ax, ay);
8658 else if (element == EL_BD_AMOEBA)
8659 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8664 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8666 /* amoeba gets larger by growing in some direction */
8668 int new_group_nr = AmoebaNr[ax][ay];
8671 if (new_group_nr == 0)
8673 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8674 printf("AmoebeAbleger(): This should never happen!\n");
8679 AmoebaNr[newax][neway] = new_group_nr;
8680 AmoebaCnt[new_group_nr]++;
8681 AmoebaCnt2[new_group_nr]++;
8683 /* if amoeba touches other amoeba(s) after growing, unify them */
8684 AmoebenVereinigen(newax, neway);
8686 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8688 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8694 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8695 (neway == lev_fieldy - 1 && newax != ax))
8697 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8698 Store[newax][neway] = element;
8700 else if (neway == ay || element == EL_EMC_DRIPPER)
8702 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8704 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8708 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8709 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8710 Store[ax][ay] = EL_AMOEBA_DROP;
8711 ContinueMoving(ax, ay);
8715 DrawLevelField(newax, neway);
8718 void Life(int ax, int ay)
8722 int element = Feld[ax][ay];
8723 int graphic = el2img(element);
8724 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8726 boolean changed = FALSE;
8728 if (IS_ANIMATED(graphic))
8729 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8734 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8735 MovDelay[ax][ay] = life_time;
8737 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8740 if (MovDelay[ax][ay])
8744 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8746 int xx = ax+x1, yy = ay+y1;
8749 if (!IN_LEV_FIELD(xx, yy))
8752 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8754 int x = xx+x2, y = yy+y2;
8756 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8759 if (((Feld[x][y] == element ||
8760 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8762 (IS_FREE(x, y) && Stop[x][y]))
8766 if (xx == ax && yy == ay) /* field in the middle */
8768 if (nachbarn < life_parameter[0] ||
8769 nachbarn > life_parameter[1])
8771 Feld[xx][yy] = EL_EMPTY;
8773 DrawLevelField(xx, yy);
8774 Stop[xx][yy] = TRUE;
8778 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8779 { /* free border field */
8780 if (nachbarn >= life_parameter[2] &&
8781 nachbarn <= life_parameter[3])
8783 Feld[xx][yy] = element;
8784 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8786 DrawLevelField(xx, yy);
8787 Stop[xx][yy] = TRUE;
8794 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8795 SND_GAME_OF_LIFE_GROWING);
8798 static void InitRobotWheel(int x, int y)
8800 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8803 static void RunRobotWheel(int x, int y)
8805 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8808 static void StopRobotWheel(int x, int y)
8810 if (ZX == x && ZY == y)
8814 game.robot_wheel_active = FALSE;
8818 static void InitTimegateWheel(int x, int y)
8820 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8823 static void RunTimegateWheel(int x, int y)
8825 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8828 static void InitMagicBallDelay(int x, int y)
8831 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8833 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8837 static void ActivateMagicBall(int bx, int by)
8841 if (level.ball_random)
8843 int pos_border = RND(8); /* select one of the eight border elements */
8844 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8845 int xx = pos_content % 3;
8846 int yy = pos_content / 3;
8851 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8852 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8856 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8858 int xx = x - bx + 1;
8859 int yy = y - by + 1;
8861 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8862 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8866 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8869 void CheckExit(int x, int y)
8871 if (local_player->gems_still_needed > 0 ||
8872 local_player->sokobanfields_still_needed > 0 ||
8873 local_player->lights_still_needed > 0)
8875 int element = Feld[x][y];
8876 int graphic = el2img(element);
8878 if (IS_ANIMATED(graphic))
8879 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8884 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8887 Feld[x][y] = EL_EXIT_OPENING;
8889 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8892 void CheckExitEM(int x, int y)
8894 if (local_player->gems_still_needed > 0 ||
8895 local_player->sokobanfields_still_needed > 0 ||
8896 local_player->lights_still_needed > 0)
8898 int element = Feld[x][y];
8899 int graphic = el2img(element);
8901 if (IS_ANIMATED(graphic))
8902 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8907 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8910 Feld[x][y] = EL_EM_EXIT_OPENING;
8912 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8915 void CheckExitSteel(int x, int y)
8917 if (local_player->gems_still_needed > 0 ||
8918 local_player->sokobanfields_still_needed > 0 ||
8919 local_player->lights_still_needed > 0)
8921 int element = Feld[x][y];
8922 int graphic = el2img(element);
8924 if (IS_ANIMATED(graphic))
8925 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8930 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8933 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8935 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8938 void CheckExitSteelEM(int x, int y)
8940 if (local_player->gems_still_needed > 0 ||
8941 local_player->sokobanfields_still_needed > 0 ||
8942 local_player->lights_still_needed > 0)
8944 int element = Feld[x][y];
8945 int graphic = el2img(element);
8947 if (IS_ANIMATED(graphic))
8948 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8953 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8956 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8958 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8961 void CheckExitSP(int x, int y)
8963 if (local_player->gems_still_needed > 0)
8965 int element = Feld[x][y];
8966 int graphic = el2img(element);
8968 if (IS_ANIMATED(graphic))
8969 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8974 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8977 Feld[x][y] = EL_SP_EXIT_OPENING;
8979 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8982 static void CloseAllOpenTimegates()
8986 SCAN_PLAYFIELD(x, y)
8988 int element = Feld[x][y];
8990 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8992 Feld[x][y] = EL_TIMEGATE_CLOSING;
8994 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8999 void DrawTwinkleOnField(int x, int y)
9001 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9004 if (Feld[x][y] == EL_BD_DIAMOND)
9007 if (MovDelay[x][y] == 0) /* next animation frame */
9008 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9010 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9014 if (setup.direct_draw && MovDelay[x][y])
9015 SetDrawtoField(DRAW_BUFFERED);
9017 DrawLevelElementAnimation(x, y, Feld[x][y]);
9019 if (MovDelay[x][y] != 0)
9021 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9022 10 - MovDelay[x][y]);
9024 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9026 if (setup.direct_draw)
9030 dest_x = FX + SCREENX(x) * TILEX;
9031 dest_y = FY + SCREENY(y) * TILEY;
9033 BlitBitmap(drawto_field, window,
9034 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9035 SetDrawtoField(DRAW_DIRECT);
9041 void MauerWaechst(int x, int y)
9045 if (!MovDelay[x][y]) /* next animation frame */
9046 MovDelay[x][y] = 3 * delay;
9048 if (MovDelay[x][y]) /* wait some time before next frame */
9052 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9054 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9055 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9057 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9060 if (!MovDelay[x][y])
9062 if (MovDir[x][y] == MV_LEFT)
9064 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9065 DrawLevelField(x - 1, y);
9067 else if (MovDir[x][y] == MV_RIGHT)
9069 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9070 DrawLevelField(x + 1, y);
9072 else if (MovDir[x][y] == MV_UP)
9074 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9075 DrawLevelField(x, y - 1);
9079 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9080 DrawLevelField(x, y + 1);
9083 Feld[x][y] = Store[x][y];
9085 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9086 DrawLevelField(x, y);
9091 void MauerAbleger(int ax, int ay)
9093 int element = Feld[ax][ay];
9094 int graphic = el2img(element);
9095 boolean oben_frei = FALSE, unten_frei = FALSE;
9096 boolean links_frei = FALSE, rechts_frei = FALSE;
9097 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9098 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9099 boolean new_wall = FALSE;
9101 if (IS_ANIMATED(graphic))
9102 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9104 if (!MovDelay[ax][ay]) /* start building new wall */
9105 MovDelay[ax][ay] = 6;
9107 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9110 if (MovDelay[ax][ay])
9114 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9116 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9118 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9120 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9123 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9124 element == EL_EXPANDABLE_WALL_ANY)
9128 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9129 Store[ax][ay-1] = element;
9130 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9131 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9132 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9133 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9138 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9139 Store[ax][ay+1] = element;
9140 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9141 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9142 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9143 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9148 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9149 element == EL_EXPANDABLE_WALL_ANY ||
9150 element == EL_EXPANDABLE_WALL ||
9151 element == EL_BD_EXPANDABLE_WALL)
9155 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9156 Store[ax-1][ay] = element;
9157 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9158 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9159 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9160 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9166 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9167 Store[ax+1][ay] = element;
9168 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9169 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9170 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9171 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9176 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9177 DrawLevelField(ax, ay);
9179 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9181 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9182 unten_massiv = TRUE;
9183 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9184 links_massiv = TRUE;
9185 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9186 rechts_massiv = TRUE;
9188 if (((oben_massiv && unten_massiv) ||
9189 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9190 element == EL_EXPANDABLE_WALL) &&
9191 ((links_massiv && rechts_massiv) ||
9192 element == EL_EXPANDABLE_WALL_VERTICAL))
9193 Feld[ax][ay] = EL_WALL;
9196 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9199 void MauerAblegerStahl(int ax, int ay)
9201 int element = Feld[ax][ay];
9202 int graphic = el2img(element);
9203 boolean oben_frei = FALSE, unten_frei = FALSE;
9204 boolean links_frei = FALSE, rechts_frei = FALSE;
9205 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9206 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9207 boolean new_wall = FALSE;
9209 if (IS_ANIMATED(graphic))
9210 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9212 if (!MovDelay[ax][ay]) /* start building new wall */
9213 MovDelay[ax][ay] = 6;
9215 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9218 if (MovDelay[ax][ay])
9222 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9224 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9226 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9228 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9231 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9232 element == EL_EXPANDABLE_STEELWALL_ANY)
9236 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9237 Store[ax][ay-1] = element;
9238 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9239 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9240 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9241 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9246 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9247 Store[ax][ay+1] = element;
9248 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9249 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9250 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9251 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9256 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9257 element == EL_EXPANDABLE_STEELWALL_ANY)
9261 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9262 Store[ax-1][ay] = element;
9263 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9264 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9265 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9266 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9272 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9273 Store[ax+1][ay] = element;
9274 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9275 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9276 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9277 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9282 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9284 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9285 unten_massiv = TRUE;
9286 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9287 links_massiv = TRUE;
9288 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9289 rechts_massiv = TRUE;
9291 if (((oben_massiv && unten_massiv) ||
9292 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9293 ((links_massiv && rechts_massiv) ||
9294 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9295 Feld[ax][ay] = EL_WALL;
9298 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9301 void CheckForDragon(int x, int y)
9304 boolean dragon_found = FALSE;
9305 static int xy[4][2] =
9313 for (i = 0; i < NUM_DIRECTIONS; i++)
9315 for (j = 0; j < 4; j++)
9317 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9319 if (IN_LEV_FIELD(xx, yy) &&
9320 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9322 if (Feld[xx][yy] == EL_DRAGON)
9323 dragon_found = TRUE;
9332 for (i = 0; i < NUM_DIRECTIONS; i++)
9334 for (j = 0; j < 3; j++)
9336 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9338 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9340 Feld[xx][yy] = EL_EMPTY;
9341 DrawLevelField(xx, yy);
9350 static void InitBuggyBase(int x, int y)
9352 int element = Feld[x][y];
9353 int activating_delay = FRAMES_PER_SECOND / 4;
9356 (element == EL_SP_BUGGY_BASE ?
9357 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9358 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9360 element == EL_SP_BUGGY_BASE_ACTIVE ?
9361 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9364 static void WarnBuggyBase(int x, int y)
9367 static int xy[4][2] =
9375 for (i = 0; i < NUM_DIRECTIONS; i++)
9377 int xx = x + xy[i][0];
9378 int yy = y + xy[i][1];
9380 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9382 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9389 static void InitTrap(int x, int y)
9391 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9394 static void ActivateTrap(int x, int y)
9396 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9399 static void ChangeActiveTrap(int x, int y)
9401 int graphic = IMG_TRAP_ACTIVE;
9403 /* if new animation frame was drawn, correct crumbled sand border */
9404 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9405 DrawLevelFieldCrumbledSand(x, y);
9408 static int getSpecialActionElement(int element, int number, int base_element)
9410 return (element != EL_EMPTY ? element :
9411 number != -1 ? base_element + number - 1 :
9415 static int getModifiedActionNumber(int value_old, int operator, int operand,
9416 int value_min, int value_max)
9418 int value_new = (operator == CA_MODE_SET ? operand :
9419 operator == CA_MODE_ADD ? value_old + operand :
9420 operator == CA_MODE_SUBTRACT ? value_old - operand :
9421 operator == CA_MODE_MULTIPLY ? value_old * operand :
9422 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9423 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9426 return (value_new < value_min ? value_min :
9427 value_new > value_max ? value_max :
9431 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9433 struct ElementInfo *ei = &element_info[element];
9434 struct ElementChangeInfo *change = &ei->change_page[page];
9435 int target_element = change->target_element;
9436 int action_type = change->action_type;
9437 int action_mode = change->action_mode;
9438 int action_arg = change->action_arg;
9441 if (!change->has_action)
9444 /* ---------- determine action paramater values -------------------------- */
9446 int level_time_value =
9447 (level.time > 0 ? TimeLeft :
9450 int action_arg_element =
9451 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9452 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9453 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9456 int action_arg_direction =
9457 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9458 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9459 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9460 change->actual_trigger_side :
9461 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9462 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9465 int action_arg_number_min =
9466 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9469 int action_arg_number_max =
9470 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9471 action_type == CA_SET_LEVEL_GEMS ? 999 :
9472 action_type == CA_SET_LEVEL_TIME ? 9999 :
9473 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9474 action_type == CA_SET_CE_VALUE ? 9999 :
9475 action_type == CA_SET_CE_SCORE ? 9999 :
9478 int action_arg_number_reset =
9479 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9480 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9481 action_type == CA_SET_LEVEL_TIME ? level.time :
9482 action_type == CA_SET_LEVEL_SCORE ? 0 :
9483 #if USE_NEW_CUSTOM_VALUE
9484 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9486 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9488 action_type == CA_SET_CE_SCORE ? 0 :
9491 int action_arg_number =
9492 (action_arg <= CA_ARG_MAX ? action_arg :
9493 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9494 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9495 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9496 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9497 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9498 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9499 #if USE_NEW_CUSTOM_VALUE
9500 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9502 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9504 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9505 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9506 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9507 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9508 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9509 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9510 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9511 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9512 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9513 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9514 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9517 int action_arg_number_old =
9518 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9519 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9520 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9521 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9522 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9525 int action_arg_number_new =
9526 getModifiedActionNumber(action_arg_number_old,
9527 action_mode, action_arg_number,
9528 action_arg_number_min, action_arg_number_max);
9530 int trigger_player_bits =
9531 (change->actual_trigger_player >= EL_PLAYER_1 &&
9532 change->actual_trigger_player <= EL_PLAYER_4 ?
9533 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9536 int action_arg_player_bits =
9537 (action_arg >= CA_ARG_PLAYER_1 &&
9538 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9539 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9542 /* ---------- execute action -------------------------------------------- */
9544 switch (action_type)
9551 /* ---------- level actions ------------------------------------------- */
9553 case CA_RESTART_LEVEL:
9555 game.restart_level = TRUE;
9560 case CA_SHOW_ENVELOPE:
9562 int element = getSpecialActionElement(action_arg_element,
9563 action_arg_number, EL_ENVELOPE_1);
9565 if (IS_ENVELOPE(element))
9566 local_player->show_envelope = element;
9571 case CA_SET_LEVEL_TIME:
9573 if (level.time > 0) /* only modify limited time value */
9575 TimeLeft = action_arg_number_new;
9578 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9580 DisplayGameControlValues();
9582 DrawGameValue_Time(TimeLeft);
9585 if (!TimeLeft && setup.time_limit)
9586 for (i = 0; i < MAX_PLAYERS; i++)
9587 KillPlayer(&stored_player[i]);
9593 case CA_SET_LEVEL_SCORE:
9595 local_player->score = action_arg_number_new;
9598 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9600 DisplayGameControlValues();
9602 DrawGameValue_Score(local_player->score);
9608 case CA_SET_LEVEL_GEMS:
9610 local_player->gems_still_needed = action_arg_number_new;
9613 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9615 DisplayGameControlValues();
9617 DrawGameValue_Emeralds(local_player->gems_still_needed);
9623 #if !USE_PLAYER_GRAVITY
9624 case CA_SET_LEVEL_GRAVITY:
9626 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9627 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9628 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9634 case CA_SET_LEVEL_WIND:
9636 game.wind_direction = action_arg_direction;
9641 /* ---------- player actions ------------------------------------------ */
9643 case CA_MOVE_PLAYER:
9645 /* automatically move to the next field in specified direction */
9646 for (i = 0; i < MAX_PLAYERS; i++)
9647 if (trigger_player_bits & (1 << i))
9648 stored_player[i].programmed_action = action_arg_direction;
9653 case CA_EXIT_PLAYER:
9655 for (i = 0; i < MAX_PLAYERS; i++)
9656 if (action_arg_player_bits & (1 << i))
9657 PlayerWins(&stored_player[i]);
9662 case CA_KILL_PLAYER:
9664 for (i = 0; i < MAX_PLAYERS; i++)
9665 if (action_arg_player_bits & (1 << i))
9666 KillPlayer(&stored_player[i]);
9671 case CA_SET_PLAYER_KEYS:
9673 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9674 int element = getSpecialActionElement(action_arg_element,
9675 action_arg_number, EL_KEY_1);
9677 if (IS_KEY(element))
9679 for (i = 0; i < MAX_PLAYERS; i++)
9681 if (trigger_player_bits & (1 << i))
9683 stored_player[i].key[KEY_NR(element)] = key_state;
9685 DrawGameDoorValues();
9693 case CA_SET_PLAYER_SPEED:
9695 for (i = 0; i < MAX_PLAYERS; i++)
9697 if (trigger_player_bits & (1 << i))
9699 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9701 if (action_arg == CA_ARG_SPEED_FASTER &&
9702 stored_player[i].cannot_move)
9704 action_arg_number = STEPSIZE_VERY_SLOW;
9706 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9707 action_arg == CA_ARG_SPEED_FASTER)
9709 action_arg_number = 2;
9710 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9713 else if (action_arg == CA_ARG_NUMBER_RESET)
9715 action_arg_number = level.initial_player_stepsize[i];
9719 getModifiedActionNumber(move_stepsize,
9722 action_arg_number_min,
9723 action_arg_number_max);
9725 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9732 case CA_SET_PLAYER_SHIELD:
9734 for (i = 0; i < MAX_PLAYERS; i++)
9736 if (trigger_player_bits & (1 << i))
9738 if (action_arg == CA_ARG_SHIELD_OFF)
9740 stored_player[i].shield_normal_time_left = 0;
9741 stored_player[i].shield_deadly_time_left = 0;
9743 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9745 stored_player[i].shield_normal_time_left = 999999;
9747 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9749 stored_player[i].shield_normal_time_left = 999999;
9750 stored_player[i].shield_deadly_time_left = 999999;
9758 #if USE_PLAYER_GRAVITY
9759 case CA_SET_PLAYER_GRAVITY:
9761 for (i = 0; i < MAX_PLAYERS; i++)
9763 if (trigger_player_bits & (1 << i))
9765 stored_player[i].gravity =
9766 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9767 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9768 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9769 stored_player[i].gravity);
9777 case CA_SET_PLAYER_ARTWORK:
9779 for (i = 0; i < MAX_PLAYERS; i++)
9781 if (trigger_player_bits & (1 << i))
9783 int artwork_element = action_arg_element;
9785 if (action_arg == CA_ARG_ELEMENT_RESET)
9787 (level.use_artwork_element[i] ? level.artwork_element[i] :
9788 stored_player[i].element_nr);
9790 #if USE_GFX_RESET_PLAYER_ARTWORK
9791 if (stored_player[i].artwork_element != artwork_element)
9792 stored_player[i].Frame = 0;
9795 stored_player[i].artwork_element = artwork_element;
9797 SetPlayerWaiting(&stored_player[i], FALSE);
9799 /* set number of special actions for bored and sleeping animation */
9800 stored_player[i].num_special_action_bored =
9801 get_num_special_action(artwork_element,
9802 ACTION_BORING_1, ACTION_BORING_LAST);
9803 stored_player[i].num_special_action_sleeping =
9804 get_num_special_action(artwork_element,
9805 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9812 /* ---------- CE actions ---------------------------------------------- */
9814 case CA_SET_CE_VALUE:
9816 #if USE_NEW_CUSTOM_VALUE
9817 int last_ce_value = CustomValue[x][y];
9819 CustomValue[x][y] = action_arg_number_new;
9821 if (CustomValue[x][y] != last_ce_value)
9823 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9824 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9826 if (CustomValue[x][y] == 0)
9828 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9829 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9837 case CA_SET_CE_SCORE:
9839 #if USE_NEW_CUSTOM_VALUE
9840 int last_ce_score = ei->collect_score;
9842 ei->collect_score = action_arg_number_new;
9844 if (ei->collect_score != last_ce_score)
9846 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9847 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9849 if (ei->collect_score == 0)
9853 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9854 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9857 This is a very special case that seems to be a mixture between
9858 CheckElementChange() and CheckTriggeredElementChange(): while
9859 the first one only affects single elements that are triggered
9860 directly, the second one affects multiple elements in the playfield
9861 that are triggered indirectly by another element. This is a third
9862 case: Changing the CE score always affects multiple identical CEs,
9863 so every affected CE must be checked, not only the single CE for
9864 which the CE score was changed in the first place (as every instance
9865 of that CE shares the same CE score, and therefore also can change)!
9867 SCAN_PLAYFIELD(xx, yy)
9869 if (Feld[xx][yy] == element)
9870 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9871 CE_SCORE_GETS_ZERO);
9880 /* ---------- engine actions ------------------------------------------ */
9882 case CA_SET_ENGINE_SCAN_MODE:
9884 InitPlayfieldScanMode(action_arg);
9894 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9896 int old_element = Feld[x][y];
9897 int new_element = GetElementFromGroupElement(element);
9898 int previous_move_direction = MovDir[x][y];
9899 #if USE_NEW_CUSTOM_VALUE
9900 int last_ce_value = CustomValue[x][y];
9902 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9903 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9904 boolean add_player_onto_element = (new_element_is_player &&
9905 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9906 /* this breaks SnakeBite when a snake is
9907 halfway through a door that closes */
9908 /* NOW FIXED AT LEVEL INIT IN files.c */
9909 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9911 IS_WALKABLE(old_element));
9914 /* check if element under the player changes from accessible to unaccessible
9915 (needed for special case of dropping element which then changes) */
9916 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9917 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9925 if (!add_player_onto_element)
9927 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9928 RemoveMovingField(x, y);
9932 Feld[x][y] = new_element;
9934 #if !USE_GFX_RESET_GFX_ANIMATION
9935 ResetGfxAnimation(x, y);
9936 ResetRandomAnimationValue(x, y);
9939 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9940 MovDir[x][y] = previous_move_direction;
9942 #if USE_NEW_CUSTOM_VALUE
9943 if (element_info[new_element].use_last_ce_value)
9944 CustomValue[x][y] = last_ce_value;
9947 InitField_WithBug1(x, y, FALSE);
9949 new_element = Feld[x][y]; /* element may have changed */
9951 #if USE_GFX_RESET_GFX_ANIMATION
9952 ResetGfxAnimation(x, y);
9953 ResetRandomAnimationValue(x, y);
9956 DrawLevelField(x, y);
9958 if (GFX_CRUMBLED(new_element))
9959 DrawLevelFieldCrumbledSandNeighbours(x, y);
9963 /* check if element under the player changes from accessible to unaccessible
9964 (needed for special case of dropping element which then changes) */
9965 /* (must be checked after creating new element for walkable group elements) */
9966 #if USE_FIX_KILLED_BY_NON_WALKABLE
9967 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9968 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9975 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9976 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9985 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9986 if (new_element_is_player)
9987 RelocatePlayer(x, y, new_element);
9990 ChangeCount[x][y]++; /* count number of changes in the same frame */
9992 TestIfBadThingTouchesPlayer(x, y);
9993 TestIfPlayerTouchesCustomElement(x, y);
9994 TestIfElementTouchesCustomElement(x, y);
9997 static void CreateField(int x, int y, int element)
9999 CreateFieldExt(x, y, element, FALSE);
10002 static void CreateElementFromChange(int x, int y, int element)
10004 element = GET_VALID_RUNTIME_ELEMENT(element);
10006 #if USE_STOP_CHANGED_ELEMENTS
10007 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10009 int old_element = Feld[x][y];
10011 /* prevent changed element from moving in same engine frame
10012 unless both old and new element can either fall or move */
10013 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10014 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10019 CreateFieldExt(x, y, element, TRUE);
10022 static boolean ChangeElement(int x, int y, int element, int page)
10024 struct ElementInfo *ei = &element_info[element];
10025 struct ElementChangeInfo *change = &ei->change_page[page];
10026 int ce_value = CustomValue[x][y];
10027 int ce_score = ei->collect_score;
10028 int target_element;
10029 int old_element = Feld[x][y];
10031 /* always use default change event to prevent running into a loop */
10032 if (ChangeEvent[x][y] == -1)
10033 ChangeEvent[x][y] = CE_DELAY;
10035 if (ChangeEvent[x][y] == CE_DELAY)
10037 /* reset actual trigger element, trigger player and action element */
10038 change->actual_trigger_element = EL_EMPTY;
10039 change->actual_trigger_player = EL_PLAYER_1;
10040 change->actual_trigger_side = CH_SIDE_NONE;
10041 change->actual_trigger_ce_value = 0;
10042 change->actual_trigger_ce_score = 0;
10045 /* do not change elements more than a specified maximum number of changes */
10046 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10049 ChangeCount[x][y]++; /* count number of changes in the same frame */
10051 if (change->explode)
10058 if (change->use_target_content)
10060 boolean complete_replace = TRUE;
10061 boolean can_replace[3][3];
10064 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10067 boolean is_walkable;
10068 boolean is_diggable;
10069 boolean is_collectible;
10070 boolean is_removable;
10071 boolean is_destructible;
10072 int ex = x + xx - 1;
10073 int ey = y + yy - 1;
10074 int content_element = change->target_content.e[xx][yy];
10077 can_replace[xx][yy] = TRUE;
10079 if (ex == x && ey == y) /* do not check changing element itself */
10082 if (content_element == EL_EMPTY_SPACE)
10084 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10089 if (!IN_LEV_FIELD(ex, ey))
10091 can_replace[xx][yy] = FALSE;
10092 complete_replace = FALSE;
10099 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10100 e = MovingOrBlocked2Element(ex, ey);
10102 is_empty = (IS_FREE(ex, ey) ||
10103 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10105 is_walkable = (is_empty || IS_WALKABLE(e));
10106 is_diggable = (is_empty || IS_DIGGABLE(e));
10107 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10108 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10109 is_removable = (is_diggable || is_collectible);
10111 can_replace[xx][yy] =
10112 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10113 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10114 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10115 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10116 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10117 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10118 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10120 if (!can_replace[xx][yy])
10121 complete_replace = FALSE;
10124 if (!change->only_if_complete || complete_replace)
10126 boolean something_has_changed = FALSE;
10128 if (change->only_if_complete && change->use_random_replace &&
10129 RND(100) < change->random_percentage)
10132 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10134 int ex = x + xx - 1;
10135 int ey = y + yy - 1;
10136 int content_element;
10138 if (can_replace[xx][yy] && (!change->use_random_replace ||
10139 RND(100) < change->random_percentage))
10141 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10142 RemoveMovingField(ex, ey);
10144 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10146 content_element = change->target_content.e[xx][yy];
10147 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10148 ce_value, ce_score);
10150 CreateElementFromChange(ex, ey, target_element);
10152 something_has_changed = TRUE;
10154 /* for symmetry reasons, freeze newly created border elements */
10155 if (ex != x || ey != y)
10156 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10160 if (something_has_changed)
10162 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10163 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10169 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10170 ce_value, ce_score);
10172 if (element == EL_DIAGONAL_GROWING ||
10173 element == EL_DIAGONAL_SHRINKING)
10175 target_element = Store[x][y];
10177 Store[x][y] = EL_EMPTY;
10180 CreateElementFromChange(x, y, target_element);
10182 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10183 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10186 /* this uses direct change before indirect change */
10187 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10192 #if USE_NEW_DELAYED_ACTION
10194 static void HandleElementChange(int x, int y, int page)
10196 int element = MovingOrBlocked2Element(x, y);
10197 struct ElementInfo *ei = &element_info[element];
10198 struct ElementChangeInfo *change = &ei->change_page[page];
10201 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10202 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10205 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10206 x, y, element, element_info[element].token_name);
10207 printf("HandleElementChange(): This should never happen!\n");
10212 /* this can happen with classic bombs on walkable, changing elements */
10213 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10216 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10217 ChangeDelay[x][y] = 0;
10223 if (ChangeDelay[x][y] == 0) /* initialize element change */
10225 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10227 if (change->can_change)
10230 /* !!! not clear why graphic animation should be reset at all here !!! */
10231 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10232 #if USE_GFX_RESET_WHEN_NOT_MOVING
10233 /* when a custom element is about to change (for example by change delay),
10234 do not reset graphic animation when the custom element is moving */
10235 if (!IS_MOVING(x, y))
10238 ResetGfxAnimation(x, y);
10239 ResetRandomAnimationValue(x, y);
10243 if (change->pre_change_function)
10244 change->pre_change_function(x, y);
10248 ChangeDelay[x][y]--;
10250 if (ChangeDelay[x][y] != 0) /* continue element change */
10252 if (change->can_change)
10254 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10256 if (IS_ANIMATED(graphic))
10257 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10259 if (change->change_function)
10260 change->change_function(x, y);
10263 else /* finish element change */
10265 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10267 page = ChangePage[x][y];
10268 ChangePage[x][y] = -1;
10270 change = &ei->change_page[page];
10273 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10275 ChangeDelay[x][y] = 1; /* try change after next move step */
10276 ChangePage[x][y] = page; /* remember page to use for change */
10281 if (change->can_change)
10283 if (ChangeElement(x, y, element, page))
10285 if (change->post_change_function)
10286 change->post_change_function(x, y);
10290 if (change->has_action)
10291 ExecuteCustomElementAction(x, y, element, page);
10297 static void HandleElementChange(int x, int y, int page)
10299 int element = MovingOrBlocked2Element(x, y);
10300 struct ElementInfo *ei = &element_info[element];
10301 struct ElementChangeInfo *change = &ei->change_page[page];
10304 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10307 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10308 x, y, element, element_info[element].token_name);
10309 printf("HandleElementChange(): This should never happen!\n");
10314 /* this can happen with classic bombs on walkable, changing elements */
10315 if (!CAN_CHANGE(element))
10318 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10319 ChangeDelay[x][y] = 0;
10325 if (ChangeDelay[x][y] == 0) /* initialize element change */
10327 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10329 ResetGfxAnimation(x, y);
10330 ResetRandomAnimationValue(x, y);
10332 if (change->pre_change_function)
10333 change->pre_change_function(x, y);
10336 ChangeDelay[x][y]--;
10338 if (ChangeDelay[x][y] != 0) /* continue element change */
10340 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10342 if (IS_ANIMATED(graphic))
10343 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10345 if (change->change_function)
10346 change->change_function(x, y);
10348 else /* finish element change */
10350 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10352 page = ChangePage[x][y];
10353 ChangePage[x][y] = -1;
10355 change = &ei->change_page[page];
10358 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10360 ChangeDelay[x][y] = 1; /* try change after next move step */
10361 ChangePage[x][y] = page; /* remember page to use for change */
10366 if (ChangeElement(x, y, element, page))
10368 if (change->post_change_function)
10369 change->post_change_function(x, y);
10376 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10377 int trigger_element,
10379 int trigger_player,
10383 boolean change_done_any = FALSE;
10384 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10387 if (!(trigger_events[trigger_element][trigger_event]))
10391 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10392 trigger_event, recursion_loop_depth, recursion_loop_detected,
10393 recursion_loop_element, EL_NAME(recursion_loop_element));
10396 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10398 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10400 int element = EL_CUSTOM_START + i;
10401 boolean change_done = FALSE;
10404 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10405 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10408 for (p = 0; p < element_info[element].num_change_pages; p++)
10410 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10412 if (change->can_change_or_has_action &&
10413 change->has_event[trigger_event] &&
10414 change->trigger_side & trigger_side &&
10415 change->trigger_player & trigger_player &&
10416 change->trigger_page & trigger_page_bits &&
10417 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10419 change->actual_trigger_element = trigger_element;
10420 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10421 change->actual_trigger_side = trigger_side;
10422 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10423 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10425 if ((change->can_change && !change_done) || change->has_action)
10429 SCAN_PLAYFIELD(x, y)
10431 if (Feld[x][y] == element)
10433 if (change->can_change && !change_done)
10435 ChangeDelay[x][y] = 1;
10436 ChangeEvent[x][y] = trigger_event;
10438 HandleElementChange(x, y, p);
10440 #if USE_NEW_DELAYED_ACTION
10441 else if (change->has_action)
10443 ExecuteCustomElementAction(x, y, element, p);
10444 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10447 if (change->has_action)
10449 ExecuteCustomElementAction(x, y, element, p);
10450 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10456 if (change->can_change)
10458 change_done = TRUE;
10459 change_done_any = TRUE;
10466 RECURSION_LOOP_DETECTION_END();
10468 return change_done_any;
10471 static boolean CheckElementChangeExt(int x, int y,
10473 int trigger_element,
10475 int trigger_player,
10478 boolean change_done = FALSE;
10481 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10482 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10485 if (Feld[x][y] == EL_BLOCKED)
10487 Blocked2Moving(x, y, &x, &y);
10488 element = Feld[x][y];
10492 /* check if element has already changed */
10493 if (Feld[x][y] != element)
10496 /* check if element has already changed or is about to change after moving */
10497 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10498 Feld[x][y] != element) ||
10500 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10501 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10502 ChangePage[x][y] != -1)))
10507 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10508 trigger_event, recursion_loop_depth, recursion_loop_detected,
10509 recursion_loop_element, EL_NAME(recursion_loop_element));
10512 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10514 for (p = 0; p < element_info[element].num_change_pages; p++)
10516 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10518 /* check trigger element for all events where the element that is checked
10519 for changing interacts with a directly adjacent element -- this is
10520 different to element changes that affect other elements to change on the
10521 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10522 boolean check_trigger_element =
10523 (trigger_event == CE_TOUCHING_X ||
10524 trigger_event == CE_HITTING_X ||
10525 trigger_event == CE_HIT_BY_X ||
10527 /* this one was forgotten until 3.2.3 */
10528 trigger_event == CE_DIGGING_X);
10531 if (change->can_change_or_has_action &&
10532 change->has_event[trigger_event] &&
10533 change->trigger_side & trigger_side &&
10534 change->trigger_player & trigger_player &&
10535 (!check_trigger_element ||
10536 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10538 change->actual_trigger_element = trigger_element;
10539 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10540 change->actual_trigger_side = trigger_side;
10541 change->actual_trigger_ce_value = CustomValue[x][y];
10542 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10544 /* special case: trigger element not at (x,y) position for some events */
10545 if (check_trigger_element)
10557 { 0, 0 }, { 0, 0 }, { 0, 0 },
10561 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10562 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10564 change->actual_trigger_ce_value = CustomValue[xx][yy];
10565 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10568 if (change->can_change && !change_done)
10570 ChangeDelay[x][y] = 1;
10571 ChangeEvent[x][y] = trigger_event;
10573 HandleElementChange(x, y, p);
10575 change_done = TRUE;
10577 #if USE_NEW_DELAYED_ACTION
10578 else if (change->has_action)
10580 ExecuteCustomElementAction(x, y, element, p);
10581 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10584 if (change->has_action)
10586 ExecuteCustomElementAction(x, y, element, p);
10587 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10593 RECURSION_LOOP_DETECTION_END();
10595 return change_done;
10598 static void PlayPlayerSound(struct PlayerInfo *player)
10600 int jx = player->jx, jy = player->jy;
10601 int sound_element = player->artwork_element;
10602 int last_action = player->last_action_waiting;
10603 int action = player->action_waiting;
10605 if (player->is_waiting)
10607 if (action != last_action)
10608 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10610 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10614 if (action != last_action)
10615 StopSound(element_info[sound_element].sound[last_action]);
10617 if (last_action == ACTION_SLEEPING)
10618 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10622 static void PlayAllPlayersSound()
10626 for (i = 0; i < MAX_PLAYERS; i++)
10627 if (stored_player[i].active)
10628 PlayPlayerSound(&stored_player[i]);
10631 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10633 boolean last_waiting = player->is_waiting;
10634 int move_dir = player->MovDir;
10636 player->dir_waiting = move_dir;
10637 player->last_action_waiting = player->action_waiting;
10641 if (!last_waiting) /* not waiting -> waiting */
10643 player->is_waiting = TRUE;
10645 player->frame_counter_bored =
10647 game.player_boring_delay_fixed +
10648 GetSimpleRandom(game.player_boring_delay_random);
10649 player->frame_counter_sleeping =
10651 game.player_sleeping_delay_fixed +
10652 GetSimpleRandom(game.player_sleeping_delay_random);
10654 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10657 if (game.player_sleeping_delay_fixed +
10658 game.player_sleeping_delay_random > 0 &&
10659 player->anim_delay_counter == 0 &&
10660 player->post_delay_counter == 0 &&
10661 FrameCounter >= player->frame_counter_sleeping)
10662 player->is_sleeping = TRUE;
10663 else if (game.player_boring_delay_fixed +
10664 game.player_boring_delay_random > 0 &&
10665 FrameCounter >= player->frame_counter_bored)
10666 player->is_bored = TRUE;
10668 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10669 player->is_bored ? ACTION_BORING :
10672 if (player->is_sleeping && player->use_murphy)
10674 /* special case for sleeping Murphy when leaning against non-free tile */
10676 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10677 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10678 !IS_MOVING(player->jx - 1, player->jy)))
10679 move_dir = MV_LEFT;
10680 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10681 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10682 !IS_MOVING(player->jx + 1, player->jy)))
10683 move_dir = MV_RIGHT;
10685 player->is_sleeping = FALSE;
10687 player->dir_waiting = move_dir;
10690 if (player->is_sleeping)
10692 if (player->num_special_action_sleeping > 0)
10694 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10696 int last_special_action = player->special_action_sleeping;
10697 int num_special_action = player->num_special_action_sleeping;
10698 int special_action =
10699 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10700 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10701 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10702 last_special_action + 1 : ACTION_SLEEPING);
10703 int special_graphic =
10704 el_act_dir2img(player->artwork_element, special_action, move_dir);
10706 player->anim_delay_counter =
10707 graphic_info[special_graphic].anim_delay_fixed +
10708 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10709 player->post_delay_counter =
10710 graphic_info[special_graphic].post_delay_fixed +
10711 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10713 player->special_action_sleeping = special_action;
10716 if (player->anim_delay_counter > 0)
10718 player->action_waiting = player->special_action_sleeping;
10719 player->anim_delay_counter--;
10721 else if (player->post_delay_counter > 0)
10723 player->post_delay_counter--;
10727 else if (player->is_bored)
10729 if (player->num_special_action_bored > 0)
10731 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10733 int special_action =
10734 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10735 int special_graphic =
10736 el_act_dir2img(player->artwork_element, special_action, move_dir);
10738 player->anim_delay_counter =
10739 graphic_info[special_graphic].anim_delay_fixed +
10740 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10741 player->post_delay_counter =
10742 graphic_info[special_graphic].post_delay_fixed +
10743 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10745 player->special_action_bored = special_action;
10748 if (player->anim_delay_counter > 0)
10750 player->action_waiting = player->special_action_bored;
10751 player->anim_delay_counter--;
10753 else if (player->post_delay_counter > 0)
10755 player->post_delay_counter--;
10760 else if (last_waiting) /* waiting -> not waiting */
10762 player->is_waiting = FALSE;
10763 player->is_bored = FALSE;
10764 player->is_sleeping = FALSE;
10766 player->frame_counter_bored = -1;
10767 player->frame_counter_sleeping = -1;
10769 player->anim_delay_counter = 0;
10770 player->post_delay_counter = 0;
10772 player->dir_waiting = player->MovDir;
10773 player->action_waiting = ACTION_DEFAULT;
10775 player->special_action_bored = ACTION_DEFAULT;
10776 player->special_action_sleeping = ACTION_DEFAULT;
10780 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10782 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10783 int left = player_action & JOY_LEFT;
10784 int right = player_action & JOY_RIGHT;
10785 int up = player_action & JOY_UP;
10786 int down = player_action & JOY_DOWN;
10787 int button1 = player_action & JOY_BUTTON_1;
10788 int button2 = player_action & JOY_BUTTON_2;
10789 int dx = (left ? -1 : right ? 1 : 0);
10790 int dy = (up ? -1 : down ? 1 : 0);
10792 if (!player->active || tape.pausing)
10798 snapped = SnapField(player, dx, dy);
10802 dropped = DropElement(player);
10804 moved = MovePlayer(player, dx, dy);
10807 if (tape.single_step && tape.recording && !tape.pausing)
10809 if (button1 || (dropped && !moved))
10811 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10812 SnapField(player, 0, 0); /* stop snapping */
10816 SetPlayerWaiting(player, FALSE);
10818 return player_action;
10822 /* no actions for this player (no input at player's configured device) */
10824 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10825 SnapField(player, 0, 0);
10826 CheckGravityMovementWhenNotMoving(player);
10828 if (player->MovPos == 0)
10829 SetPlayerWaiting(player, TRUE);
10831 if (player->MovPos == 0) /* needed for tape.playing */
10832 player->is_moving = FALSE;
10834 player->is_dropping = FALSE;
10835 player->is_dropping_pressed = FALSE;
10836 player->drop_pressed_delay = 0;
10842 static void CheckLevelTime()
10846 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10848 if (level.native_em_level->lev->home == 0) /* all players at home */
10850 PlayerWins(local_player);
10852 AllPlayersGone = TRUE;
10854 level.native_em_level->lev->home = -1;
10857 if (level.native_em_level->ply[0]->alive == 0 &&
10858 level.native_em_level->ply[1]->alive == 0 &&
10859 level.native_em_level->ply[2]->alive == 0 &&
10860 level.native_em_level->ply[3]->alive == 0) /* all dead */
10861 AllPlayersGone = TRUE;
10864 if (TimeFrames >= FRAMES_PER_SECOND)
10869 for (i = 0; i < MAX_PLAYERS; i++)
10871 struct PlayerInfo *player = &stored_player[i];
10873 if (SHIELD_ON(player))
10875 player->shield_normal_time_left--;
10877 if (player->shield_deadly_time_left > 0)
10878 player->shield_deadly_time_left--;
10882 if (!local_player->LevelSolved && !level.use_step_counter)
10890 if (TimeLeft <= 10 && setup.time_limit)
10891 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10894 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10896 DisplayGameControlValues();
10898 DrawGameValue_Time(TimeLeft);
10901 if (!TimeLeft && setup.time_limit)
10903 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10904 level.native_em_level->lev->killed_out_of_time = TRUE;
10906 for (i = 0; i < MAX_PLAYERS; i++)
10907 KillPlayer(&stored_player[i]);
10911 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10913 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10915 DisplayGameControlValues();
10918 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10919 DrawGameValue_Time(TimePlayed);
10922 level.native_em_level->lev->time =
10923 (level.time == 0 ? TimePlayed : TimeLeft);
10926 if (tape.recording || tape.playing)
10927 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10930 UpdateGameDoorValues();
10931 DrawGameDoorValues();
10934 void AdvanceFrameAndPlayerCounters(int player_nr)
10938 /* advance frame counters (global frame counter and time frame counter) */
10942 /* advance player counters (counters for move delay, move animation etc.) */
10943 for (i = 0; i < MAX_PLAYERS; i++)
10945 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10946 int move_delay_value = stored_player[i].move_delay_value;
10947 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10949 if (!advance_player_counters) /* not all players may be affected */
10952 #if USE_NEW_PLAYER_ANIM
10953 if (move_frames == 0) /* less than one move per game frame */
10955 int stepsize = TILEX / move_delay_value;
10956 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10957 int count = (stored_player[i].is_moving ?
10958 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10960 if (count % delay == 0)
10965 stored_player[i].Frame += move_frames;
10967 if (stored_player[i].MovPos != 0)
10968 stored_player[i].StepFrame += move_frames;
10970 if (stored_player[i].move_delay > 0)
10971 stored_player[i].move_delay--;
10973 /* due to bugs in previous versions, counter must count up, not down */
10974 if (stored_player[i].push_delay != -1)
10975 stored_player[i].push_delay++;
10977 if (stored_player[i].drop_delay > 0)
10978 stored_player[i].drop_delay--;
10980 if (stored_player[i].is_dropping_pressed)
10981 stored_player[i].drop_pressed_delay++;
10985 void StartGameActions(boolean init_network_game, boolean record_tape,
10988 unsigned long new_random_seed = InitRND(random_seed);
10991 TapeStartRecording(new_random_seed);
10993 #if defined(NETWORK_AVALIABLE)
10994 if (init_network_game)
10996 SendToServer_StartPlaying();
11007 static unsigned long game_frame_delay = 0;
11008 unsigned long game_frame_delay_value;
11009 byte *recorded_player_action;
11010 byte summarized_player_action = 0;
11011 byte tape_action[MAX_PLAYERS];
11014 /* detect endless loops, caused by custom element programming */
11015 if (recursion_loop_detected && recursion_loop_depth == 0)
11017 char *message = getStringCat3("Internal Error ! Element ",
11018 EL_NAME(recursion_loop_element),
11019 " caused endless loop ! Quit the game ?");
11021 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11022 EL_NAME(recursion_loop_element));
11024 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11026 recursion_loop_detected = FALSE; /* if game should be continued */
11033 if (game.restart_level)
11034 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11036 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11038 if (level.native_em_level->lev->home == 0) /* all players at home */
11040 PlayerWins(local_player);
11042 AllPlayersGone = TRUE;
11044 level.native_em_level->lev->home = -1;
11047 if (level.native_em_level->ply[0]->alive == 0 &&
11048 level.native_em_level->ply[1]->alive == 0 &&
11049 level.native_em_level->ply[2]->alive == 0 &&
11050 level.native_em_level->ply[3]->alive == 0) /* all dead */
11051 AllPlayersGone = TRUE;
11054 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11057 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11060 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11063 game_frame_delay_value =
11064 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11066 if (tape.playing && tape.warp_forward && !tape.pausing)
11067 game_frame_delay_value = 0;
11069 /* ---------- main game synchronization point ---------- */
11071 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11073 if (network_playing && !network_player_action_received)
11075 /* try to get network player actions in time */
11077 #if defined(NETWORK_AVALIABLE)
11078 /* last chance to get network player actions without main loop delay */
11079 HandleNetworking();
11082 /* game was quit by network peer */
11083 if (game_status != GAME_MODE_PLAYING)
11086 if (!network_player_action_received)
11087 return; /* failed to get network player actions in time */
11089 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11095 /* at this point we know that we really continue executing the game */
11097 network_player_action_received = FALSE;
11099 /* when playing tape, read previously recorded player input from tape data */
11100 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11103 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11108 if (tape.set_centered_player)
11110 game.centered_player_nr_next = tape.centered_player_nr_next;
11111 game.set_centered_player = TRUE;
11114 for (i = 0; i < MAX_PLAYERS; i++)
11116 summarized_player_action |= stored_player[i].action;
11118 if (!network_playing)
11119 stored_player[i].effective_action = stored_player[i].action;
11122 #if defined(NETWORK_AVALIABLE)
11123 if (network_playing)
11124 SendToServer_MovePlayer(summarized_player_action);
11127 if (!options.network && !setup.team_mode)
11128 local_player->effective_action = summarized_player_action;
11130 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11132 for (i = 0; i < MAX_PLAYERS; i++)
11133 stored_player[i].effective_action =
11134 (i == game.centered_player_nr ? summarized_player_action : 0);
11137 if (recorded_player_action != NULL)
11138 for (i = 0; i < MAX_PLAYERS; i++)
11139 stored_player[i].effective_action = recorded_player_action[i];
11141 for (i = 0; i < MAX_PLAYERS; i++)
11143 tape_action[i] = stored_player[i].effective_action;
11145 /* (this can only happen in the R'n'D game engine) */
11146 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11147 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11150 /* only record actions from input devices, but not programmed actions */
11151 if (tape.recording)
11152 TapeRecordAction(tape_action);
11154 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11156 GameActions_EM_Main();
11164 void GameActions_EM_Main()
11166 byte effective_action[MAX_PLAYERS];
11167 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11170 for (i = 0; i < MAX_PLAYERS; i++)
11171 effective_action[i] = stored_player[i].effective_action;
11173 GameActions_EM(effective_action, warp_mode);
11177 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11180 void GameActions_RND()
11182 int magic_wall_x = 0, magic_wall_y = 0;
11183 int i, x, y, element, graphic;
11185 InitPlayfieldScanModeVars();
11187 #if USE_ONE_MORE_CHANGE_PER_FRAME
11188 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11190 SCAN_PLAYFIELD(x, y)
11192 ChangeCount[x][y] = 0;
11193 ChangeEvent[x][y] = -1;
11198 if (game.set_centered_player)
11200 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11202 /* switching to "all players" only possible if all players fit to screen */
11203 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11205 game.centered_player_nr_next = game.centered_player_nr;
11206 game.set_centered_player = FALSE;
11209 /* do not switch focus to non-existing (or non-active) player */
11210 if (game.centered_player_nr_next >= 0 &&
11211 !stored_player[game.centered_player_nr_next].active)
11213 game.centered_player_nr_next = game.centered_player_nr;
11214 game.set_centered_player = FALSE;
11218 if (game.set_centered_player &&
11219 ScreenMovPos == 0) /* screen currently aligned at tile position */
11223 if (game.centered_player_nr_next == -1)
11225 setScreenCenteredToAllPlayers(&sx, &sy);
11229 sx = stored_player[game.centered_player_nr_next].jx;
11230 sy = stored_player[game.centered_player_nr_next].jy;
11233 game.centered_player_nr = game.centered_player_nr_next;
11234 game.set_centered_player = FALSE;
11236 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11237 DrawGameDoorValues();
11240 for (i = 0; i < MAX_PLAYERS; i++)
11242 int actual_player_action = stored_player[i].effective_action;
11245 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11246 - rnd_equinox_tetrachloride 048
11247 - rnd_equinox_tetrachloride_ii 096
11248 - rnd_emanuel_schmieg 002
11249 - doctor_sloan_ww 001, 020
11251 if (stored_player[i].MovPos == 0)
11252 CheckGravityMovement(&stored_player[i]);
11255 /* overwrite programmed action with tape action */
11256 if (stored_player[i].programmed_action)
11257 actual_player_action = stored_player[i].programmed_action;
11259 PlayerActions(&stored_player[i], actual_player_action);
11261 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11264 ScrollScreen(NULL, SCROLL_GO_ON);
11266 /* for backwards compatibility, the following code emulates a fixed bug that
11267 occured when pushing elements (causing elements that just made their last
11268 pushing step to already (if possible) make their first falling step in the
11269 same game frame, which is bad); this code is also needed to use the famous
11270 "spring push bug" which is used in older levels and might be wanted to be
11271 used also in newer levels, but in this case the buggy pushing code is only
11272 affecting the "spring" element and no other elements */
11274 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11276 for (i = 0; i < MAX_PLAYERS; i++)
11278 struct PlayerInfo *player = &stored_player[i];
11279 int x = player->jx;
11280 int y = player->jy;
11282 if (player->active && player->is_pushing && player->is_moving &&
11284 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11285 Feld[x][y] == EL_SPRING))
11287 ContinueMoving(x, y);
11289 /* continue moving after pushing (this is actually a bug) */
11290 if (!IS_MOVING(x, y))
11291 Stop[x][y] = FALSE;
11297 debug_print_timestamp(0, "start main loop profiling");
11300 SCAN_PLAYFIELD(x, y)
11302 ChangeCount[x][y] = 0;
11303 ChangeEvent[x][y] = -1;
11305 /* this must be handled before main playfield loop */
11306 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11309 if (MovDelay[x][y] <= 0)
11313 #if USE_NEW_SNAP_DELAY
11314 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11317 if (MovDelay[x][y] <= 0)
11320 DrawLevelField(x, y);
11322 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11328 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11330 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11331 printf("GameActions(): This should never happen!\n");
11333 ChangePage[x][y] = -1;
11337 Stop[x][y] = FALSE;
11338 if (WasJustMoving[x][y] > 0)
11339 WasJustMoving[x][y]--;
11340 if (WasJustFalling[x][y] > 0)
11341 WasJustFalling[x][y]--;
11342 if (CheckCollision[x][y] > 0)
11343 CheckCollision[x][y]--;
11344 if (CheckImpact[x][y] > 0)
11345 CheckImpact[x][y]--;
11349 /* reset finished pushing action (not done in ContinueMoving() to allow
11350 continuous pushing animation for elements with zero push delay) */
11351 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11353 ResetGfxAnimation(x, y);
11354 DrawLevelField(x, y);
11358 if (IS_BLOCKED(x, y))
11362 Blocked2Moving(x, y, &oldx, &oldy);
11363 if (!IS_MOVING(oldx, oldy))
11365 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11366 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11367 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11368 printf("GameActions(): This should never happen!\n");
11375 debug_print_timestamp(0, "- time for pre-main loop:");
11378 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11379 SCAN_PLAYFIELD(x, y)
11381 element = Feld[x][y];
11382 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11387 int element2 = element;
11388 int graphic2 = graphic;
11390 int element2 = Feld[x][y];
11391 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11393 int last_gfx_frame = GfxFrame[x][y];
11395 if (graphic_info[graphic2].anim_global_sync)
11396 GfxFrame[x][y] = FrameCounter;
11397 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11398 GfxFrame[x][y] = CustomValue[x][y];
11399 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11400 GfxFrame[x][y] = element_info[element2].collect_score;
11401 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11402 GfxFrame[x][y] = ChangeDelay[x][y];
11404 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11405 DrawLevelGraphicAnimation(x, y, graphic2);
11408 ResetGfxFrame(x, y, TRUE);
11412 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11413 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11414 ResetRandomAnimationValue(x, y);
11418 SetRandomAnimationValue(x, y);
11422 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11425 #endif // -------------------- !!! TEST ONLY !!! --------------------
11428 debug_print_timestamp(0, "- time for TEST loop: -->");
11431 SCAN_PLAYFIELD(x, y)
11433 element = Feld[x][y];
11434 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11436 ResetGfxFrame(x, y, TRUE);
11438 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11439 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11440 ResetRandomAnimationValue(x, y);
11442 SetRandomAnimationValue(x, y);
11444 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11446 if (IS_INACTIVE(element))
11448 if (IS_ANIMATED(graphic))
11449 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11454 /* this may take place after moving, so 'element' may have changed */
11455 if (IS_CHANGING(x, y) &&
11456 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11458 int page = element_info[element].event_page_nr[CE_DELAY];
11461 HandleElementChange(x, y, page);
11463 if (CAN_CHANGE(element))
11464 HandleElementChange(x, y, page);
11466 if (HAS_ACTION(element))
11467 ExecuteCustomElementAction(x, y, element, page);
11470 element = Feld[x][y];
11471 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11474 #if 0 // ---------------------------------------------------------------------
11476 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11480 element = Feld[x][y];
11481 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11483 if (IS_ANIMATED(graphic) &&
11484 !IS_MOVING(x, y) &&
11486 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11488 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11489 DrawTwinkleOnField(x, y);
11491 else if (IS_MOVING(x, y))
11492 ContinueMoving(x, y);
11499 case EL_EM_EXIT_OPEN:
11500 case EL_SP_EXIT_OPEN:
11501 case EL_STEEL_EXIT_OPEN:
11502 case EL_EM_STEEL_EXIT_OPEN:
11503 case EL_SP_TERMINAL:
11504 case EL_SP_TERMINAL_ACTIVE:
11505 case EL_EXTRA_TIME:
11506 case EL_SHIELD_NORMAL:
11507 case EL_SHIELD_DEADLY:
11508 if (IS_ANIMATED(graphic))
11509 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11512 case EL_DYNAMITE_ACTIVE:
11513 case EL_EM_DYNAMITE_ACTIVE:
11514 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11515 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11516 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11517 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11518 case EL_SP_DISK_RED_ACTIVE:
11519 CheckDynamite(x, y);
11522 case EL_AMOEBA_GROWING:
11523 AmoebeWaechst(x, y);
11526 case EL_AMOEBA_SHRINKING:
11527 AmoebaDisappearing(x, y);
11530 #if !USE_NEW_AMOEBA_CODE
11531 case EL_AMOEBA_WET:
11532 case EL_AMOEBA_DRY:
11533 case EL_AMOEBA_FULL:
11535 case EL_EMC_DRIPPER:
11536 AmoebeAbleger(x, y);
11540 case EL_GAME_OF_LIFE:
11545 case EL_EXIT_CLOSED:
11549 case EL_EM_EXIT_CLOSED:
11553 case EL_STEEL_EXIT_CLOSED:
11554 CheckExitSteel(x, y);
11557 case EL_EM_STEEL_EXIT_CLOSED:
11558 CheckExitSteelEM(x, y);
11561 case EL_SP_EXIT_CLOSED:
11565 case EL_EXPANDABLE_WALL_GROWING:
11566 case EL_EXPANDABLE_STEELWALL_GROWING:
11567 MauerWaechst(x, y);
11570 case EL_EXPANDABLE_WALL:
11571 case EL_EXPANDABLE_WALL_HORIZONTAL:
11572 case EL_EXPANDABLE_WALL_VERTICAL:
11573 case EL_EXPANDABLE_WALL_ANY:
11574 case EL_BD_EXPANDABLE_WALL:
11575 MauerAbleger(x, y);
11578 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11579 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11580 case EL_EXPANDABLE_STEELWALL_ANY:
11581 MauerAblegerStahl(x, y);
11585 CheckForDragon(x, y);
11591 case EL_ELEMENT_SNAPPING:
11592 case EL_DIAGONAL_SHRINKING:
11593 case EL_DIAGONAL_GROWING:
11596 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11598 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11603 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11604 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11609 #else // ---------------------------------------------------------------------
11611 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11615 element = Feld[x][y];
11616 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11618 if (IS_ANIMATED(graphic) &&
11619 !IS_MOVING(x, y) &&
11621 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11623 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11624 DrawTwinkleOnField(x, y);
11626 else if ((element == EL_ACID ||
11627 element == EL_EXIT_OPEN ||
11628 element == EL_EM_EXIT_OPEN ||
11629 element == EL_SP_EXIT_OPEN ||
11630 element == EL_STEEL_EXIT_OPEN ||
11631 element == EL_EM_STEEL_EXIT_OPEN ||
11632 element == EL_SP_TERMINAL ||
11633 element == EL_SP_TERMINAL_ACTIVE ||
11634 element == EL_EXTRA_TIME ||
11635 element == EL_SHIELD_NORMAL ||
11636 element == EL_SHIELD_DEADLY) &&
11637 IS_ANIMATED(graphic))
11638 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11639 else if (IS_MOVING(x, y))
11640 ContinueMoving(x, y);
11641 else if (IS_ACTIVE_BOMB(element))
11642 CheckDynamite(x, y);
11643 else if (element == EL_AMOEBA_GROWING)
11644 AmoebeWaechst(x, y);
11645 else if (element == EL_AMOEBA_SHRINKING)
11646 AmoebaDisappearing(x, y);
11648 #if !USE_NEW_AMOEBA_CODE
11649 else if (IS_AMOEBALIVE(element))
11650 AmoebeAbleger(x, y);
11653 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11655 else if (element == EL_EXIT_CLOSED)
11657 else if (element == EL_EM_EXIT_CLOSED)
11659 else if (element == EL_STEEL_EXIT_CLOSED)
11660 CheckExitSteel(x, y);
11661 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11662 CheckExitSteelEM(x, y);
11663 else if (element == EL_SP_EXIT_CLOSED)
11665 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11666 element == EL_EXPANDABLE_STEELWALL_GROWING)
11667 MauerWaechst(x, y);
11668 else if (element == EL_EXPANDABLE_WALL ||
11669 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11670 element == EL_EXPANDABLE_WALL_VERTICAL ||
11671 element == EL_EXPANDABLE_WALL_ANY ||
11672 element == EL_BD_EXPANDABLE_WALL)
11673 MauerAbleger(x, y);
11674 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11675 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11676 element == EL_EXPANDABLE_STEELWALL_ANY)
11677 MauerAblegerStahl(x, y);
11678 else if (element == EL_FLAMES)
11679 CheckForDragon(x, y);
11680 else if (element == EL_EXPLOSION)
11681 ; /* drawing of correct explosion animation is handled separately */
11682 else if (element == EL_ELEMENT_SNAPPING ||
11683 element == EL_DIAGONAL_SHRINKING ||
11684 element == EL_DIAGONAL_GROWING)
11686 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11688 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11690 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11691 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11693 #endif // ---------------------------------------------------------------------
11695 if (IS_BELT_ACTIVE(element))
11696 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11698 if (game.magic_wall_active)
11700 int jx = local_player->jx, jy = local_player->jy;
11702 /* play the element sound at the position nearest to the player */
11703 if ((element == EL_MAGIC_WALL_FULL ||
11704 element == EL_MAGIC_WALL_ACTIVE ||
11705 element == EL_MAGIC_WALL_EMPTYING ||
11706 element == EL_BD_MAGIC_WALL_FULL ||
11707 element == EL_BD_MAGIC_WALL_ACTIVE ||
11708 element == EL_BD_MAGIC_WALL_EMPTYING ||
11709 element == EL_DC_MAGIC_WALL_FULL ||
11710 element == EL_DC_MAGIC_WALL_ACTIVE ||
11711 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11712 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11721 debug_print_timestamp(0, "- time for MAIN loop: -->");
11724 #if USE_NEW_AMOEBA_CODE
11725 /* new experimental amoeba growth stuff */
11726 if (!(FrameCounter % 8))
11728 static unsigned long random = 1684108901;
11730 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11732 x = RND(lev_fieldx);
11733 y = RND(lev_fieldy);
11734 element = Feld[x][y];
11736 if (!IS_PLAYER(x,y) &&
11737 (element == EL_EMPTY ||
11738 CAN_GROW_INTO(element) ||
11739 element == EL_QUICKSAND_EMPTY ||
11740 element == EL_QUICKSAND_FAST_EMPTY ||
11741 element == EL_ACID_SPLASH_LEFT ||
11742 element == EL_ACID_SPLASH_RIGHT))
11744 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11745 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11746 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11747 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11748 Feld[x][y] = EL_AMOEBA_DROP;
11751 random = random * 129 + 1;
11757 if (game.explosions_delayed)
11760 game.explosions_delayed = FALSE;
11762 SCAN_PLAYFIELD(x, y)
11764 element = Feld[x][y];
11766 if (ExplodeField[x][y])
11767 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11768 else if (element == EL_EXPLOSION)
11769 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11771 ExplodeField[x][y] = EX_TYPE_NONE;
11774 game.explosions_delayed = TRUE;
11777 if (game.magic_wall_active)
11779 if (!(game.magic_wall_time_left % 4))
11781 int element = Feld[magic_wall_x][magic_wall_y];
11783 if (element == EL_BD_MAGIC_WALL_FULL ||
11784 element == EL_BD_MAGIC_WALL_ACTIVE ||
11785 element == EL_BD_MAGIC_WALL_EMPTYING)
11786 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11787 else if (element == EL_DC_MAGIC_WALL_FULL ||
11788 element == EL_DC_MAGIC_WALL_ACTIVE ||
11789 element == EL_DC_MAGIC_WALL_EMPTYING)
11790 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11792 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11795 if (game.magic_wall_time_left > 0)
11797 game.magic_wall_time_left--;
11799 if (!game.magic_wall_time_left)
11801 SCAN_PLAYFIELD(x, y)
11803 element = Feld[x][y];
11805 if (element == EL_MAGIC_WALL_ACTIVE ||
11806 element == EL_MAGIC_WALL_FULL)
11808 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11809 DrawLevelField(x, y);
11811 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11812 element == EL_BD_MAGIC_WALL_FULL)
11814 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11815 DrawLevelField(x, y);
11817 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11818 element == EL_DC_MAGIC_WALL_FULL)
11820 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11821 DrawLevelField(x, y);
11825 game.magic_wall_active = FALSE;
11830 if (game.light_time_left > 0)
11832 game.light_time_left--;
11834 if (game.light_time_left == 0)
11835 RedrawAllLightSwitchesAndInvisibleElements();
11838 if (game.timegate_time_left > 0)
11840 game.timegate_time_left--;
11842 if (game.timegate_time_left == 0)
11843 CloseAllOpenTimegates();
11846 if (game.lenses_time_left > 0)
11848 game.lenses_time_left--;
11850 if (game.lenses_time_left == 0)
11851 RedrawAllInvisibleElementsForLenses();
11854 if (game.magnify_time_left > 0)
11856 game.magnify_time_left--;
11858 if (game.magnify_time_left == 0)
11859 RedrawAllInvisibleElementsForMagnifier();
11862 for (i = 0; i < MAX_PLAYERS; i++)
11864 struct PlayerInfo *player = &stored_player[i];
11866 if (SHIELD_ON(player))
11868 if (player->shield_deadly_time_left)
11869 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11870 else if (player->shield_normal_time_left)
11871 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11878 PlayAllPlayersSound();
11880 if (options.debug) /* calculate frames per second */
11882 static unsigned long fps_counter = 0;
11883 static int fps_frames = 0;
11884 unsigned long fps_delay_ms = Counter() - fps_counter;
11888 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11890 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11893 fps_counter = Counter();
11896 redraw_mask |= REDRAW_FPS;
11899 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11901 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11903 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11905 local_player->show_envelope = 0;
11909 debug_print_timestamp(0, "stop main loop profiling ");
11910 printf("----------------------------------------------------------\n");
11913 /* use random number generator in every frame to make it less predictable */
11914 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11918 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11920 int min_x = x, min_y = y, max_x = x, max_y = y;
11923 for (i = 0; i < MAX_PLAYERS; i++)
11925 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11927 if (!stored_player[i].active || &stored_player[i] == player)
11930 min_x = MIN(min_x, jx);
11931 min_y = MIN(min_y, jy);
11932 max_x = MAX(max_x, jx);
11933 max_y = MAX(max_y, jy);
11936 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11939 static boolean AllPlayersInVisibleScreen()
11943 for (i = 0; i < MAX_PLAYERS; i++)
11945 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11947 if (!stored_player[i].active)
11950 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11957 void ScrollLevel(int dx, int dy)
11960 static Bitmap *bitmap_db_field2 = NULL;
11961 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11968 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11969 /* only horizontal XOR vertical scroll direction allowed */
11970 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11975 if (bitmap_db_field2 == NULL)
11976 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11978 /* needed when blitting directly to same bitmap -- should not be needed with
11979 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11980 BlitBitmap(drawto_field, bitmap_db_field2,
11981 FX + TILEX * (dx == -1) - softscroll_offset,
11982 FY + TILEY * (dy == -1) - softscroll_offset,
11983 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11984 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11985 FX + TILEX * (dx == 1) - softscroll_offset,
11986 FY + TILEY * (dy == 1) - softscroll_offset);
11987 BlitBitmap(bitmap_db_field2, drawto_field,
11988 FX + TILEX * (dx == 1) - softscroll_offset,
11989 FY + TILEY * (dy == 1) - softscroll_offset,
11990 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11991 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11992 FX + TILEX * (dx == 1) - softscroll_offset,
11993 FY + TILEY * (dy == 1) - softscroll_offset);
11998 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11999 int xsize = (BX2 - BX1 + 1);
12000 int ysize = (BY2 - BY1 + 1);
12001 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12002 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12003 int step = (start < end ? +1 : -1);
12005 for (i = start; i != end; i += step)
12007 BlitBitmap(drawto_field, drawto_field,
12008 FX + TILEX * (dx != 0 ? i + step : 0),
12009 FY + TILEY * (dy != 0 ? i + step : 0),
12010 TILEX * (dx != 0 ? 1 : xsize),
12011 TILEY * (dy != 0 ? 1 : ysize),
12012 FX + TILEX * (dx != 0 ? i : 0),
12013 FY + TILEY * (dy != 0 ? i : 0));
12018 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12020 BlitBitmap(drawto_field, drawto_field,
12021 FX + TILEX * (dx == -1) - softscroll_offset,
12022 FY + TILEY * (dy == -1) - softscroll_offset,
12023 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12024 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12025 FX + TILEX * (dx == 1) - softscroll_offset,
12026 FY + TILEY * (dy == 1) - softscroll_offset);
12032 x = (dx == 1 ? BX1 : BX2);
12033 for (y = BY1; y <= BY2; y++)
12034 DrawScreenField(x, y);
12039 y = (dy == 1 ? BY1 : BY2);
12040 for (x = BX1; x <= BX2; x++)
12041 DrawScreenField(x, y);
12044 redraw_mask |= REDRAW_FIELD;
12047 static boolean canFallDown(struct PlayerInfo *player)
12049 int jx = player->jx, jy = player->jy;
12051 return (IN_LEV_FIELD(jx, jy + 1) &&
12052 (IS_FREE(jx, jy + 1) ||
12053 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12054 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12055 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12058 static boolean canPassField(int x, int y, int move_dir)
12060 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12061 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12062 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12063 int nextx = x + dx;
12064 int nexty = y + dy;
12065 int element = Feld[x][y];
12067 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12068 !CAN_MOVE(element) &&
12069 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12070 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12071 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12074 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12076 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12077 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12078 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12082 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12083 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12084 (IS_DIGGABLE(Feld[newx][newy]) ||
12085 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12086 canPassField(newx, newy, move_dir)));
12089 static void CheckGravityMovement(struct PlayerInfo *player)
12091 #if USE_PLAYER_GRAVITY
12092 if (player->gravity && !player->programmed_action)
12094 if (game.gravity && !player->programmed_action)
12097 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12098 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12099 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12100 int jx = player->jx, jy = player->jy;
12101 boolean player_is_moving_to_valid_field =
12102 (!player_is_snapping &&
12103 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12104 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12105 boolean player_can_fall_down = canFallDown(player);
12107 if (player_can_fall_down &&
12108 !player_is_moving_to_valid_field)
12109 player->programmed_action = MV_DOWN;
12113 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12115 return CheckGravityMovement(player);
12117 #if USE_PLAYER_GRAVITY
12118 if (player->gravity && !player->programmed_action)
12120 if (game.gravity && !player->programmed_action)
12123 int jx = player->jx, jy = player->jy;
12124 boolean field_under_player_is_free =
12125 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12126 boolean player_is_standing_on_valid_field =
12127 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12128 (IS_WALKABLE(Feld[jx][jy]) &&
12129 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12131 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12132 player->programmed_action = MV_DOWN;
12137 MovePlayerOneStep()
12138 -----------------------------------------------------------------------------
12139 dx, dy: direction (non-diagonal) to try to move the player to
12140 real_dx, real_dy: direction as read from input device (can be diagonal)
12143 boolean MovePlayerOneStep(struct PlayerInfo *player,
12144 int dx, int dy, int real_dx, int real_dy)
12146 int jx = player->jx, jy = player->jy;
12147 int new_jx = jx + dx, new_jy = jy + dy;
12148 #if !USE_FIXED_DONT_RUN_INTO
12152 boolean player_can_move = !player->cannot_move;
12154 if (!player->active || (!dx && !dy))
12155 return MP_NO_ACTION;
12157 player->MovDir = (dx < 0 ? MV_LEFT :
12158 dx > 0 ? MV_RIGHT :
12160 dy > 0 ? MV_DOWN : MV_NONE);
12162 if (!IN_LEV_FIELD(new_jx, new_jy))
12163 return MP_NO_ACTION;
12165 if (!player_can_move)
12167 if (player->MovPos == 0)
12169 player->is_moving = FALSE;
12170 player->is_digging = FALSE;
12171 player->is_collecting = FALSE;
12172 player->is_snapping = FALSE;
12173 player->is_pushing = FALSE;
12178 if (!options.network && game.centered_player_nr == -1 &&
12179 !AllPlayersInSight(player, new_jx, new_jy))
12180 return MP_NO_ACTION;
12182 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12183 return MP_NO_ACTION;
12186 #if !USE_FIXED_DONT_RUN_INTO
12187 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12189 /* (moved to DigField()) */
12190 if (player_can_move && DONT_RUN_INTO(element))
12192 if (element == EL_ACID && dx == 0 && dy == 1)
12194 SplashAcid(new_jx, new_jy);
12195 Feld[jx][jy] = EL_PLAYER_1;
12196 InitMovingField(jx, jy, MV_DOWN);
12197 Store[jx][jy] = EL_ACID;
12198 ContinueMoving(jx, jy);
12199 BuryPlayer(player);
12202 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12208 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12209 if (can_move != MP_MOVING)
12212 /* check if DigField() has caused relocation of the player */
12213 if (player->jx != jx || player->jy != jy)
12214 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12216 StorePlayer[jx][jy] = 0;
12217 player->last_jx = jx;
12218 player->last_jy = jy;
12219 player->jx = new_jx;
12220 player->jy = new_jy;
12221 StorePlayer[new_jx][new_jy] = player->element_nr;
12223 if (player->move_delay_value_next != -1)
12225 player->move_delay_value = player->move_delay_value_next;
12226 player->move_delay_value_next = -1;
12230 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12232 player->step_counter++;
12234 PlayerVisit[jx][jy] = FrameCounter;
12236 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12237 player->is_moving = TRUE;
12241 /* should better be called in MovePlayer(), but this breaks some tapes */
12242 ScrollPlayer(player, SCROLL_INIT);
12248 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12250 int jx = player->jx, jy = player->jy;
12251 int old_jx = jx, old_jy = jy;
12252 int moved = MP_NO_ACTION;
12254 if (!player->active)
12259 if (player->MovPos == 0)
12261 player->is_moving = FALSE;
12262 player->is_digging = FALSE;
12263 player->is_collecting = FALSE;
12264 player->is_snapping = FALSE;
12265 player->is_pushing = FALSE;
12271 if (player->move_delay > 0)
12274 player->move_delay = -1; /* set to "uninitialized" value */
12276 /* store if player is automatically moved to next field */
12277 player->is_auto_moving = (player->programmed_action != MV_NONE);
12279 /* remove the last programmed player action */
12280 player->programmed_action = 0;
12282 if (player->MovPos)
12284 /* should only happen if pre-1.2 tape recordings are played */
12285 /* this is only for backward compatibility */
12287 int original_move_delay_value = player->move_delay_value;
12290 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12294 /* scroll remaining steps with finest movement resolution */
12295 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12297 while (player->MovPos)
12299 ScrollPlayer(player, SCROLL_GO_ON);
12300 ScrollScreen(NULL, SCROLL_GO_ON);
12302 AdvanceFrameAndPlayerCounters(player->index_nr);
12308 player->move_delay_value = original_move_delay_value;
12311 player->is_active = FALSE;
12313 if (player->last_move_dir & MV_HORIZONTAL)
12315 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12316 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12320 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12321 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12324 #if USE_FIXED_BORDER_RUNNING_GFX
12325 if (!moved && !player->is_active)
12327 player->is_moving = FALSE;
12328 player->is_digging = FALSE;
12329 player->is_collecting = FALSE;
12330 player->is_snapping = FALSE;
12331 player->is_pushing = FALSE;
12339 if (moved & MP_MOVING && !ScreenMovPos &&
12340 (player->index_nr == game.centered_player_nr ||
12341 game.centered_player_nr == -1))
12343 if (moved & MP_MOVING && !ScreenMovPos &&
12344 (player == local_player || !options.network))
12347 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12348 int offset = game.scroll_delay_value;
12350 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12352 /* actual player has left the screen -- scroll in that direction */
12353 if (jx != old_jx) /* player has moved horizontally */
12354 scroll_x += (jx - old_jx);
12355 else /* player has moved vertically */
12356 scroll_y += (jy - old_jy);
12360 if (jx != old_jx) /* player has moved horizontally */
12362 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12363 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12364 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12366 /* don't scroll over playfield boundaries */
12367 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12368 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12370 /* don't scroll more than one field at a time */
12371 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12373 /* don't scroll against the player's moving direction */
12374 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12375 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12376 scroll_x = old_scroll_x;
12378 else /* player has moved vertically */
12380 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12381 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12382 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12384 /* don't scroll over playfield boundaries */
12385 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12386 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12388 /* don't scroll more than one field at a time */
12389 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12391 /* don't scroll against the player's moving direction */
12392 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12393 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12394 scroll_y = old_scroll_y;
12398 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12401 if (!options.network && game.centered_player_nr == -1 &&
12402 !AllPlayersInVisibleScreen())
12404 scroll_x = old_scroll_x;
12405 scroll_y = old_scroll_y;
12409 if (!options.network && !AllPlayersInVisibleScreen())
12411 scroll_x = old_scroll_x;
12412 scroll_y = old_scroll_y;
12417 ScrollScreen(player, SCROLL_INIT);
12418 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12423 player->StepFrame = 0;
12425 if (moved & MP_MOVING)
12427 if (old_jx != jx && old_jy == jy)
12428 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12429 else if (old_jx == jx && old_jy != jy)
12430 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12432 DrawLevelField(jx, jy); /* for "crumbled sand" */
12434 player->last_move_dir = player->MovDir;
12435 player->is_moving = TRUE;
12436 player->is_snapping = FALSE;
12437 player->is_switching = FALSE;
12438 player->is_dropping = FALSE;
12439 player->is_dropping_pressed = FALSE;
12440 player->drop_pressed_delay = 0;
12443 /* should better be called here than above, but this breaks some tapes */
12444 ScrollPlayer(player, SCROLL_INIT);
12449 CheckGravityMovementWhenNotMoving(player);
12451 player->is_moving = FALSE;
12453 /* at this point, the player is allowed to move, but cannot move right now
12454 (e.g. because of something blocking the way) -- ensure that the player
12455 is also allowed to move in the next frame (in old versions before 3.1.1,
12456 the player was forced to wait again for eight frames before next try) */
12458 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12459 player->move_delay = 0; /* allow direct movement in the next frame */
12462 if (player->move_delay == -1) /* not yet initialized by DigField() */
12463 player->move_delay = player->move_delay_value;
12465 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12467 TestIfPlayerTouchesBadThing(jx, jy);
12468 TestIfPlayerTouchesCustomElement(jx, jy);
12471 if (!player->active)
12472 RemovePlayer(player);
12477 void ScrollPlayer(struct PlayerInfo *player, int mode)
12479 int jx = player->jx, jy = player->jy;
12480 int last_jx = player->last_jx, last_jy = player->last_jy;
12481 int move_stepsize = TILEX / player->move_delay_value;
12483 #if USE_NEW_PLAYER_SPEED
12484 if (!player->active)
12487 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12490 if (!player->active || player->MovPos == 0)
12494 if (mode == SCROLL_INIT)
12496 player->actual_frame_counter = FrameCounter;
12497 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12499 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12500 Feld[last_jx][last_jy] == EL_EMPTY)
12502 int last_field_block_delay = 0; /* start with no blocking at all */
12503 int block_delay_adjustment = player->block_delay_adjustment;
12505 /* if player blocks last field, add delay for exactly one move */
12506 if (player->block_last_field)
12508 last_field_block_delay += player->move_delay_value;
12510 /* when blocking enabled, prevent moving up despite gravity */
12511 #if USE_PLAYER_GRAVITY
12512 if (player->gravity && player->MovDir == MV_UP)
12513 block_delay_adjustment = -1;
12515 if (game.gravity && player->MovDir == MV_UP)
12516 block_delay_adjustment = -1;
12520 /* add block delay adjustment (also possible when not blocking) */
12521 last_field_block_delay += block_delay_adjustment;
12523 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12524 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12527 #if USE_NEW_PLAYER_SPEED
12528 if (player->MovPos != 0) /* player has not yet reached destination */
12534 else if (!FrameReached(&player->actual_frame_counter, 1))
12537 #if USE_NEW_PLAYER_SPEED
12538 if (player->MovPos != 0)
12540 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12541 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12543 /* before DrawPlayer() to draw correct player graphic for this case */
12544 if (player->MovPos == 0)
12545 CheckGravityMovement(player);
12548 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12549 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12551 /* before DrawPlayer() to draw correct player graphic for this case */
12552 if (player->MovPos == 0)
12553 CheckGravityMovement(player);
12556 if (player->MovPos == 0) /* player reached destination field */
12558 if (player->move_delay_reset_counter > 0)
12560 player->move_delay_reset_counter--;
12562 if (player->move_delay_reset_counter == 0)
12564 /* continue with normal speed after quickly moving through gate */
12565 HALVE_PLAYER_SPEED(player);
12567 /* be able to make the next move without delay */
12568 player->move_delay = 0;
12572 player->last_jx = jx;
12573 player->last_jy = jy;
12575 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12576 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12577 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12578 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12579 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12580 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12582 DrawPlayer(player); /* needed here only to cleanup last field */
12583 RemovePlayer(player);
12585 if (local_player->friends_still_needed == 0 ||
12586 IS_SP_ELEMENT(Feld[jx][jy]))
12587 PlayerWins(player);
12590 /* this breaks one level: "machine", level 000 */
12592 int move_direction = player->MovDir;
12593 int enter_side = MV_DIR_OPPOSITE(move_direction);
12594 int leave_side = move_direction;
12595 int old_jx = last_jx;
12596 int old_jy = last_jy;
12597 int old_element = Feld[old_jx][old_jy];
12598 int new_element = Feld[jx][jy];
12600 if (IS_CUSTOM_ELEMENT(old_element))
12601 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12603 player->index_bit, leave_side);
12605 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12606 CE_PLAYER_LEAVES_X,
12607 player->index_bit, leave_side);
12609 if (IS_CUSTOM_ELEMENT(new_element))
12610 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12611 player->index_bit, enter_side);
12613 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12614 CE_PLAYER_ENTERS_X,
12615 player->index_bit, enter_side);
12617 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12618 CE_MOVE_OF_X, move_direction);
12621 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12623 TestIfPlayerTouchesBadThing(jx, jy);
12624 TestIfPlayerTouchesCustomElement(jx, jy);
12626 /* needed because pushed element has not yet reached its destination,
12627 so it would trigger a change event at its previous field location */
12628 if (!player->is_pushing)
12629 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12631 if (!player->active)
12632 RemovePlayer(player);
12635 if (!local_player->LevelSolved && level.use_step_counter)
12645 if (TimeLeft <= 10 && setup.time_limit)
12646 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12649 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12651 DisplayGameControlValues();
12653 DrawGameValue_Time(TimeLeft);
12656 if (!TimeLeft && setup.time_limit)
12657 for (i = 0; i < MAX_PLAYERS; i++)
12658 KillPlayer(&stored_player[i]);
12661 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12663 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12665 DisplayGameControlValues();
12668 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12669 DrawGameValue_Time(TimePlayed);
12673 if (tape.single_step && tape.recording && !tape.pausing &&
12674 !player->programmed_action)
12675 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12679 void ScrollScreen(struct PlayerInfo *player, int mode)
12681 static unsigned long screen_frame_counter = 0;
12683 if (mode == SCROLL_INIT)
12685 /* set scrolling step size according to actual player's moving speed */
12686 ScrollStepSize = TILEX / player->move_delay_value;
12688 screen_frame_counter = FrameCounter;
12689 ScreenMovDir = player->MovDir;
12690 ScreenMovPos = player->MovPos;
12691 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12694 else if (!FrameReached(&screen_frame_counter, 1))
12699 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12700 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12701 redraw_mask |= REDRAW_FIELD;
12704 ScreenMovDir = MV_NONE;
12707 void TestIfPlayerTouchesCustomElement(int x, int y)
12709 static int xy[4][2] =
12716 static int trigger_sides[4][2] =
12718 /* center side border side */
12719 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12720 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12721 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12722 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12724 static int touch_dir[4] =
12726 MV_LEFT | MV_RIGHT,
12731 int center_element = Feld[x][y]; /* should always be non-moving! */
12734 for (i = 0; i < NUM_DIRECTIONS; i++)
12736 int xx = x + xy[i][0];
12737 int yy = y + xy[i][1];
12738 int center_side = trigger_sides[i][0];
12739 int border_side = trigger_sides[i][1];
12740 int border_element;
12742 if (!IN_LEV_FIELD(xx, yy))
12745 if (IS_PLAYER(x, y))
12747 struct PlayerInfo *player = PLAYERINFO(x, y);
12749 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12750 border_element = Feld[xx][yy]; /* may be moving! */
12751 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12752 border_element = Feld[xx][yy];
12753 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12754 border_element = MovingOrBlocked2Element(xx, yy);
12756 continue; /* center and border element do not touch */
12758 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12759 player->index_bit, border_side);
12760 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12761 CE_PLAYER_TOUCHES_X,
12762 player->index_bit, border_side);
12764 else if (IS_PLAYER(xx, yy))
12766 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12768 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12770 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12771 continue; /* center and border element do not touch */
12774 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12775 player->index_bit, center_side);
12776 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12777 CE_PLAYER_TOUCHES_X,
12778 player->index_bit, center_side);
12784 #if USE_ELEMENT_TOUCHING_BUGFIX
12786 void TestIfElementTouchesCustomElement(int x, int y)
12788 static int xy[4][2] =
12795 static int trigger_sides[4][2] =
12797 /* center side border side */
12798 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12799 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12800 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12801 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12803 static int touch_dir[4] =
12805 MV_LEFT | MV_RIGHT,
12810 boolean change_center_element = FALSE;
12811 int center_element = Feld[x][y]; /* should always be non-moving! */
12812 int border_element_old[NUM_DIRECTIONS];
12815 for (i = 0; i < NUM_DIRECTIONS; i++)
12817 int xx = x + xy[i][0];
12818 int yy = y + xy[i][1];
12819 int border_element;
12821 border_element_old[i] = -1;
12823 if (!IN_LEV_FIELD(xx, yy))
12826 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12827 border_element = Feld[xx][yy]; /* may be moving! */
12828 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12829 border_element = Feld[xx][yy];
12830 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12831 border_element = MovingOrBlocked2Element(xx, yy);
12833 continue; /* center and border element do not touch */
12835 border_element_old[i] = border_element;
12838 for (i = 0; i < NUM_DIRECTIONS; i++)
12840 int xx = x + xy[i][0];
12841 int yy = y + xy[i][1];
12842 int center_side = trigger_sides[i][0];
12843 int border_element = border_element_old[i];
12845 if (border_element == -1)
12848 /* check for change of border element */
12849 CheckElementChangeBySide(xx, yy, border_element, center_element,
12850 CE_TOUCHING_X, center_side);
12853 for (i = 0; i < NUM_DIRECTIONS; i++)
12855 int border_side = trigger_sides[i][1];
12856 int border_element = border_element_old[i];
12858 if (border_element == -1)
12861 /* check for change of center element (but change it only once) */
12862 if (!change_center_element)
12863 change_center_element =
12864 CheckElementChangeBySide(x, y, center_element, border_element,
12865 CE_TOUCHING_X, border_side);
12871 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12873 static int xy[4][2] =
12880 static int trigger_sides[4][2] =
12882 /* center side border side */
12883 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12884 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12885 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12886 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12888 static int touch_dir[4] =
12890 MV_LEFT | MV_RIGHT,
12895 boolean change_center_element = FALSE;
12896 int center_element = Feld[x][y]; /* should always be non-moving! */
12899 for (i = 0; i < NUM_DIRECTIONS; i++)
12901 int xx = x + xy[i][0];
12902 int yy = y + xy[i][1];
12903 int center_side = trigger_sides[i][0];
12904 int border_side = trigger_sides[i][1];
12905 int border_element;
12907 if (!IN_LEV_FIELD(xx, yy))
12910 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12911 border_element = Feld[xx][yy]; /* may be moving! */
12912 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12913 border_element = Feld[xx][yy];
12914 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12915 border_element = MovingOrBlocked2Element(xx, yy);
12917 continue; /* center and border element do not touch */
12919 /* check for change of center element (but change it only once) */
12920 if (!change_center_element)
12921 change_center_element =
12922 CheckElementChangeBySide(x, y, center_element, border_element,
12923 CE_TOUCHING_X, border_side);
12925 /* check for change of border element */
12926 CheckElementChangeBySide(xx, yy, border_element, center_element,
12927 CE_TOUCHING_X, center_side);
12933 void TestIfElementHitsCustomElement(int x, int y, int direction)
12935 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12936 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12937 int hitx = x + dx, hity = y + dy;
12938 int hitting_element = Feld[x][y];
12939 int touched_element;
12941 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12944 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12945 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12947 if (IN_LEV_FIELD(hitx, hity))
12949 int opposite_direction = MV_DIR_OPPOSITE(direction);
12950 int hitting_side = direction;
12951 int touched_side = opposite_direction;
12952 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12953 MovDir[hitx][hity] != direction ||
12954 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12960 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12961 CE_HITTING_X, touched_side);
12963 CheckElementChangeBySide(hitx, hity, touched_element,
12964 hitting_element, CE_HIT_BY_X, hitting_side);
12966 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12967 CE_HIT_BY_SOMETHING, opposite_direction);
12971 /* "hitting something" is also true when hitting the playfield border */
12972 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12973 CE_HITTING_SOMETHING, direction);
12977 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12979 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12980 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12981 int hitx = x + dx, hity = y + dy;
12982 int hitting_element = Feld[x][y];
12983 int touched_element;
12985 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12986 !IS_FREE(hitx, hity) &&
12987 (!IS_MOVING(hitx, hity) ||
12988 MovDir[hitx][hity] != direction ||
12989 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12992 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12996 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13000 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13001 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13003 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13004 EP_CAN_SMASH_EVERYTHING, direction);
13006 if (IN_LEV_FIELD(hitx, hity))
13008 int opposite_direction = MV_DIR_OPPOSITE(direction);
13009 int hitting_side = direction;
13010 int touched_side = opposite_direction;
13012 int touched_element = MovingOrBlocked2Element(hitx, hity);
13015 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13016 MovDir[hitx][hity] != direction ||
13017 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13026 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13027 CE_SMASHED_BY_SOMETHING, opposite_direction);
13029 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13030 CE_OTHER_IS_SMASHING, touched_side);
13032 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13033 CE_OTHER_GETS_SMASHED, hitting_side);
13039 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13041 int i, kill_x = -1, kill_y = -1;
13043 int bad_element = -1;
13044 static int test_xy[4][2] =
13051 static int test_dir[4] =
13059 for (i = 0; i < NUM_DIRECTIONS; i++)
13061 int test_x, test_y, test_move_dir, test_element;
13063 test_x = good_x + test_xy[i][0];
13064 test_y = good_y + test_xy[i][1];
13066 if (!IN_LEV_FIELD(test_x, test_y))
13070 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13072 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13074 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13075 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13077 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13078 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13082 bad_element = test_element;
13088 if (kill_x != -1 || kill_y != -1)
13090 if (IS_PLAYER(good_x, good_y))
13092 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13094 if (player->shield_deadly_time_left > 0 &&
13095 !IS_INDESTRUCTIBLE(bad_element))
13096 Bang(kill_x, kill_y);
13097 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13098 KillPlayer(player);
13101 Bang(good_x, good_y);
13105 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13107 int i, kill_x = -1, kill_y = -1;
13108 int bad_element = Feld[bad_x][bad_y];
13109 static int test_xy[4][2] =
13116 static int touch_dir[4] =
13118 MV_LEFT | MV_RIGHT,
13123 static int test_dir[4] =
13131 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13134 for (i = 0; i < NUM_DIRECTIONS; i++)
13136 int test_x, test_y, test_move_dir, test_element;
13138 test_x = bad_x + test_xy[i][0];
13139 test_y = bad_y + test_xy[i][1];
13140 if (!IN_LEV_FIELD(test_x, test_y))
13144 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13146 test_element = Feld[test_x][test_y];
13148 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13149 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13151 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13152 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13154 /* good thing is player or penguin that does not move away */
13155 if (IS_PLAYER(test_x, test_y))
13157 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13159 if (bad_element == EL_ROBOT && player->is_moving)
13160 continue; /* robot does not kill player if he is moving */
13162 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13164 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13165 continue; /* center and border element do not touch */
13172 else if (test_element == EL_PENGUIN)
13181 if (kill_x != -1 || kill_y != -1)
13183 if (IS_PLAYER(kill_x, kill_y))
13185 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13187 if (player->shield_deadly_time_left > 0 &&
13188 !IS_INDESTRUCTIBLE(bad_element))
13189 Bang(bad_x, bad_y);
13190 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13191 KillPlayer(player);
13194 Bang(kill_x, kill_y);
13198 void TestIfPlayerTouchesBadThing(int x, int y)
13200 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13203 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13205 TestIfGoodThingHitsBadThing(x, y, move_dir);
13208 void TestIfBadThingTouchesPlayer(int x, int y)
13210 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13213 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13215 TestIfBadThingHitsGoodThing(x, y, move_dir);
13218 void TestIfFriendTouchesBadThing(int x, int y)
13220 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13223 void TestIfBadThingTouchesFriend(int x, int y)
13225 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13228 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13230 int i, kill_x = bad_x, kill_y = bad_y;
13231 static int xy[4][2] =
13239 for (i = 0; i < NUM_DIRECTIONS; i++)
13243 x = bad_x + xy[i][0];
13244 y = bad_y + xy[i][1];
13245 if (!IN_LEV_FIELD(x, y))
13248 element = Feld[x][y];
13249 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13250 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13258 if (kill_x != bad_x || kill_y != bad_y)
13259 Bang(bad_x, bad_y);
13262 void KillPlayer(struct PlayerInfo *player)
13264 int jx = player->jx, jy = player->jy;
13266 if (!player->active)
13269 /* the following code was introduced to prevent an infinite loop when calling
13271 -> CheckTriggeredElementChangeExt()
13272 -> ExecuteCustomElementAction()
13274 -> (infinitely repeating the above sequence of function calls)
13275 which occurs when killing the player while having a CE with the setting
13276 "kill player X when explosion of <player X>"; the solution using a new
13277 field "player->killed" was chosen for backwards compatibility, although
13278 clever use of the fields "player->active" etc. would probably also work */
13280 if (player->killed)
13284 player->killed = TRUE;
13286 /* remove accessible field at the player's position */
13287 Feld[jx][jy] = EL_EMPTY;
13289 /* deactivate shield (else Bang()/Explode() would not work right) */
13290 player->shield_normal_time_left = 0;
13291 player->shield_deadly_time_left = 0;
13294 BuryPlayer(player);
13297 static void KillPlayerUnlessEnemyProtected(int x, int y)
13299 if (!PLAYER_ENEMY_PROTECTED(x, y))
13300 KillPlayer(PLAYERINFO(x, y));
13303 static void KillPlayerUnlessExplosionProtected(int x, int y)
13305 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13306 KillPlayer(PLAYERINFO(x, y));
13309 void BuryPlayer(struct PlayerInfo *player)
13311 int jx = player->jx, jy = player->jy;
13313 if (!player->active)
13316 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13317 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13319 player->GameOver = TRUE;
13320 RemovePlayer(player);
13323 void RemovePlayer(struct PlayerInfo *player)
13325 int jx = player->jx, jy = player->jy;
13326 int i, found = FALSE;
13328 player->present = FALSE;
13329 player->active = FALSE;
13331 if (!ExplodeField[jx][jy])
13332 StorePlayer[jx][jy] = 0;
13334 if (player->is_moving)
13335 DrawLevelField(player->last_jx, player->last_jy);
13337 for (i = 0; i < MAX_PLAYERS; i++)
13338 if (stored_player[i].active)
13342 AllPlayersGone = TRUE;
13348 #if USE_NEW_SNAP_DELAY
13349 static void setFieldForSnapping(int x, int y, int element, int direction)
13351 struct ElementInfo *ei = &element_info[element];
13352 int direction_bit = MV_DIR_TO_BIT(direction);
13353 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13354 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13355 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13357 Feld[x][y] = EL_ELEMENT_SNAPPING;
13358 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13360 ResetGfxAnimation(x, y);
13362 GfxElement[x][y] = element;
13363 GfxAction[x][y] = action;
13364 GfxDir[x][y] = direction;
13365 GfxFrame[x][y] = -1;
13370 =============================================================================
13371 checkDiagonalPushing()
13372 -----------------------------------------------------------------------------
13373 check if diagonal input device direction results in pushing of object
13374 (by checking if the alternative direction is walkable, diggable, ...)
13375 =============================================================================
13378 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13379 int x, int y, int real_dx, int real_dy)
13381 int jx, jy, dx, dy, xx, yy;
13383 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13386 /* diagonal direction: check alternative direction */
13391 xx = jx + (dx == 0 ? real_dx : 0);
13392 yy = jy + (dy == 0 ? real_dy : 0);
13394 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13398 =============================================================================
13400 -----------------------------------------------------------------------------
13401 x, y: field next to player (non-diagonal) to try to dig to
13402 real_dx, real_dy: direction as read from input device (can be diagonal)
13403 =============================================================================
13406 int DigField(struct PlayerInfo *player,
13407 int oldx, int oldy, int x, int y,
13408 int real_dx, int real_dy, int mode)
13410 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13411 boolean player_was_pushing = player->is_pushing;
13412 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13413 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13414 int jx = oldx, jy = oldy;
13415 int dx = x - jx, dy = y - jy;
13416 int nextx = x + dx, nexty = y + dy;
13417 int move_direction = (dx == -1 ? MV_LEFT :
13418 dx == +1 ? MV_RIGHT :
13420 dy == +1 ? MV_DOWN : MV_NONE);
13421 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13422 int dig_side = MV_DIR_OPPOSITE(move_direction);
13423 int old_element = Feld[jx][jy];
13424 #if USE_FIXED_DONT_RUN_INTO
13425 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13431 if (is_player) /* function can also be called by EL_PENGUIN */
13433 if (player->MovPos == 0)
13435 player->is_digging = FALSE;
13436 player->is_collecting = FALSE;
13439 if (player->MovPos == 0) /* last pushing move finished */
13440 player->is_pushing = FALSE;
13442 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13444 player->is_switching = FALSE;
13445 player->push_delay = -1;
13447 return MP_NO_ACTION;
13451 #if !USE_FIXED_DONT_RUN_INTO
13452 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13453 return MP_NO_ACTION;
13456 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13457 old_element = Back[jx][jy];
13459 /* in case of element dropped at player position, check background */
13460 else if (Back[jx][jy] != EL_EMPTY &&
13461 game.engine_version >= VERSION_IDENT(2,2,0,0))
13462 old_element = Back[jx][jy];
13464 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13465 return MP_NO_ACTION; /* field has no opening in this direction */
13467 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13468 return MP_NO_ACTION; /* field has no opening in this direction */
13470 #if USE_FIXED_DONT_RUN_INTO
13471 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13475 Feld[jx][jy] = player->artwork_element;
13476 InitMovingField(jx, jy, MV_DOWN);
13477 Store[jx][jy] = EL_ACID;
13478 ContinueMoving(jx, jy);
13479 BuryPlayer(player);
13481 return MP_DONT_RUN_INTO;
13485 #if USE_FIXED_DONT_RUN_INTO
13486 if (player_can_move && DONT_RUN_INTO(element))
13488 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13490 return MP_DONT_RUN_INTO;
13494 #if USE_FIXED_DONT_RUN_INTO
13495 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13496 return MP_NO_ACTION;
13499 #if !USE_FIXED_DONT_RUN_INTO
13500 element = Feld[x][y];
13503 collect_count = element_info[element].collect_count_initial;
13505 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13506 return MP_NO_ACTION;
13508 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13509 player_can_move = player_can_move_or_snap;
13511 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13512 game.engine_version >= VERSION_IDENT(2,2,0,0))
13514 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13515 player->index_bit, dig_side);
13516 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13517 player->index_bit, dig_side);
13519 if (element == EL_DC_LANDMINE)
13522 if (Feld[x][y] != element) /* field changed by snapping */
13525 return MP_NO_ACTION;
13528 #if USE_PLAYER_GRAVITY
13529 if (player->gravity && is_player && !player->is_auto_moving &&
13530 canFallDown(player) && move_direction != MV_DOWN &&
13531 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13532 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13534 if (game.gravity && is_player && !player->is_auto_moving &&
13535 canFallDown(player) && move_direction != MV_DOWN &&
13536 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13537 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13540 if (player_can_move &&
13541 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13543 int sound_element = SND_ELEMENT(element);
13544 int sound_action = ACTION_WALKING;
13546 if (IS_RND_GATE(element))
13548 if (!player->key[RND_GATE_NR(element)])
13549 return MP_NO_ACTION;
13551 else if (IS_RND_GATE_GRAY(element))
13553 if (!player->key[RND_GATE_GRAY_NR(element)])
13554 return MP_NO_ACTION;
13556 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13558 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13559 return MP_NO_ACTION;
13561 else if (element == EL_EXIT_OPEN ||
13562 element == EL_EM_EXIT_OPEN ||
13563 element == EL_STEEL_EXIT_OPEN ||
13564 element == EL_EM_STEEL_EXIT_OPEN ||
13565 element == EL_SP_EXIT_OPEN ||
13566 element == EL_SP_EXIT_OPENING)
13568 sound_action = ACTION_PASSING; /* player is passing exit */
13570 else if (element == EL_EMPTY)
13572 sound_action = ACTION_MOVING; /* nothing to walk on */
13575 /* play sound from background or player, whatever is available */
13576 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13577 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13579 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13581 else if (player_can_move &&
13582 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13584 if (!ACCESS_FROM(element, opposite_direction))
13585 return MP_NO_ACTION; /* field not accessible from this direction */
13587 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13588 return MP_NO_ACTION;
13590 if (IS_EM_GATE(element))
13592 if (!player->key[EM_GATE_NR(element)])
13593 return MP_NO_ACTION;
13595 else if (IS_EM_GATE_GRAY(element))
13597 if (!player->key[EM_GATE_GRAY_NR(element)])
13598 return MP_NO_ACTION;
13600 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13602 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13603 return MP_NO_ACTION;
13605 else if (IS_EMC_GATE(element))
13607 if (!player->key[EMC_GATE_NR(element)])
13608 return MP_NO_ACTION;
13610 else if (IS_EMC_GATE_GRAY(element))
13612 if (!player->key[EMC_GATE_GRAY_NR(element)])
13613 return MP_NO_ACTION;
13615 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13617 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13618 return MP_NO_ACTION;
13620 else if (element == EL_DC_GATE_WHITE ||
13621 element == EL_DC_GATE_WHITE_GRAY ||
13622 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13624 if (player->num_white_keys == 0)
13625 return MP_NO_ACTION;
13627 player->num_white_keys--;
13629 else if (IS_SP_PORT(element))
13631 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13632 element == EL_SP_GRAVITY_PORT_RIGHT ||
13633 element == EL_SP_GRAVITY_PORT_UP ||
13634 element == EL_SP_GRAVITY_PORT_DOWN)
13635 #if USE_PLAYER_GRAVITY
13636 player->gravity = !player->gravity;
13638 game.gravity = !game.gravity;
13640 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13641 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13642 element == EL_SP_GRAVITY_ON_PORT_UP ||
13643 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13644 #if USE_PLAYER_GRAVITY
13645 player->gravity = TRUE;
13647 game.gravity = TRUE;
13649 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13650 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13651 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13652 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13653 #if USE_PLAYER_GRAVITY
13654 player->gravity = FALSE;
13656 game.gravity = FALSE;
13660 /* automatically move to the next field with double speed */
13661 player->programmed_action = move_direction;
13663 if (player->move_delay_reset_counter == 0)
13665 player->move_delay_reset_counter = 2; /* two double speed steps */
13667 DOUBLE_PLAYER_SPEED(player);
13670 PlayLevelSoundAction(x, y, ACTION_PASSING);
13672 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13676 if (mode != DF_SNAP)
13678 GfxElement[x][y] = GFX_ELEMENT(element);
13679 player->is_digging = TRUE;
13682 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13684 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13685 player->index_bit, dig_side);
13687 if (mode == DF_SNAP)
13689 #if USE_NEW_SNAP_DELAY
13690 if (level.block_snap_field)
13691 setFieldForSnapping(x, y, element, move_direction);
13693 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13695 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13698 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13699 player->index_bit, dig_side);
13702 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13706 if (is_player && mode != DF_SNAP)
13708 GfxElement[x][y] = element;
13709 player->is_collecting = TRUE;
13712 if (element == EL_SPEED_PILL)
13714 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13716 else if (element == EL_EXTRA_TIME && level.time > 0)
13718 TimeLeft += level.extra_time;
13721 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13723 DisplayGameControlValues();
13725 DrawGameValue_Time(TimeLeft);
13728 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13730 player->shield_normal_time_left += level.shield_normal_time;
13731 if (element == EL_SHIELD_DEADLY)
13732 player->shield_deadly_time_left += level.shield_deadly_time;
13734 else if (element == EL_DYNAMITE ||
13735 element == EL_EM_DYNAMITE ||
13736 element == EL_SP_DISK_RED)
13738 if (player->inventory_size < MAX_INVENTORY_SIZE)
13739 player->inventory_element[player->inventory_size++] = element;
13741 DrawGameDoorValues();
13743 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13745 player->dynabomb_count++;
13746 player->dynabombs_left++;
13748 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13750 player->dynabomb_size++;
13752 else if (element == EL_DYNABOMB_INCREASE_POWER)
13754 player->dynabomb_xl = TRUE;
13756 else if (IS_KEY(element))
13758 player->key[KEY_NR(element)] = TRUE;
13760 DrawGameDoorValues();
13762 else if (element == EL_DC_KEY_WHITE)
13764 player->num_white_keys++;
13766 /* display white keys? */
13767 /* DrawGameDoorValues(); */
13769 else if (IS_ENVELOPE(element))
13771 player->show_envelope = element;
13773 else if (element == EL_EMC_LENSES)
13775 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13777 RedrawAllInvisibleElementsForLenses();
13779 else if (element == EL_EMC_MAGNIFIER)
13781 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13783 RedrawAllInvisibleElementsForMagnifier();
13785 else if (IS_DROPPABLE(element) ||
13786 IS_THROWABLE(element)) /* can be collected and dropped */
13790 if (collect_count == 0)
13791 player->inventory_infinite_element = element;
13793 for (i = 0; i < collect_count; i++)
13794 if (player->inventory_size < MAX_INVENTORY_SIZE)
13795 player->inventory_element[player->inventory_size++] = element;
13797 DrawGameDoorValues();
13799 else if (collect_count > 0)
13801 local_player->gems_still_needed -= collect_count;
13802 if (local_player->gems_still_needed < 0)
13803 local_player->gems_still_needed = 0;
13806 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13808 DisplayGameControlValues();
13810 DrawGameValue_Emeralds(local_player->gems_still_needed);
13814 RaiseScoreElement(element);
13815 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13818 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13819 player->index_bit, dig_side);
13821 if (mode == DF_SNAP)
13823 #if USE_NEW_SNAP_DELAY
13824 if (level.block_snap_field)
13825 setFieldForSnapping(x, y, element, move_direction);
13827 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13829 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13832 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13833 player->index_bit, dig_side);
13836 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13838 if (mode == DF_SNAP && element != EL_BD_ROCK)
13839 return MP_NO_ACTION;
13841 if (CAN_FALL(element) && dy)
13842 return MP_NO_ACTION;
13844 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13845 !(element == EL_SPRING && level.use_spring_bug))
13846 return MP_NO_ACTION;
13848 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13849 ((move_direction & MV_VERTICAL &&
13850 ((element_info[element].move_pattern & MV_LEFT &&
13851 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13852 (element_info[element].move_pattern & MV_RIGHT &&
13853 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13854 (move_direction & MV_HORIZONTAL &&
13855 ((element_info[element].move_pattern & MV_UP &&
13856 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13857 (element_info[element].move_pattern & MV_DOWN &&
13858 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13859 return MP_NO_ACTION;
13861 /* do not push elements already moving away faster than player */
13862 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13863 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13864 return MP_NO_ACTION;
13866 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13868 if (player->push_delay_value == -1 || !player_was_pushing)
13869 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13871 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13873 if (player->push_delay_value == -1)
13874 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13876 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13878 if (!player->is_pushing)
13879 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13882 player->is_pushing = TRUE;
13883 player->is_active = TRUE;
13885 if (!(IN_LEV_FIELD(nextx, nexty) &&
13886 (IS_FREE(nextx, nexty) ||
13887 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13888 IS_SB_ELEMENT(element)))))
13889 return MP_NO_ACTION;
13891 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13892 return MP_NO_ACTION;
13894 if (player->push_delay == -1) /* new pushing; restart delay */
13895 player->push_delay = 0;
13897 if (player->push_delay < player->push_delay_value &&
13898 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13899 element != EL_SPRING && element != EL_BALLOON)
13901 /* make sure that there is no move delay before next try to push */
13902 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13903 player->move_delay = 0;
13905 return MP_NO_ACTION;
13908 if (IS_SB_ELEMENT(element))
13910 if (element == EL_SOKOBAN_FIELD_FULL)
13912 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13913 local_player->sokobanfields_still_needed++;
13916 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13918 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13919 local_player->sokobanfields_still_needed--;
13922 Feld[x][y] = EL_SOKOBAN_OBJECT;
13924 if (Back[x][y] == Back[nextx][nexty])
13925 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13926 else if (Back[x][y] != 0)
13927 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13930 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13933 if (local_player->sokobanfields_still_needed == 0 &&
13934 game.emulation == EMU_SOKOBAN)
13936 PlayerWins(player);
13938 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13942 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13944 InitMovingField(x, y, move_direction);
13945 GfxAction[x][y] = ACTION_PUSHING;
13947 if (mode == DF_SNAP)
13948 ContinueMoving(x, y);
13950 MovPos[x][y] = (dx != 0 ? dx : dy);
13952 Pushed[x][y] = TRUE;
13953 Pushed[nextx][nexty] = TRUE;
13955 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13956 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13958 player->push_delay_value = -1; /* get new value later */
13960 /* check for element change _after_ element has been pushed */
13961 if (game.use_change_when_pushing_bug)
13963 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13964 player->index_bit, dig_side);
13965 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13966 player->index_bit, dig_side);
13969 else if (IS_SWITCHABLE(element))
13971 if (PLAYER_SWITCHING(player, x, y))
13973 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13974 player->index_bit, dig_side);
13979 player->is_switching = TRUE;
13980 player->switch_x = x;
13981 player->switch_y = y;
13983 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13985 if (element == EL_ROBOT_WHEEL)
13987 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13991 game.robot_wheel_active = TRUE;
13993 DrawLevelField(x, y);
13995 else if (element == EL_SP_TERMINAL)
13999 SCAN_PLAYFIELD(xx, yy)
14001 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14003 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14004 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14007 else if (IS_BELT_SWITCH(element))
14009 ToggleBeltSwitch(x, y);
14011 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14012 element == EL_SWITCHGATE_SWITCH_DOWN ||
14013 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14014 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14016 ToggleSwitchgateSwitch(x, y);
14018 else if (element == EL_LIGHT_SWITCH ||
14019 element == EL_LIGHT_SWITCH_ACTIVE)
14021 ToggleLightSwitch(x, y);
14023 else if (element == EL_TIMEGATE_SWITCH ||
14024 element == EL_DC_TIMEGATE_SWITCH)
14026 ActivateTimegateSwitch(x, y);
14028 else if (element == EL_BALLOON_SWITCH_LEFT ||
14029 element == EL_BALLOON_SWITCH_RIGHT ||
14030 element == EL_BALLOON_SWITCH_UP ||
14031 element == EL_BALLOON_SWITCH_DOWN ||
14032 element == EL_BALLOON_SWITCH_NONE ||
14033 element == EL_BALLOON_SWITCH_ANY)
14035 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14036 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14037 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14038 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14039 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14042 else if (element == EL_LAMP)
14044 Feld[x][y] = EL_LAMP_ACTIVE;
14045 local_player->lights_still_needed--;
14047 ResetGfxAnimation(x, y);
14048 DrawLevelField(x, y);
14050 else if (element == EL_TIME_ORB_FULL)
14052 Feld[x][y] = EL_TIME_ORB_EMPTY;
14054 if (level.time > 0 || level.use_time_orb_bug)
14056 TimeLeft += level.time_orb_time;
14059 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14061 DisplayGameControlValues();
14063 DrawGameValue_Time(TimeLeft);
14067 ResetGfxAnimation(x, y);
14068 DrawLevelField(x, y);
14070 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14071 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14075 game.ball_state = !game.ball_state;
14077 SCAN_PLAYFIELD(xx, yy)
14079 int e = Feld[xx][yy];
14081 if (game.ball_state)
14083 if (e == EL_EMC_MAGIC_BALL)
14084 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14085 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14086 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14090 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14091 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14092 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14093 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14098 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14099 player->index_bit, dig_side);
14101 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14102 player->index_bit, dig_side);
14104 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14105 player->index_bit, dig_side);
14111 if (!PLAYER_SWITCHING(player, x, y))
14113 player->is_switching = TRUE;
14114 player->switch_x = x;
14115 player->switch_y = y;
14117 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14118 player->index_bit, dig_side);
14119 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14120 player->index_bit, dig_side);
14122 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14123 player->index_bit, dig_side);
14124 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14125 player->index_bit, dig_side);
14128 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14129 player->index_bit, dig_side);
14130 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14131 player->index_bit, dig_side);
14133 return MP_NO_ACTION;
14136 player->push_delay = -1;
14138 if (is_player) /* function can also be called by EL_PENGUIN */
14140 if (Feld[x][y] != element) /* really digged/collected something */
14142 player->is_collecting = !player->is_digging;
14143 player->is_active = TRUE;
14150 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14152 int jx = player->jx, jy = player->jy;
14153 int x = jx + dx, y = jy + dy;
14154 int snap_direction = (dx == -1 ? MV_LEFT :
14155 dx == +1 ? MV_RIGHT :
14157 dy == +1 ? MV_DOWN : MV_NONE);
14158 boolean can_continue_snapping = (level.continuous_snapping &&
14159 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14161 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14164 if (!player->active || !IN_LEV_FIELD(x, y))
14172 if (player->MovPos == 0)
14173 player->is_pushing = FALSE;
14175 player->is_snapping = FALSE;
14177 if (player->MovPos == 0)
14179 player->is_moving = FALSE;
14180 player->is_digging = FALSE;
14181 player->is_collecting = FALSE;
14187 #if USE_NEW_CONTINUOUS_SNAPPING
14188 /* prevent snapping with already pressed snap key when not allowed */
14189 if (player->is_snapping && !can_continue_snapping)
14192 if (player->is_snapping)
14196 player->MovDir = snap_direction;
14198 if (player->MovPos == 0)
14200 player->is_moving = FALSE;
14201 player->is_digging = FALSE;
14202 player->is_collecting = FALSE;
14205 player->is_dropping = FALSE;
14206 player->is_dropping_pressed = FALSE;
14207 player->drop_pressed_delay = 0;
14209 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14212 player->is_snapping = TRUE;
14213 player->is_active = TRUE;
14215 if (player->MovPos == 0)
14217 player->is_moving = FALSE;
14218 player->is_digging = FALSE;
14219 player->is_collecting = FALSE;
14222 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14223 DrawLevelField(player->last_jx, player->last_jy);
14225 DrawLevelField(x, y);
14230 boolean DropElement(struct PlayerInfo *player)
14232 int old_element, new_element;
14233 int dropx = player->jx, dropy = player->jy;
14234 int drop_direction = player->MovDir;
14235 int drop_side = drop_direction;
14237 int drop_element = get_next_dropped_element(player);
14239 int drop_element = (player->inventory_size > 0 ?
14240 player->inventory_element[player->inventory_size - 1] :
14241 player->inventory_infinite_element != EL_UNDEFINED ?
14242 player->inventory_infinite_element :
14243 player->dynabombs_left > 0 ?
14244 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14248 player->is_dropping_pressed = TRUE;
14250 /* do not drop an element on top of another element; when holding drop key
14251 pressed without moving, dropped element must move away before the next
14252 element can be dropped (this is especially important if the next element
14253 is dynamite, which can be placed on background for historical reasons) */
14254 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14257 if (IS_THROWABLE(drop_element))
14259 dropx += GET_DX_FROM_DIR(drop_direction);
14260 dropy += GET_DY_FROM_DIR(drop_direction);
14262 if (!IN_LEV_FIELD(dropx, dropy))
14266 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14267 new_element = drop_element; /* default: no change when dropping */
14269 /* check if player is active, not moving and ready to drop */
14270 if (!player->active || player->MovPos || player->drop_delay > 0)
14273 /* check if player has anything that can be dropped */
14274 if (new_element == EL_UNDEFINED)
14277 /* check if drop key was pressed long enough for EM style dynamite */
14278 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14281 /* check if anything can be dropped at the current position */
14282 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14285 /* collected custom elements can only be dropped on empty fields */
14286 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14289 if (old_element != EL_EMPTY)
14290 Back[dropx][dropy] = old_element; /* store old element on this field */
14292 ResetGfxAnimation(dropx, dropy);
14293 ResetRandomAnimationValue(dropx, dropy);
14295 if (player->inventory_size > 0 ||
14296 player->inventory_infinite_element != EL_UNDEFINED)
14298 if (player->inventory_size > 0)
14300 player->inventory_size--;
14302 DrawGameDoorValues();
14304 if (new_element == EL_DYNAMITE)
14305 new_element = EL_DYNAMITE_ACTIVE;
14306 else if (new_element == EL_EM_DYNAMITE)
14307 new_element = EL_EM_DYNAMITE_ACTIVE;
14308 else if (new_element == EL_SP_DISK_RED)
14309 new_element = EL_SP_DISK_RED_ACTIVE;
14312 Feld[dropx][dropy] = new_element;
14314 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14315 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14316 el2img(Feld[dropx][dropy]), 0);
14318 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14320 /* needed if previous element just changed to "empty" in the last frame */
14321 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14323 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14324 player->index_bit, drop_side);
14325 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14327 player->index_bit, drop_side);
14329 TestIfElementTouchesCustomElement(dropx, dropy);
14331 else /* player is dropping a dyna bomb */
14333 player->dynabombs_left--;
14335 Feld[dropx][dropy] = new_element;
14337 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14338 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14339 el2img(Feld[dropx][dropy]), 0);
14341 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14344 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14345 InitField_WithBug1(dropx, dropy, FALSE);
14347 new_element = Feld[dropx][dropy]; /* element might have changed */
14349 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14350 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14352 int move_direction, nextx, nexty;
14354 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14355 MovDir[dropx][dropy] = drop_direction;
14357 move_direction = MovDir[dropx][dropy];
14358 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14359 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14361 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14363 #if USE_FIX_IMPACT_COLLISION
14364 /* do not cause impact style collision by dropping elements that can fall */
14365 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14367 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14371 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14372 player->is_dropping = TRUE;
14374 player->drop_pressed_delay = 0;
14375 player->is_dropping_pressed = FALSE;
14377 player->drop_x = dropx;
14378 player->drop_y = dropy;
14383 /* ------------------------------------------------------------------------- */
14384 /* game sound playing functions */
14385 /* ------------------------------------------------------------------------- */
14387 static int *loop_sound_frame = NULL;
14388 static int *loop_sound_volume = NULL;
14390 void InitPlayLevelSound()
14392 int num_sounds = getSoundListSize();
14394 checked_free(loop_sound_frame);
14395 checked_free(loop_sound_volume);
14397 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14398 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14401 static void PlayLevelSound(int x, int y, int nr)
14403 int sx = SCREENX(x), sy = SCREENY(y);
14404 int volume, stereo_position;
14405 int max_distance = 8;
14406 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14408 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14409 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14412 if (!IN_LEV_FIELD(x, y) ||
14413 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14414 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14417 volume = SOUND_MAX_VOLUME;
14419 if (!IN_SCR_FIELD(sx, sy))
14421 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14422 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14424 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14427 stereo_position = (SOUND_MAX_LEFT +
14428 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14429 (SCR_FIELDX + 2 * max_distance));
14431 if (IS_LOOP_SOUND(nr))
14433 /* This assures that quieter loop sounds do not overwrite louder ones,
14434 while restarting sound volume comparison with each new game frame. */
14436 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14439 loop_sound_volume[nr] = volume;
14440 loop_sound_frame[nr] = FrameCounter;
14443 PlaySoundExt(nr, volume, stereo_position, type);
14446 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14448 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14449 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14450 y < LEVELY(BY1) ? LEVELY(BY1) :
14451 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14455 static void PlayLevelSoundAction(int x, int y, int action)
14457 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14460 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14462 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14464 if (sound_effect != SND_UNDEFINED)
14465 PlayLevelSound(x, y, sound_effect);
14468 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14471 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14473 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14474 PlayLevelSound(x, y, sound_effect);
14477 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14479 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14481 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14482 PlayLevelSound(x, y, sound_effect);
14485 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14487 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14489 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14490 StopSound(sound_effect);
14493 static void PlayLevelMusic()
14495 if (levelset.music[level_nr] != MUS_UNDEFINED)
14496 PlayMusic(levelset.music[level_nr]); /* from config file */
14498 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14501 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14503 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14504 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14505 int x = xx - 1 - offset;
14506 int y = yy - 1 - offset;
14511 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14515 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14519 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14523 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14527 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14531 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14535 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14538 case SAMPLE_android_clone:
14539 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14542 case SAMPLE_android_move:
14543 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14546 case SAMPLE_spring:
14547 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14551 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14555 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14558 case SAMPLE_eater_eat:
14559 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14563 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14566 case SAMPLE_collect:
14567 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14570 case SAMPLE_diamond:
14571 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14574 case SAMPLE_squash:
14575 /* !!! CHECK THIS !!! */
14577 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14579 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14583 case SAMPLE_wonderfall:
14584 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14588 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14592 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14596 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14600 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14604 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14608 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14611 case SAMPLE_wonder:
14612 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14616 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14619 case SAMPLE_exit_open:
14620 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14623 case SAMPLE_exit_leave:
14624 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14627 case SAMPLE_dynamite:
14628 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14632 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14636 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14640 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14644 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14648 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14652 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14656 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14662 void ChangeTime(int value)
14664 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14668 /* EMC game engine uses value from time counter of RND game engine */
14669 level.native_em_level->lev->time = *time;
14671 DrawGameValue_Time(*time);
14674 void RaiseScore(int value)
14676 /* EMC game engine and RND game engine have separate score counters */
14677 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14678 &level.native_em_level->lev->score : &local_player->score);
14682 DrawGameValue_Score(*score);
14686 void RaiseScore(int value)
14688 local_player->score += value;
14691 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14693 DisplayGameControlValues();
14695 DrawGameValue_Score(local_player->score);
14699 void RaiseScoreElement(int element)
14704 case EL_BD_DIAMOND:
14705 case EL_EMERALD_YELLOW:
14706 case EL_EMERALD_RED:
14707 case EL_EMERALD_PURPLE:
14708 case EL_SP_INFOTRON:
14709 RaiseScore(level.score[SC_EMERALD]);
14712 RaiseScore(level.score[SC_DIAMOND]);
14715 RaiseScore(level.score[SC_CRYSTAL]);
14718 RaiseScore(level.score[SC_PEARL]);
14721 case EL_BD_BUTTERFLY:
14722 case EL_SP_ELECTRON:
14723 RaiseScore(level.score[SC_BUG]);
14726 case EL_BD_FIREFLY:
14727 case EL_SP_SNIKSNAK:
14728 RaiseScore(level.score[SC_SPACESHIP]);
14731 case EL_DARK_YAMYAM:
14732 RaiseScore(level.score[SC_YAMYAM]);
14735 RaiseScore(level.score[SC_ROBOT]);
14738 RaiseScore(level.score[SC_PACMAN]);
14741 RaiseScore(level.score[SC_NUT]);
14744 case EL_EM_DYNAMITE:
14745 case EL_SP_DISK_RED:
14746 case EL_DYNABOMB_INCREASE_NUMBER:
14747 case EL_DYNABOMB_INCREASE_SIZE:
14748 case EL_DYNABOMB_INCREASE_POWER:
14749 RaiseScore(level.score[SC_DYNAMITE]);
14751 case EL_SHIELD_NORMAL:
14752 case EL_SHIELD_DEADLY:
14753 RaiseScore(level.score[SC_SHIELD]);
14755 case EL_EXTRA_TIME:
14756 RaiseScore(level.extra_time_score);
14770 case EL_DC_KEY_WHITE:
14771 RaiseScore(level.score[SC_KEY]);
14774 RaiseScore(element_info[element].collect_score);
14779 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14781 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14783 #if defined(NETWORK_AVALIABLE)
14784 if (options.network)
14785 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14794 FadeSkipNextFadeIn();
14796 fading = fading_none;
14800 OpenDoor(DOOR_CLOSE_1);
14803 game_status = GAME_MODE_MAIN;
14806 DrawAndFadeInMainMenu(REDRAW_FIELD);
14814 FadeOut(REDRAW_FIELD);
14817 game_status = GAME_MODE_MAIN;
14819 DrawAndFadeInMainMenu(REDRAW_FIELD);
14823 else /* continue playing the game */
14825 if (tape.playing && tape.deactivate_display)
14826 TapeDeactivateDisplayOff(TRUE);
14828 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14830 if (tape.playing && tape.deactivate_display)
14831 TapeDeactivateDisplayOn();
14835 void RequestQuitGame(boolean ask_if_really_quit)
14837 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14838 boolean skip_request = AllPlayersGone || quick_quit;
14840 RequestQuitGameExt(skip_request, quick_quit,
14841 "Do you really want to quit the game ?");
14845 /* ------------------------------------------------------------------------- */
14846 /* random generator functions */
14847 /* ------------------------------------------------------------------------- */
14849 unsigned int InitEngineRandom_RND(long seed)
14851 game.num_random_calls = 0;
14854 unsigned int rnd_seed = InitEngineRandom(seed);
14856 printf("::: START RND: %d\n", rnd_seed);
14861 return InitEngineRandom(seed);
14867 unsigned int RND(int max)
14871 game.num_random_calls++;
14873 return GetEngineRandom(max);
14880 /* ------------------------------------------------------------------------- */
14881 /* game engine snapshot handling functions */
14882 /* ------------------------------------------------------------------------- */
14884 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14886 struct EngineSnapshotInfo
14888 /* runtime values for custom element collect score */
14889 int collect_score[NUM_CUSTOM_ELEMENTS];
14891 /* runtime values for group element choice position */
14892 int choice_pos[NUM_GROUP_ELEMENTS];
14894 /* runtime values for belt position animations */
14895 int belt_graphic[4 * NUM_BELT_PARTS];
14896 int belt_anim_mode[4 * NUM_BELT_PARTS];
14899 struct EngineSnapshotNodeInfo
14906 static struct EngineSnapshotInfo engine_snapshot_rnd;
14907 static ListNode *engine_snapshot_list = NULL;
14908 static char *snapshot_level_identifier = NULL;
14909 static int snapshot_level_nr = -1;
14911 void FreeEngineSnapshot()
14913 while (engine_snapshot_list != NULL)
14914 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14917 setString(&snapshot_level_identifier, NULL);
14918 snapshot_level_nr = -1;
14921 static void SaveEngineSnapshotValues_RND()
14923 static int belt_base_active_element[4] =
14925 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14926 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14927 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14928 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14932 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14934 int element = EL_CUSTOM_START + i;
14936 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14939 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14941 int element = EL_GROUP_START + i;
14943 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14946 for (i = 0; i < 4; i++)
14948 for (j = 0; j < NUM_BELT_PARTS; j++)
14950 int element = belt_base_active_element[i] + j;
14951 int graphic = el2img(element);
14952 int anim_mode = graphic_info[graphic].anim_mode;
14954 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14955 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14960 static void LoadEngineSnapshotValues_RND()
14962 unsigned long num_random_calls = game.num_random_calls;
14965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14967 int element = EL_CUSTOM_START + i;
14969 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14972 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14974 int element = EL_GROUP_START + i;
14976 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14979 for (i = 0; i < 4; i++)
14981 for (j = 0; j < NUM_BELT_PARTS; j++)
14983 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14984 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14986 graphic_info[graphic].anim_mode = anim_mode;
14990 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14992 InitRND(tape.random_seed);
14993 for (i = 0; i < num_random_calls; i++)
14997 if (game.num_random_calls != num_random_calls)
14999 Error(ERR_INFO, "number of random calls out of sync");
15000 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15001 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15002 Error(ERR_EXIT, "this should not happen -- please debug");
15006 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15008 struct EngineSnapshotNodeInfo *bi =
15009 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15011 bi->buffer_orig = buffer;
15012 bi->buffer_copy = checked_malloc(size);
15015 memcpy(bi->buffer_copy, buffer, size);
15017 addNodeToList(&engine_snapshot_list, NULL, bi);
15020 void SaveEngineSnapshot()
15022 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15024 if (level_editor_test_game) /* do not save snapshots from editor */
15027 /* copy some special values to a structure better suited for the snapshot */
15029 SaveEngineSnapshotValues_RND();
15030 SaveEngineSnapshotValues_EM();
15032 /* save values stored in special snapshot structure */
15034 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15035 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15037 /* save further RND engine values */
15039 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15040 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15041 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15043 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15044 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15045 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15046 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15048 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15049 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15050 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15051 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15052 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15054 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15055 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15056 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15058 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15060 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15062 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15063 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15065 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15066 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15067 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15068 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15069 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15070 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15071 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15072 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15073 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15074 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15075 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15076 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15077 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15078 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15079 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15080 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15081 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15082 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15084 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15085 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15087 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15088 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15089 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15091 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15092 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15094 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15095 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15096 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15097 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15098 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15100 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15101 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15103 /* save level identification information */
15105 setString(&snapshot_level_identifier, leveldir_current->identifier);
15106 snapshot_level_nr = level_nr;
15109 ListNode *node = engine_snapshot_list;
15112 while (node != NULL)
15114 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15119 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15123 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15125 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15128 void LoadEngineSnapshot()
15130 ListNode *node = engine_snapshot_list;
15132 if (engine_snapshot_list == NULL)
15135 while (node != NULL)
15137 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15142 /* restore special values from snapshot structure */
15144 LoadEngineSnapshotValues_RND();
15145 LoadEngineSnapshotValues_EM();
15148 boolean CheckEngineSnapshot()
15150 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15151 snapshot_level_nr == level_nr);
15155 /* ---------- new game button stuff ---------------------------------------- */
15157 /* graphic position values for game buttons */
15158 #define GAME_BUTTON_XSIZE 30
15159 #define GAME_BUTTON_YSIZE 30
15160 #define GAME_BUTTON_XPOS 5
15161 #define GAME_BUTTON_YPOS 215
15162 #define SOUND_BUTTON_XPOS 5
15163 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15165 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15166 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15167 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15168 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15169 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15170 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15178 } gamebutton_info[NUM_GAME_BUTTONS] =
15182 &game.button.stop.x, &game.button.stop.y,
15183 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15188 &game.button.pause.x, &game.button.pause.y,
15189 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15190 GAME_CTRL_ID_PAUSE,
15194 &game.button.play.x, &game.button.play.y,
15195 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15200 &game.button.sound_music.x, &game.button.sound_music.y,
15201 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15202 SOUND_CTRL_ID_MUSIC,
15203 "background music on/off"
15206 &game.button.sound_loops.x, &game.button.sound_loops.y,
15207 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15208 SOUND_CTRL_ID_LOOPS,
15209 "sound loops on/off"
15212 &game.button.sound_simple.x,&game.button.sound_simple.y,
15213 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15214 SOUND_CTRL_ID_SIMPLE,
15215 "normal sounds on/off"
15219 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15224 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15225 GAME_CTRL_ID_PAUSE,
15229 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15234 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15235 SOUND_CTRL_ID_MUSIC,
15236 "background music on/off"
15239 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15240 SOUND_CTRL_ID_LOOPS,
15241 "sound loops on/off"
15244 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15245 SOUND_CTRL_ID_SIMPLE,
15246 "normal sounds on/off"
15251 void CreateGameButtons()
15255 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15257 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15258 struct GadgetInfo *gi;
15261 unsigned long event_mask;
15263 int gd_xoffset, gd_yoffset;
15264 int gd_x1, gd_x2, gd_y1, gd_y2;
15267 x = DX + *gamebutton_info[i].x;
15268 y = DY + *gamebutton_info[i].y;
15269 gd_xoffset = gamebutton_info[i].gd_x;
15270 gd_yoffset = gamebutton_info[i].gd_y;
15271 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15272 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15274 if (id == GAME_CTRL_ID_STOP ||
15275 id == GAME_CTRL_ID_PAUSE ||
15276 id == GAME_CTRL_ID_PLAY)
15278 button_type = GD_TYPE_NORMAL_BUTTON;
15280 event_mask = GD_EVENT_RELEASED;
15281 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15282 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15286 button_type = GD_TYPE_CHECK_BUTTON;
15288 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15289 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15290 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15291 event_mask = GD_EVENT_PRESSED;
15292 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15293 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15296 gi = CreateGadget(GDI_CUSTOM_ID, id,
15297 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15302 GDI_X, DX + gd_xoffset,
15303 GDI_Y, DY + gd_yoffset,
15305 GDI_WIDTH, GAME_BUTTON_XSIZE,
15306 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15307 GDI_TYPE, button_type,
15308 GDI_STATE, GD_BUTTON_UNPRESSED,
15309 GDI_CHECKED, checked,
15310 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15311 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15312 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15313 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15314 GDI_EVENT_MASK, event_mask,
15315 GDI_CALLBACK_ACTION, HandleGameButtons,
15319 Error(ERR_EXIT, "cannot create gadget");
15321 game_gadget[id] = gi;
15325 void FreeGameButtons()
15329 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15330 FreeGadget(game_gadget[i]);
15333 static void MapGameButtons()
15337 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15338 MapGadget(game_gadget[i]);
15341 void UnmapGameButtons()
15345 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15346 UnmapGadget(game_gadget[i]);
15349 static void HandleGameButtons(struct GadgetInfo *gi)
15351 int id = gi->custom_id;
15353 if (game_status != GAME_MODE_PLAYING)
15358 case GAME_CTRL_ID_STOP:
15362 RequestQuitGame(TRUE);
15365 case GAME_CTRL_ID_PAUSE:
15366 if (options.network)
15368 #if defined(NETWORK_AVALIABLE)
15370 SendToServer_ContinuePlaying();
15372 SendToServer_PausePlaying();
15376 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15379 case GAME_CTRL_ID_PLAY:
15382 #if defined(NETWORK_AVALIABLE)
15383 if (options.network)
15384 SendToServer_ContinuePlaying();
15388 tape.pausing = FALSE;
15389 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15394 case SOUND_CTRL_ID_MUSIC:
15395 if (setup.sound_music)
15397 setup.sound_music = FALSE;
15400 else if (audio.music_available)
15402 setup.sound = setup.sound_music = TRUE;
15404 SetAudioMode(setup.sound);
15410 case SOUND_CTRL_ID_LOOPS:
15411 if (setup.sound_loops)
15412 setup.sound_loops = FALSE;
15413 else if (audio.loops_available)
15415 setup.sound = setup.sound_loops = TRUE;
15416 SetAudioMode(setup.sound);
15420 case SOUND_CTRL_ID_SIMPLE:
15421 if (setup.sound_simple)
15422 setup.sound_simple = FALSE;
15423 else if (audio.sound_available)
15425 setup.sound = setup.sound_simple = TRUE;
15426 SetAudioMode(setup.sound);