1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_CONTROL_LEVEL_NUMBER 0
135 #define GAME_CONTROL_GEMS 1
136 #define GAME_CONTROL_INVENTORY 2
137 #define GAME_CONTROL_KEY_1 3
138 #define GAME_CONTROL_KEY_2 4
139 #define GAME_CONTROL_KEY_3 5
140 #define GAME_CONTROL_KEY_4 6
141 #define GAME_CONTROL_KEY_5 7
142 #define GAME_CONTROL_KEY_6 8
143 #define GAME_CONTROL_KEY_7 9
144 #define GAME_CONTROL_KEY_8 10
145 #define GAME_CONTROL_KEY_WHITE 11
146 #define GAME_CONTROL_KEY_WHITE_COUNT 12
147 #define GAME_CONTROL_SCORE 13
148 #define GAME_CONTROL_TIME 14
149 #define GAME_CONTROL_TIME_HH 15
150 #define GAME_CONTROL_TIME_MM 16
151 #define GAME_CONTROL_TIME_SS 17
152 #define GAME_CONTROL_DROP_NEXT_1 18
153 #define GAME_CONTROL_DROP_NEXT_2 19
154 #define GAME_CONTROL_DROP_NEXT_3 20
155 #define GAME_CONTROL_DROP_NEXT_4 21
156 #define GAME_CONTROL_DROP_NEXT_5 22
157 #define GAME_CONTROL_DROP_NEXT_6 23
158 #define GAME_CONTROL_DROP_NEXT_7 24
159 #define GAME_CONTROL_DROP_NEXT_8 25
160 #define GAME_CONTROL_SHIELD_NORMAL 26
161 #define GAME_CONTROL_SHIELD_NORMAL_TIME 27
162 #define GAME_CONTROL_SHIELD_DEADLY 28
163 #define GAME_CONTROL_SHIELD_DEADLY_TIME 29
164 #define GAME_CONTROL_EXIT 30
165 #define GAME_CONTROL_EM_EXIT 31
166 #define GAME_CONTROL_SP_EXIT 32
167 #define GAME_CONTROL_STEEL_EXIT 33
168 #define GAME_CONTROL_EM_STEEL_EXIT 34
169 #define GAME_CONTROL_EMC_MAGIC_BALL 35
170 #define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH 36
171 #define GAME_CONTROL_LIGHT_SWITCH 37
172 #define GAME_CONTROL_LIGHT_SWITCH_TIME 38
173 #define GAME_CONTROL_TIMEGATE_SWITCH 39
174 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME 40
175 #define GAME_CONTROL_SWITCHGATE_SWITCH 41
176 #define GAME_CONTROL_EMC_LENSES 42
177 #define GAME_CONTROL_EMC_LENSES_TIME 43
178 #define GAME_CONTROL_EMC_MAGNIFIER 44
179 #define GAME_CONTROL_EMC_MAGNIFIER_TIME 45
180 #define GAME_CONTROL_BALLOON_SWITCH 46
181 #define GAME_CONTROL_DYNABOMB_NUMBER 47
182 #define GAME_CONTROL_DYNABOMB_SIZE 48
183 #define GAME_CONTROL_DYNABOMB_POWER 49
184 #define GAME_CONTROL_PENGUINS 50
185 #define GAME_CONTROL_SOKOBAN_OBJECTS 51
186 #define GAME_CONTROL_SOKOBAN_FIELDS 52
187 #define GAME_CONTROL_ROBOT_WHEEL 53
188 #define GAME_CONTROL_CONVEYOR_BELT_1 54
189 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 55
190 #define GAME_CONTROL_CONVEYOR_BELT_2 56
191 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 57
192 #define GAME_CONTROL_CONVEYOR_BELT_3 58
193 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 59
194 #define GAME_CONTROL_CONVEYOR_BELT_4 60
195 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 61
196 #define GAME_CONTROL_MAGIC_WALL 62
197 #define GAME_CONTROL_MAGIC_WALL_TIME 63
198 #define GAME_CONTROL_BD_MAGIC_WALL 64
199 #define GAME_CONTROL_DC_MAGIC_WALL 65
200 #define GAME_CONTROL_PLAYER_NAME 66
201 #define GAME_CONTROL_LEVEL_NAME 67
202 #define GAME_CONTROL_LEVEL_AUTHOR 68
204 #define NUM_GAME_CONTROLS 69
206 int game_control_value[NUM_GAME_CONTROLS];
207 int last_game_control_value[NUM_GAME_CONTROLS];
209 struct GameControlInfo
213 struct TextPosInfo *pos;
217 static struct GameControlInfo game_controls[] =
220 GAME_CONTROL_LEVEL_NUMBER,
221 &game.panel.level_number,
230 GAME_CONTROL_INVENTORY,
231 &game.panel.inventory,
275 GAME_CONTROL_KEY_WHITE,
276 &game.panel.key_white,
280 GAME_CONTROL_KEY_WHITE_COUNT,
281 &game.panel.key_white_count,
295 GAME_CONTROL_TIME_HH,
300 GAME_CONTROL_TIME_MM,
305 GAME_CONTROL_TIME_SS,
310 GAME_CONTROL_DROP_NEXT_1,
311 &game.panel.drop_next_1,
315 GAME_CONTROL_DROP_NEXT_2,
316 &game.panel.drop_next_2,
320 GAME_CONTROL_DROP_NEXT_3,
321 &game.panel.drop_next_3,
325 GAME_CONTROL_DROP_NEXT_4,
326 &game.panel.drop_next_4,
330 GAME_CONTROL_DROP_NEXT_5,
331 &game.panel.drop_next_5,
335 GAME_CONTROL_DROP_NEXT_6,
336 &game.panel.drop_next_6,
340 GAME_CONTROL_DROP_NEXT_7,
341 &game.panel.drop_next_7,
345 GAME_CONTROL_DROP_NEXT_8,
346 &game.panel.drop_next_8,
350 GAME_CONTROL_SHIELD_NORMAL,
351 &game.panel.shield_normal,
355 GAME_CONTROL_SHIELD_NORMAL_TIME,
356 &game.panel.shield_normal_time,
360 GAME_CONTROL_SHIELD_DEADLY,
361 &game.panel.shield_deadly,
365 GAME_CONTROL_SHIELD_DEADLY_TIME,
366 &game.panel.shield_deadly_time,
375 GAME_CONTROL_EM_EXIT,
380 GAME_CONTROL_SP_EXIT,
385 GAME_CONTROL_STEEL_EXIT,
386 &game.panel.steel_exit,
390 GAME_CONTROL_EM_STEEL_EXIT,
391 &game.panel.em_steel_exit,
395 GAME_CONTROL_EMC_MAGIC_BALL,
396 &game.panel.emc_magic_ball,
400 GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
401 &game.panel.emc_magic_ball_switch,
405 GAME_CONTROL_LIGHT_SWITCH,
406 &game.panel.light_switch,
410 GAME_CONTROL_LIGHT_SWITCH_TIME,
411 &game.panel.light_switch_time,
415 GAME_CONTROL_TIMEGATE_SWITCH,
416 &game.panel.timegate_switch,
420 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
421 &game.panel.timegate_switch_time,
425 GAME_CONTROL_SWITCHGATE_SWITCH,
426 &game.panel.switchgate_switch,
430 GAME_CONTROL_EMC_LENSES,
431 &game.panel.emc_lenses,
435 GAME_CONTROL_EMC_LENSES_TIME,
436 &game.panel.emc_lenses_time,
440 GAME_CONTROL_EMC_MAGNIFIER,
441 &game.panel.emc_magnifier,
445 GAME_CONTROL_EMC_MAGNIFIER_TIME,
446 &game.panel.emc_magnifier_time,
450 GAME_CONTROL_BALLOON_SWITCH,
451 &game.panel.balloon_switch,
455 GAME_CONTROL_DYNABOMB_NUMBER,
456 &game.panel.dynabomb_number,
460 GAME_CONTROL_DYNABOMB_SIZE,
461 &game.panel.dynabomb_size,
465 GAME_CONTROL_DYNABOMB_POWER,
466 &game.panel.dynabomb_power,
470 GAME_CONTROL_PENGUINS,
471 &game.panel.penguins,
475 GAME_CONTROL_SOKOBAN_OBJECTS,
476 &game.panel.sokoban_objects,
480 GAME_CONTROL_SOKOBAN_FIELDS,
481 &game.panel.sokoban_fields,
485 GAME_CONTROL_ROBOT_WHEEL,
486 &game.panel.robot_wheel,
490 GAME_CONTROL_CONVEYOR_BELT_1,
491 &game.panel.conveyor_belt_1,
495 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
496 &game.panel.conveyor_belt_1_switch,
500 GAME_CONTROL_CONVEYOR_BELT_2,
501 &game.panel.conveyor_belt_2,
505 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
506 &game.panel.conveyor_belt_2_switch,
510 GAME_CONTROL_CONVEYOR_BELT_3,
511 &game.panel.conveyor_belt_3,
515 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
516 &game.panel.conveyor_belt_3_switch,
520 GAME_CONTROL_CONVEYOR_BELT_4,
521 &game.panel.conveyor_belt_4,
525 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
526 &game.panel.conveyor_belt_4_switch,
530 GAME_CONTROL_MAGIC_WALL,
531 &game.panel.magic_wall,
535 GAME_CONTROL_MAGIC_WALL_TIME,
536 &game.panel.magic_wall_time,
540 GAME_CONTROL_BD_MAGIC_WALL,
541 &game.panel.bd_magic_wall,
545 GAME_CONTROL_DC_MAGIC_WALL,
546 &game.panel.dc_magic_wall,
550 GAME_CONTROL_PLAYER_NAME,
551 &game.panel.player_name,
555 GAME_CONTROL_LEVEL_NAME,
556 &game.panel.level_name,
560 GAME_CONTROL_LEVEL_AUTHOR,
561 &game.panel.level_author,
574 /* values for delayed check of falling and moving elements and for collision */
575 #define CHECK_DELAY_MOVING 3
576 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
577 #define CHECK_DELAY_COLLISION 2
578 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
580 /* values for initial player move delay (initial delay counter value) */
581 #define INITIAL_MOVE_DELAY_OFF -1
582 #define INITIAL_MOVE_DELAY_ON 0
584 /* values for player movement speed (which is in fact a delay value) */
585 #define MOVE_DELAY_MIN_SPEED 32
586 #define MOVE_DELAY_NORMAL_SPEED 8
587 #define MOVE_DELAY_HIGH_SPEED 4
588 #define MOVE_DELAY_MAX_SPEED 1
590 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
591 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
593 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
594 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
596 /* values for other actions */
597 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
598 #define MOVE_STEPSIZE_MIN (1)
599 #define MOVE_STEPSIZE_MAX (TILEX)
601 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
602 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
604 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
606 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
607 RND(element_info[e].push_delay_random))
608 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
609 RND(element_info[e].drop_delay_random))
610 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
611 RND(element_info[e].move_delay_random))
612 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
613 (element_info[e].move_delay_random))
614 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
615 RND(element_info[e].ce_value_random_initial))
616 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
617 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
618 RND((c)->delay_random * (c)->delay_frames))
619 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
620 RND((c)->delay_random))
623 #define GET_VALID_RUNTIME_ELEMENT(e) \
624 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
626 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
627 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
628 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
629 (be) + (e) - EL_SELF)
631 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
632 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
633 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
634 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
635 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
636 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
637 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
638 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
639 RESOLVED_REFERENCE_ELEMENT(be, e) : \
642 #define CAN_GROW_INTO(e) \
643 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
645 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
646 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
649 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
650 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
651 (CAN_MOVE_INTO_ACID(e) && \
652 Feld[x][y] == EL_ACID) || \
655 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
656 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
657 (CAN_MOVE_INTO_ACID(e) && \
658 Feld[x][y] == EL_ACID) || \
661 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
662 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
664 (CAN_MOVE_INTO_ACID(e) && \
665 Feld[x][y] == EL_ACID) || \
666 (DONT_COLLIDE_WITH(e) && \
668 !PLAYER_ENEMY_PROTECTED(x, y))))
670 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
671 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
673 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
674 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
676 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
677 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
679 #define ANDROID_CAN_CLONE_FIELD(x, y) \
680 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
681 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
683 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
684 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
686 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
687 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
689 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
690 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
692 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
693 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
695 #define PIG_CAN_ENTER_FIELD(e, x, y) \
696 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
698 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
699 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
700 Feld[x][y] == EL_EM_EXIT_OPEN || \
701 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
702 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
703 IS_FOOD_PENGUIN(Feld[x][y])))
704 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
705 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
707 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
708 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
710 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
711 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
713 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
714 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
715 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
717 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
719 #define CE_ENTER_FIELD_COND(e, x, y) \
720 (!IS_PLAYER(x, y) && \
721 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
723 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
724 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
726 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
727 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
729 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
730 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
731 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
732 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
734 /* game button identifiers */
735 #define GAME_CTRL_ID_STOP 0
736 #define GAME_CTRL_ID_PAUSE 1
737 #define GAME_CTRL_ID_PLAY 2
738 #define SOUND_CTRL_ID_MUSIC 3
739 #define SOUND_CTRL_ID_LOOPS 4
740 #define SOUND_CTRL_ID_SIMPLE 5
742 #define NUM_GAME_BUTTONS 6
745 /* forward declaration for internal use */
747 static void CreateField(int, int, int);
749 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
750 static void AdvanceFrameAndPlayerCounters(int);
752 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
753 static boolean MovePlayer(struct PlayerInfo *, int, int);
754 static void ScrollPlayer(struct PlayerInfo *, int);
755 static void ScrollScreen(struct PlayerInfo *, int);
757 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
759 static void InitBeltMovement(void);
760 static void CloseAllOpenTimegates(void);
761 static void CheckGravityMovement(struct PlayerInfo *);
762 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
763 static void KillPlayerUnlessEnemyProtected(int, int);
764 static void KillPlayerUnlessExplosionProtected(int, int);
766 static void TestIfPlayerTouchesCustomElement(int, int);
767 static void TestIfElementTouchesCustomElement(int, int);
768 static void TestIfElementHitsCustomElement(int, int, int);
770 static void TestIfElementSmashesCustomElement(int, int, int);
773 static void HandleElementChange(int, int, int);
774 static void ExecuteCustomElementAction(int, int, int, int);
775 static boolean ChangeElement(int, int, int, int);
777 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
778 #define CheckTriggeredElementChange(x, y, e, ev) \
779 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
780 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
781 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
782 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
783 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
784 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
785 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
787 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
788 #define CheckElementChange(x, y, e, te, ev) \
789 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
790 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
791 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
792 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
793 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
795 static void PlayLevelSound(int, int, int);
796 static void PlayLevelSoundNearest(int, int, int);
797 static void PlayLevelSoundAction(int, int, int);
798 static void PlayLevelSoundElementAction(int, int, int, int);
799 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
800 static void PlayLevelSoundActionIfLoop(int, int, int);
801 static void StopLevelSoundActionIfLoop(int, int, int);
802 static void PlayLevelMusic();
804 static void MapGameButtons();
805 static void HandleGameButtons(struct GadgetInfo *);
807 int AmoebeNachbarNr(int, int);
808 void AmoebeUmwandeln(int, int);
809 void ContinueMoving(int, int);
811 void InitMovDir(int, int);
812 void InitAmoebaNr(int, int);
813 int NewHiScore(void);
815 void TestIfGoodThingHitsBadThing(int, int, int);
816 void TestIfBadThingHitsGoodThing(int, int, int);
817 void TestIfPlayerTouchesBadThing(int, int);
818 void TestIfPlayerRunsIntoBadThing(int, int, int);
819 void TestIfBadThingTouchesPlayer(int, int);
820 void TestIfBadThingRunsIntoPlayer(int, int, int);
821 void TestIfFriendTouchesBadThing(int, int);
822 void TestIfBadThingTouchesFriend(int, int);
823 void TestIfBadThingTouchesOtherBadThing(int, int);
825 void KillPlayer(struct PlayerInfo *);
826 void BuryPlayer(struct PlayerInfo *);
827 void RemovePlayer(struct PlayerInfo *);
829 boolean SnapField(struct PlayerInfo *, int, int);
830 boolean DropElement(struct PlayerInfo *);
832 static int getInvisibleActiveFromInvisibleElement(int);
833 static int getInvisibleFromInvisibleActiveElement(int);
835 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
837 /* for detection of endless loops, caused by custom element programming */
838 /* (using maximal playfield width x 10 is just a rough approximation) */
839 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
841 #define RECURSION_LOOP_DETECTION_START(e, rc) \
843 if (recursion_loop_detected) \
846 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
848 recursion_loop_detected = TRUE; \
849 recursion_loop_element = (e); \
852 recursion_loop_depth++; \
855 #define RECURSION_LOOP_DETECTION_END() \
857 recursion_loop_depth--; \
860 static int recursion_loop_depth;
861 static boolean recursion_loop_detected;
862 static boolean recursion_loop_element;
865 /* ------------------------------------------------------------------------- */
866 /* definition of elements that automatically change to other elements after */
867 /* a specified time, eventually calling a function when changing */
868 /* ------------------------------------------------------------------------- */
870 /* forward declaration for changer functions */
871 static void InitBuggyBase(int, int);
872 static void WarnBuggyBase(int, int);
874 static void InitTrap(int, int);
875 static void ActivateTrap(int, int);
876 static void ChangeActiveTrap(int, int);
878 static void InitRobotWheel(int, int);
879 static void RunRobotWheel(int, int);
880 static void StopRobotWheel(int, int);
882 static void InitTimegateWheel(int, int);
883 static void RunTimegateWheel(int, int);
885 static void InitMagicBallDelay(int, int);
886 static void ActivateMagicBall(int, int);
888 struct ChangingElementInfo
893 void (*pre_change_function)(int x, int y);
894 void (*change_function)(int x, int y);
895 void (*post_change_function)(int x, int y);
898 static struct ChangingElementInfo change_delay_list[] =
933 EL_STEEL_EXIT_OPENING,
941 EL_STEEL_EXIT_CLOSING,
942 EL_STEEL_EXIT_CLOSED,
969 EL_EM_STEEL_EXIT_OPENING,
970 EL_EM_STEEL_EXIT_OPEN,
977 EL_EM_STEEL_EXIT_CLOSING,
981 EL_EM_STEEL_EXIT_CLOSED,
1005 EL_SWITCHGATE_OPENING,
1013 EL_SWITCHGATE_CLOSING,
1014 EL_SWITCHGATE_CLOSED,
1021 EL_TIMEGATE_OPENING,
1029 EL_TIMEGATE_CLOSING,
1038 EL_ACID_SPLASH_LEFT,
1046 EL_ACID_SPLASH_RIGHT,
1055 EL_SP_BUGGY_BASE_ACTIVATING,
1062 EL_SP_BUGGY_BASE_ACTIVATING,
1063 EL_SP_BUGGY_BASE_ACTIVE,
1070 EL_SP_BUGGY_BASE_ACTIVE,
1094 EL_ROBOT_WHEEL_ACTIVE,
1102 EL_TIMEGATE_SWITCH_ACTIVE,
1110 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1111 EL_DC_TIMEGATE_SWITCH,
1118 EL_EMC_MAGIC_BALL_ACTIVE,
1119 EL_EMC_MAGIC_BALL_ACTIVE,
1126 EL_EMC_SPRING_BUMPER_ACTIVE,
1127 EL_EMC_SPRING_BUMPER,
1134 EL_DIAGONAL_SHRINKING,
1142 EL_DIAGONAL_GROWING,
1163 int push_delay_fixed, push_delay_random;
1167 { EL_SPRING, 0, 0 },
1168 { EL_BALLOON, 0, 0 },
1170 { EL_SOKOBAN_OBJECT, 2, 0 },
1171 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1172 { EL_SATELLITE, 2, 0 },
1173 { EL_SP_DISK_YELLOW, 2, 0 },
1175 { EL_UNDEFINED, 0, 0 },
1183 move_stepsize_list[] =
1185 { EL_AMOEBA_DROP, 2 },
1186 { EL_AMOEBA_DROPPING, 2 },
1187 { EL_QUICKSAND_FILLING, 1 },
1188 { EL_QUICKSAND_EMPTYING, 1 },
1189 { EL_QUICKSAND_FAST_FILLING, 2 },
1190 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1191 { EL_MAGIC_WALL_FILLING, 2 },
1192 { EL_MAGIC_WALL_EMPTYING, 2 },
1193 { EL_BD_MAGIC_WALL_FILLING, 2 },
1194 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1195 { EL_DC_MAGIC_WALL_FILLING, 2 },
1196 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1198 { EL_UNDEFINED, 0 },
1206 collect_count_list[] =
1209 { EL_BD_DIAMOND, 1 },
1210 { EL_EMERALD_YELLOW, 1 },
1211 { EL_EMERALD_RED, 1 },
1212 { EL_EMERALD_PURPLE, 1 },
1214 { EL_SP_INFOTRON, 1 },
1218 { EL_UNDEFINED, 0 },
1226 access_direction_list[] =
1228 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1229 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1230 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1231 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1232 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1233 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1234 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1235 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1236 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1237 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1238 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1240 { EL_SP_PORT_LEFT, MV_RIGHT },
1241 { EL_SP_PORT_RIGHT, MV_LEFT },
1242 { EL_SP_PORT_UP, MV_DOWN },
1243 { EL_SP_PORT_DOWN, MV_UP },
1244 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1245 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1246 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1247 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1248 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1249 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1250 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1251 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1252 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1253 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1254 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1255 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1256 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1257 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1258 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1260 { EL_UNDEFINED, MV_NONE }
1263 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1265 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1266 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1267 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1268 IS_JUST_CHANGING(x, y))
1270 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1272 /* static variables for playfield scan mode (scanning forward or backward) */
1273 static int playfield_scan_start_x = 0;
1274 static int playfield_scan_start_y = 0;
1275 static int playfield_scan_delta_x = 1;
1276 static int playfield_scan_delta_y = 1;
1278 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1279 (y) >= 0 && (y) <= lev_fieldy - 1; \
1280 (y) += playfield_scan_delta_y) \
1281 for ((x) = playfield_scan_start_x; \
1282 (x) >= 0 && (x) <= lev_fieldx - 1; \
1283 (x) += playfield_scan_delta_x)
1286 void DEBUG_SetMaximumDynamite()
1290 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1291 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1292 local_player->inventory_element[local_player->inventory_size++] =
1297 static void InitPlayfieldScanModeVars()
1299 if (game.use_reverse_scan_direction)
1301 playfield_scan_start_x = lev_fieldx - 1;
1302 playfield_scan_start_y = lev_fieldy - 1;
1304 playfield_scan_delta_x = -1;
1305 playfield_scan_delta_y = -1;
1309 playfield_scan_start_x = 0;
1310 playfield_scan_start_y = 0;
1312 playfield_scan_delta_x = 1;
1313 playfield_scan_delta_y = 1;
1317 static void InitPlayfieldScanMode(int mode)
1319 game.use_reverse_scan_direction =
1320 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1322 InitPlayfieldScanModeVars();
1325 static int get_move_delay_from_stepsize(int move_stepsize)
1328 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1330 /* make sure that stepsize value is always a power of 2 */
1331 move_stepsize = (1 << log_2(move_stepsize));
1333 return TILEX / move_stepsize;
1336 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1339 int player_nr = player->index_nr;
1340 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1341 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1343 /* do no immediately change move delay -- the player might just be moving */
1344 player->move_delay_value_next = move_delay;
1346 /* information if player can move must be set separately */
1347 player->cannot_move = cannot_move;
1351 player->move_delay = game.initial_move_delay[player_nr];
1352 player->move_delay_value = game.initial_move_delay_value[player_nr];
1354 player->move_delay_value_next = -1;
1356 player->move_delay_reset_counter = 0;
1360 void GetPlayerConfig()
1362 GameFrameDelay = setup.game_frame_delay;
1364 if (!audio.sound_available)
1365 setup.sound_simple = FALSE;
1367 if (!audio.loops_available)
1368 setup.sound_loops = FALSE;
1370 if (!audio.music_available)
1371 setup.sound_music = FALSE;
1373 if (!video.fullscreen_available)
1374 setup.fullscreen = FALSE;
1376 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1378 SetAudioMode(setup.sound);
1382 int GetElementFromGroupElement(int element)
1384 if (IS_GROUP_ELEMENT(element))
1386 struct ElementGroupInfo *group = element_info[element].group;
1387 int last_anim_random_frame = gfx.anim_random_frame;
1390 if (group->choice_mode == ANIM_RANDOM)
1391 gfx.anim_random_frame = RND(group->num_elements_resolved);
1393 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1394 group->choice_mode, 0,
1397 if (group->choice_mode == ANIM_RANDOM)
1398 gfx.anim_random_frame = last_anim_random_frame;
1400 group->choice_pos++;
1402 element = group->element_resolved[element_pos];
1408 static void InitPlayerField(int x, int y, int element, boolean init_game)
1410 if (element == EL_SP_MURPHY)
1414 if (stored_player[0].present)
1416 Feld[x][y] = EL_SP_MURPHY_CLONE;
1422 stored_player[0].use_murphy = TRUE;
1424 if (!level.use_artwork_element[0])
1425 stored_player[0].artwork_element = EL_SP_MURPHY;
1428 Feld[x][y] = EL_PLAYER_1;
1434 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1435 int jx = player->jx, jy = player->jy;
1437 player->present = TRUE;
1439 player->block_last_field = (element == EL_SP_MURPHY ?
1440 level.sp_block_last_field :
1441 level.block_last_field);
1443 /* ---------- initialize player's last field block delay --------------- */
1445 /* always start with reliable default value (no adjustment needed) */
1446 player->block_delay_adjustment = 0;
1448 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1449 if (player->block_last_field && element == EL_SP_MURPHY)
1450 player->block_delay_adjustment = 1;
1452 /* special case 2: in game engines before 3.1.1, blocking was different */
1453 if (game.use_block_last_field_bug)
1454 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1456 if (!options.network || player->connected)
1458 player->active = TRUE;
1460 /* remove potentially duplicate players */
1461 if (StorePlayer[jx][jy] == Feld[x][y])
1462 StorePlayer[jx][jy] = 0;
1464 StorePlayer[x][y] = Feld[x][y];
1468 printf("Player %d activated.\n", player->element_nr);
1469 printf("[Local player is %d and currently %s.]\n",
1470 local_player->element_nr,
1471 local_player->active ? "active" : "not active");
1475 Feld[x][y] = EL_EMPTY;
1477 player->jx = player->last_jx = x;
1478 player->jy = player->last_jy = y;
1482 static void InitField(int x, int y, boolean init_game)
1484 int element = Feld[x][y];
1493 InitPlayerField(x, y, element, init_game);
1496 case EL_SOKOBAN_FIELD_PLAYER:
1497 element = Feld[x][y] = EL_PLAYER_1;
1498 InitField(x, y, init_game);
1500 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1501 InitField(x, y, init_game);
1504 case EL_SOKOBAN_FIELD_EMPTY:
1505 local_player->sokobanfields_still_needed++;
1509 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1510 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1511 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1512 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1513 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1514 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1515 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1516 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1517 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1518 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1527 case EL_SPACESHIP_RIGHT:
1528 case EL_SPACESHIP_UP:
1529 case EL_SPACESHIP_LEFT:
1530 case EL_SPACESHIP_DOWN:
1531 case EL_BD_BUTTERFLY:
1532 case EL_BD_BUTTERFLY_RIGHT:
1533 case EL_BD_BUTTERFLY_UP:
1534 case EL_BD_BUTTERFLY_LEFT:
1535 case EL_BD_BUTTERFLY_DOWN:
1537 case EL_BD_FIREFLY_RIGHT:
1538 case EL_BD_FIREFLY_UP:
1539 case EL_BD_FIREFLY_LEFT:
1540 case EL_BD_FIREFLY_DOWN:
1541 case EL_PACMAN_RIGHT:
1543 case EL_PACMAN_LEFT:
1544 case EL_PACMAN_DOWN:
1546 case EL_YAMYAM_LEFT:
1547 case EL_YAMYAM_RIGHT:
1549 case EL_YAMYAM_DOWN:
1550 case EL_DARK_YAMYAM:
1553 case EL_SP_SNIKSNAK:
1554 case EL_SP_ELECTRON:
1563 case EL_AMOEBA_FULL:
1568 case EL_AMOEBA_DROP:
1569 if (y == lev_fieldy - 1)
1571 Feld[x][y] = EL_AMOEBA_GROWING;
1572 Store[x][y] = EL_AMOEBA_WET;
1576 case EL_DYNAMITE_ACTIVE:
1577 case EL_SP_DISK_RED_ACTIVE:
1578 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1579 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1580 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1581 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1582 MovDelay[x][y] = 96;
1585 case EL_EM_DYNAMITE_ACTIVE:
1586 MovDelay[x][y] = 32;
1590 local_player->lights_still_needed++;
1594 local_player->friends_still_needed++;
1599 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1602 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1603 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1604 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1605 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1606 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1607 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1608 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1609 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1610 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1611 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1612 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1613 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1616 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1617 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1618 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1620 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1622 game.belt_dir[belt_nr] = belt_dir;
1623 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1625 else /* more than one switch -- set it like the first switch */
1627 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1632 #if !USE_BOTH_SWITCHGATE_SWITCHES
1633 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1635 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1638 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1640 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1644 case EL_LIGHT_SWITCH_ACTIVE:
1646 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1649 case EL_INVISIBLE_STEELWALL:
1650 case EL_INVISIBLE_WALL:
1651 case EL_INVISIBLE_SAND:
1652 if (game.light_time_left > 0 ||
1653 game.lenses_time_left > 0)
1654 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1657 case EL_EMC_MAGIC_BALL:
1658 if (game.ball_state)
1659 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1662 case EL_EMC_MAGIC_BALL_SWITCH:
1663 if (game.ball_state)
1664 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1668 if (IS_CUSTOM_ELEMENT(element))
1670 if (CAN_MOVE(element))
1673 #if USE_NEW_CUSTOM_VALUE
1674 if (!element_info[element].use_last_ce_value || init_game)
1675 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1678 else if (IS_GROUP_ELEMENT(element))
1680 Feld[x][y] = GetElementFromGroupElement(element);
1682 InitField(x, y, init_game);
1689 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1692 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1694 InitField(x, y, init_game);
1696 /* not needed to call InitMovDir() -- already done by InitField()! */
1697 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1698 CAN_MOVE(Feld[x][y]))
1702 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1704 int old_element = Feld[x][y];
1706 InitField(x, y, init_game);
1708 /* not needed to call InitMovDir() -- already done by InitField()! */
1709 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1710 CAN_MOVE(old_element) &&
1711 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1714 /* this case is in fact a combination of not less than three bugs:
1715 first, it calls InitMovDir() for elements that can move, although this is
1716 already done by InitField(); then, it checks the element that was at this
1717 field _before_ the call to InitField() (which can change it); lastly, it
1718 was not called for "mole with direction" elements, which were treated as
1719 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1725 void InitGameControlValues()
1729 for (i = 0; i < NUM_GAME_CONTROLS; i++)
1730 game_control_value[i] = last_game_control_value[i] = -1;
1732 for (i = 0; game_controls[i].nr != -1; i++)
1734 int nr = game_controls[i].nr;
1735 int type = game_controls[i].type;
1736 struct TextPosInfo *pos = game_controls[i].pos;
1738 game_control_value[nr] = last_game_control_value[nr] = -1;
1740 /* determine panel value width for later calculation of alignment */
1741 if (type == TYPE_INTEGER || type == TYPE_STRING)
1742 pos->width = pos->chars * getFontWidth(pos->font);
1743 else if (type == TYPE_ELEMENT)
1744 pos->width = MINI_TILESIZE;
1748 void UpdateGameControlValues()
1752 game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1753 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1755 game_control_value[GAME_CONTROL_INVENTORY] = 0;
1756 for (i = 0; i < MAX_NUM_KEYS; i++)
1757 game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
1758 game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
1759 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1761 if (game.centered_player_nr == -1)
1763 for (i = 0; i < MAX_PLAYERS; i++)
1765 for (j = 0; j < MAX_NUM_KEYS; j++)
1766 if (stored_player[i].key[j])
1767 game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
1769 game_control_value[GAME_CONTROL_INVENTORY] +=
1770 stored_player[i].inventory_size;
1772 if (stored_player[i].num_white_keys > 0)
1773 game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1775 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1776 stored_player[i].num_white_keys;
1781 int player_nr = game.centered_player_nr;
1783 for (i = 0; i < MAX_NUM_KEYS; i++)
1784 if (stored_player[player_nr].key[i])
1785 game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
1787 game_control_value[GAME_CONTROL_INVENTORY] +=
1788 stored_player[player_nr].inventory_size;
1790 if (stored_player[player_nr].num_white_keys > 0)
1791 game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1793 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1794 stored_player[player_nr].num_white_keys;
1797 game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
1798 local_player->score_final :
1799 local_player->score);
1801 game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
1805 game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
1806 game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
1807 game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
1809 for (i = 0; i < 8; i++)
1810 game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED;
1812 game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1813 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1815 game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1816 local_player->shield_normal_time_left;
1817 game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1818 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1820 game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1821 local_player->shield_deadly_time_left;
1823 if (local_player->gems_still_needed > 0 ||
1824 local_player->sokobanfields_still_needed > 0 ||
1825 local_player->lights_still_needed > 0)
1827 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_CLOSED;
1828 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_CLOSED;
1829 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_CLOSED;
1830 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_CLOSED;
1831 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
1835 game_control_value[GAME_CONTROL_EXIT] = EL_EXIT_OPEN;
1836 game_control_value[GAME_CONTROL_EM_EXIT] = EL_EM_EXIT_OPEN;
1837 game_control_value[GAME_CONTROL_SP_EXIT] = EL_SP_EXIT_OPEN;
1838 game_control_value[GAME_CONTROL_STEEL_EXIT] = EL_STEEL_EXIT_OPEN;
1839 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
1842 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
1843 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
1845 game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
1846 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1847 game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1849 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
1850 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1851 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1852 game.timegate_time_left;
1854 game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
1856 game_control_value[GAME_CONTROL_EMC_LENSES] =
1857 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1858 game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1860 game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
1861 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1862 game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1864 game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
1865 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
1866 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1867 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
1868 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
1869 EL_BALLOON_SWITCH_NONE);
1871 game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1872 local_player->dynabomb_count;
1873 game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1874 local_player->dynabomb_size;
1875 game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1876 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1878 game_control_value[GAME_CONTROL_PENGUINS] =
1879 local_player->friends_still_needed;
1881 game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1882 local_player->sokobanfields_still_needed;
1883 game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1884 local_player->sokobanfields_still_needed;
1886 game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
1888 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
1889 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
1890 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
1891 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
1892 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
1893 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
1894 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
1895 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
1897 game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
1898 game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
1899 game.magic_wall_time_left;
1900 game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
1901 game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
1903 game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1904 game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1905 game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1908 void DisplayGameControlValues()
1912 for (i = 0; game_controls[i].nr != -1; i++)
1914 int nr = game_controls[i].nr;
1915 int type = game_controls[i].type;
1916 struct TextPosInfo *pos = game_controls[i].pos;
1917 int value = game_control_value[nr];
1918 int last_value = last_game_control_value[nr];
1919 int chars = pos->chars;
1920 int font = pos->font;
1922 if (value == last_value)
1925 last_game_control_value[nr] = value;
1928 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
1931 if (PANEL_DEACTIVATED(pos))
1934 if (type == TYPE_INTEGER)
1936 if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
1938 boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
1940 if (use_dynamic_chars) /* use dynamic number of chars */
1942 int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
1943 int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
1944 int chars2 = chars1 + 1;
1945 int font1 = pos->font;
1946 int font2 = pos->font_alt;
1948 chars = (value < value_change ? chars1 : chars2);
1949 font = (value < value_change ? font1 : font2);
1951 /* clear background if value just changed its size (dynamic chars) */
1952 if ((last_value < value_change) != (value < value_change))
1954 int width1 = chars1 * getFontWidth(font1);
1955 int width2 = chars2 * getFontWidth(font2);
1956 int max_width = MAX(width1, width2);
1957 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
1959 pos->width = max_width;
1961 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1962 max_width, max_height);
1966 pos->width = chars * getFontWidth(font);
1969 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
1971 else if (type == TYPE_ELEMENT)
1973 if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
1975 int key_nr = nr - GAME_CONTROL_KEY_1;
1976 int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
1977 int src_y = DOOR_GFX_PAGEY1 + 123;
1978 int dst_x = PANEL_XPOS(pos);
1979 int dst_y = PANEL_YPOS(pos);
1980 int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1981 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1982 EL_EM_KEY_1 : EL_KEY_1) + key_nr;
1983 int graphic = el2edimg(element);
1986 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1988 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1989 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1991 else if (value != EL_UNDEFINED)
1993 int graphic = el2edimg(value);
1994 int dst_x = PANEL_XPOS(pos);
1995 int dst_y = PANEL_YPOS(pos);
1997 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2000 else if (type == TYPE_STRING)
2002 char *s = (nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name :
2003 nr == GAME_CONTROL_LEVEL_NAME ? level.name :
2004 nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
2008 char *s_cut = getStringCopyN(s, pos->chars);
2010 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
2016 redraw_mask |= REDRAW_DOOR_1;
2020 void DrawGameValue_Emeralds(int value)
2022 struct TextPosInfo *pos = &game.panel.gems;
2024 int font_nr = pos->font;
2026 int font_nr = FONT_TEXT_2;
2028 int font_width = getFontWidth(font_nr);
2029 int chars = pos->chars;
2032 return; /* !!! USE NEW STUFF !!! */
2035 if (PANEL_DEACTIVATED(pos))
2038 pos->width = chars * font_width;
2040 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2043 void DrawGameValue_Dynamite(int value)
2045 struct TextPosInfo *pos = &game.panel.inventory;
2047 int font_nr = pos->font;
2049 int font_nr = FONT_TEXT_2;
2051 int font_width = getFontWidth(font_nr);
2052 int chars = pos->chars;
2055 return; /* !!! USE NEW STUFF !!! */
2058 if (PANEL_DEACTIVATED(pos))
2061 pos->width = chars * font_width;
2063 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2066 void DrawGameValue_Score(int value)
2068 struct TextPosInfo *pos = &game.panel.score;
2070 int font_nr = pos->font;
2072 int font_nr = FONT_TEXT_2;
2074 int font_width = getFontWidth(font_nr);
2075 int chars = pos->chars;
2078 return; /* !!! USE NEW STUFF !!! */
2081 if (PANEL_DEACTIVATED(pos))
2084 pos->width = chars * font_width;
2086 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2089 void DrawGameValue_Time(int value)
2091 struct TextPosInfo *pos = &game.panel.time;
2092 static int last_value = -1;
2095 int chars = pos->chars;
2097 int font1_nr = pos->font;
2098 int font2_nr = pos->font_alt;
2100 int font1_nr = FONT_TEXT_2;
2101 int font2_nr = FONT_TEXT_1;
2103 int font_nr = font1_nr;
2104 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2107 return; /* !!! USE NEW STUFF !!! */
2110 if (PANEL_DEACTIVATED(pos))
2113 if (use_dynamic_chars) /* use dynamic number of chars */
2115 chars = (value < 1000 ? chars1 : chars2);
2116 font_nr = (value < 1000 ? font1_nr : font2_nr);
2119 /* clear background if value just changed its size (dynamic chars only) */
2120 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2122 int width1 = chars1 * getFontWidth(font1_nr);
2123 int width2 = chars2 * getFontWidth(font2_nr);
2124 int max_width = MAX(width1, width2);
2125 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2127 pos->width = max_width;
2129 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2130 max_width, max_height);
2133 pos->width = chars * getFontWidth(font_nr);
2135 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2140 void DrawGameValue_Level(int value)
2142 struct TextPosInfo *pos = &game.panel.level_number;
2145 int chars = pos->chars;
2147 int font1_nr = pos->font;
2148 int font2_nr = pos->font_alt;
2150 int font1_nr = FONT_TEXT_2;
2151 int font2_nr = FONT_TEXT_1;
2153 int font_nr = font1_nr;
2154 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2157 return; /* !!! USE NEW STUFF !!! */
2160 if (PANEL_DEACTIVATED(pos))
2163 if (use_dynamic_chars) /* use dynamic number of chars */
2165 chars = (level_nr < 100 ? chars1 : chars2);
2166 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2169 pos->width = chars * getFontWidth(font_nr);
2171 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2174 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2177 struct TextPosInfo *pos = &game.panel.keys;
2180 int base_key_graphic = EL_KEY_1;
2185 return; /* !!! USE NEW STUFF !!! */
2189 if (PANEL_DEACTIVATED(pos))
2194 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2195 base_key_graphic = EL_EM_KEY_1;
2199 pos->width = 4 * MINI_TILEX;
2203 for (i = 0; i < MAX_NUM_KEYS; i++)
2205 /* currently only 4 of 8 possible keys are displayed */
2206 for (i = 0; i < STD_NUM_KEYS; i++)
2210 struct TextPosInfo *pos = &game.panel.key[i];
2212 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2213 int src_y = DOOR_GFX_PAGEY1 + 123;
2215 int dst_x = PANEL_XPOS(pos);
2216 int dst_y = PANEL_YPOS(pos);
2218 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2219 int dst_y = PANEL_YPOS(pos);
2223 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2224 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2226 int graphic = el2edimg(element);
2230 if (PANEL_DEACTIVATED(pos))
2235 /* masked blit with tiles from half-size scaled bitmap does not work yet
2236 (no mask bitmap created for these sizes after loading and scaling) --
2237 solution: load without creating mask, scale, then create final mask */
2239 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2240 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2245 int graphic = el2edimg(base_key_graphic + i);
2250 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2252 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2253 dst_x - src_x, dst_y - src_y);
2254 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2260 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2262 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2263 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2266 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2268 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2269 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2277 void DrawGameValue_Emeralds(int value)
2279 int font_nr = FONT_TEXT_2;
2280 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2282 if (PANEL_DEACTIVATED(game.panel.gems))
2285 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2288 void DrawGameValue_Dynamite(int value)
2290 int font_nr = FONT_TEXT_2;
2291 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2293 if (PANEL_DEACTIVATED(game.panel.inventory))
2296 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2299 void DrawGameValue_Score(int value)
2301 int font_nr = FONT_TEXT_2;
2302 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2304 if (PANEL_DEACTIVATED(game.panel.score))
2307 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2310 void DrawGameValue_Time(int value)
2312 int font1_nr = FONT_TEXT_2;
2314 int font2_nr = FONT_TEXT_1;
2316 int font2_nr = FONT_LEVEL_NUMBER;
2318 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2319 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2321 if (PANEL_DEACTIVATED(game.panel.time))
2324 /* clear background if value just changed its size */
2325 if (value == 999 || value == 1000)
2326 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2329 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2331 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2334 void DrawGameValue_Level(int value)
2336 int font1_nr = FONT_TEXT_2;
2338 int font2_nr = FONT_TEXT_1;
2340 int font2_nr = FONT_LEVEL_NUMBER;
2343 if (PANEL_DEACTIVATED(game.panel.level))
2347 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2349 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2352 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2354 int base_key_graphic = EL_KEY_1;
2357 if (PANEL_DEACTIVATED(game.panel.keys))
2360 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2361 base_key_graphic = EL_EM_KEY_1;
2363 /* currently only 4 of 8 possible keys are displayed */
2364 for (i = 0; i < STD_NUM_KEYS; i++)
2366 int x = XX_KEYS + i * MINI_TILEX;
2370 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2372 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2373 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2379 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2382 int key[MAX_NUM_KEYS];
2385 /* prevent EM engine from updating time/score values parallel to GameWon() */
2386 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2387 local_player->LevelSolved)
2390 for (i = 0; i < MAX_NUM_KEYS; i++)
2391 key[i] = key_bits & (1 << i);
2393 DrawGameValue_Level(level_nr);
2395 DrawGameValue_Emeralds(emeralds);
2396 DrawGameValue_Dynamite(dynamite);
2397 DrawGameValue_Score(score);
2398 DrawGameValue_Time(time);
2400 DrawGameValue_Keys(key);
2403 void DrawGameDoorValues()
2405 UpdateGameControlValues();
2406 DisplayGameControlValues();
2409 void DrawGameDoorValues_OLD()
2411 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2412 int dynamite_value = 0;
2413 int score_value = (local_player->LevelSolved ? local_player->score_final :
2414 local_player->score);
2415 int gems_value = local_player->gems_still_needed;
2419 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2421 DrawGameDoorValues_EM();
2426 if (game.centered_player_nr == -1)
2428 for (i = 0; i < MAX_PLAYERS; i++)
2430 for (j = 0; j < MAX_NUM_KEYS; j++)
2431 if (stored_player[i].key[j])
2432 key_bits |= (1 << j);
2434 dynamite_value += stored_player[i].inventory_size;
2439 int player_nr = game.centered_player_nr;
2441 for (i = 0; i < MAX_NUM_KEYS; i++)
2442 if (stored_player[player_nr].key[i])
2443 key_bits |= (1 << i);
2445 dynamite_value = stored_player[player_nr].inventory_size;
2448 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2454 =============================================================================
2456 -----------------------------------------------------------------------------
2457 initialize game engine due to level / tape version number
2458 =============================================================================
2461 static void InitGameEngine()
2463 int i, j, k, l, x, y;
2465 /* set game engine from tape file when re-playing, else from level file */
2466 game.engine_version = (tape.playing ? tape.engine_version :
2467 level.game_version);
2469 /* ---------------------------------------------------------------------- */
2470 /* set flags for bugs and changes according to active game engine version */
2471 /* ---------------------------------------------------------------------- */
2474 Summary of bugfix/change:
2475 Fixed handling for custom elements that change when pushed by the player.
2477 Fixed/changed in version:
2481 Before 3.1.0, custom elements that "change when pushing" changed directly
2482 after the player started pushing them (until then handled in "DigField()").
2483 Since 3.1.0, these custom elements are not changed until the "pushing"
2484 move of the element is finished (now handled in "ContinueMoving()").
2486 Affected levels/tapes:
2487 The first condition is generally needed for all levels/tapes before version
2488 3.1.0, which might use the old behaviour before it was changed; known tapes
2489 that are affected are some tapes from the level set "Walpurgis Gardens" by
2491 The second condition is an exception from the above case and is needed for
2492 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2493 above (including some development versions of 3.1.0), but before it was
2494 known that this change would break tapes like the above and was fixed in
2495 3.1.1, so that the changed behaviour was active although the engine version
2496 while recording maybe was before 3.1.0. There is at least one tape that is
2497 affected by this exception, which is the tape for the one-level set "Bug
2498 Machine" by Juergen Bonhagen.
2501 game.use_change_when_pushing_bug =
2502 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2504 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2505 tape.game_version < VERSION_IDENT(3,1,1,0)));
2508 Summary of bugfix/change:
2509 Fixed handling for blocking the field the player leaves when moving.
2511 Fixed/changed in version:
2515 Before 3.1.1, when "block last field when moving" was enabled, the field
2516 the player is leaving when moving was blocked for the time of the move,
2517 and was directly unblocked afterwards. This resulted in the last field
2518 being blocked for exactly one less than the number of frames of one player
2519 move. Additionally, even when blocking was disabled, the last field was
2520 blocked for exactly one frame.
2521 Since 3.1.1, due to changes in player movement handling, the last field
2522 is not blocked at all when blocking is disabled. When blocking is enabled,
2523 the last field is blocked for exactly the number of frames of one player
2524 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2525 last field is blocked for exactly one more than the number of frames of
2528 Affected levels/tapes:
2529 (!!! yet to be determined -- probably many !!!)
2532 game.use_block_last_field_bug =
2533 (game.engine_version < VERSION_IDENT(3,1,1,0));
2536 Summary of bugfix/change:
2537 Changed behaviour of CE changes with multiple changes per single frame.
2539 Fixed/changed in version:
2543 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2544 This resulted in race conditions where CEs seem to behave strange in some
2545 situations (where triggered CE changes were just skipped because there was
2546 already a CE change on that tile in the playfield in that engine frame).
2547 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2548 (The number of changes per frame must be limited in any case, because else
2549 it is easily possible to define CE changes that would result in an infinite
2550 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2551 should be set large enough so that it would only be reached in cases where
2552 the corresponding CE change conditions run into a loop. Therefore, it seems
2553 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2554 maximal number of change pages for custom elements.)
2556 Affected levels/tapes:
2560 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2561 game.max_num_changes_per_frame = 1;
2563 game.max_num_changes_per_frame =
2564 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2567 /* ---------------------------------------------------------------------- */
2569 /* default scan direction: scan playfield from top/left to bottom/right */
2570 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2572 /* dynamically adjust element properties according to game engine version */
2573 InitElementPropertiesEngine(game.engine_version);
2576 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2577 printf(" tape version == %06d [%s] [file: %06d]\n",
2578 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2580 printf(" => game.engine_version == %06d\n", game.engine_version);
2583 /* ---------- initialize player's initial move delay --------------------- */
2585 /* dynamically adjust player properties according to level information */
2586 for (i = 0; i < MAX_PLAYERS; i++)
2587 game.initial_move_delay_value[i] =
2588 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2590 /* dynamically adjust player properties according to game engine version */
2591 for (i = 0; i < MAX_PLAYERS; i++)
2592 game.initial_move_delay[i] =
2593 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2594 game.initial_move_delay_value[i] : 0);
2596 /* ---------- initialize player's initial push delay --------------------- */
2598 /* dynamically adjust player properties according to game engine version */
2599 game.initial_push_delay_value =
2600 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2602 /* ---------- initialize changing elements ------------------------------- */
2604 /* initialize changing elements information */
2605 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2607 struct ElementInfo *ei = &element_info[i];
2609 /* this pointer might have been changed in the level editor */
2610 ei->change = &ei->change_page[0];
2612 if (!IS_CUSTOM_ELEMENT(i))
2614 ei->change->target_element = EL_EMPTY_SPACE;
2615 ei->change->delay_fixed = 0;
2616 ei->change->delay_random = 0;
2617 ei->change->delay_frames = 1;
2620 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2622 ei->has_change_event[j] = FALSE;
2624 ei->event_page_nr[j] = 0;
2625 ei->event_page[j] = &ei->change_page[0];
2629 /* add changing elements from pre-defined list */
2630 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2632 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2633 struct ElementInfo *ei = &element_info[ch_delay->element];
2635 ei->change->target_element = ch_delay->target_element;
2636 ei->change->delay_fixed = ch_delay->change_delay;
2638 ei->change->pre_change_function = ch_delay->pre_change_function;
2639 ei->change->change_function = ch_delay->change_function;
2640 ei->change->post_change_function = ch_delay->post_change_function;
2642 ei->change->can_change = TRUE;
2643 ei->change->can_change_or_has_action = TRUE;
2645 ei->has_change_event[CE_DELAY] = TRUE;
2647 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2648 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2651 /* ---------- initialize internal run-time variables ------------- */
2653 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2655 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2657 for (j = 0; j < ei->num_change_pages; j++)
2659 ei->change_page[j].can_change_or_has_action =
2660 (ei->change_page[j].can_change |
2661 ei->change_page[j].has_action);
2665 /* add change events from custom element configuration */
2666 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2668 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2670 for (j = 0; j < ei->num_change_pages; j++)
2672 if (!ei->change_page[j].can_change_or_has_action)
2675 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2677 /* only add event page for the first page found with this event */
2678 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2680 ei->has_change_event[k] = TRUE;
2682 ei->event_page_nr[k] = j;
2683 ei->event_page[k] = &ei->change_page[j];
2689 /* ---------- initialize run-time trigger player and element ------------- */
2691 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2693 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2695 for (j = 0; j < ei->num_change_pages; j++)
2697 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2698 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2699 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2700 ei->change_page[j].actual_trigger_ce_value = 0;
2701 ei->change_page[j].actual_trigger_ce_score = 0;
2705 /* ---------- initialize trigger events ---------------------------------- */
2707 /* initialize trigger events information */
2708 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2709 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2710 trigger_events[i][j] = FALSE;
2712 /* add trigger events from element change event properties */
2713 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2715 struct ElementInfo *ei = &element_info[i];
2717 for (j = 0; j < ei->num_change_pages; j++)
2719 if (!ei->change_page[j].can_change_or_has_action)
2722 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2724 int trigger_element = ei->change_page[j].trigger_element;
2726 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2728 if (ei->change_page[j].has_event[k])
2730 if (IS_GROUP_ELEMENT(trigger_element))
2732 struct ElementGroupInfo *group =
2733 element_info[trigger_element].group;
2735 for (l = 0; l < group->num_elements_resolved; l++)
2736 trigger_events[group->element_resolved[l]][k] = TRUE;
2738 else if (trigger_element == EL_ANY_ELEMENT)
2739 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2740 trigger_events[l][k] = TRUE;
2742 trigger_events[trigger_element][k] = TRUE;
2749 /* ---------- initialize push delay -------------------------------------- */
2751 /* initialize push delay values to default */
2752 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2754 if (!IS_CUSTOM_ELEMENT(i))
2756 /* set default push delay values (corrected since version 3.0.7-1) */
2757 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2759 element_info[i].push_delay_fixed = 2;
2760 element_info[i].push_delay_random = 8;
2764 element_info[i].push_delay_fixed = 8;
2765 element_info[i].push_delay_random = 8;
2770 /* set push delay value for certain elements from pre-defined list */
2771 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2773 int e = push_delay_list[i].element;
2775 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2776 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2779 /* set push delay value for Supaplex elements for newer engine versions */
2780 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2782 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2784 if (IS_SP_ELEMENT(i))
2786 /* set SP push delay to just enough to push under a falling zonk */
2787 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2789 element_info[i].push_delay_fixed = delay;
2790 element_info[i].push_delay_random = 0;
2795 /* ---------- initialize move stepsize ----------------------------------- */
2797 /* initialize move stepsize values to default */
2798 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2799 if (!IS_CUSTOM_ELEMENT(i))
2800 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2802 /* set move stepsize value for certain elements from pre-defined list */
2803 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2805 int e = move_stepsize_list[i].element;
2807 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2810 /* ---------- initialize collect score ----------------------------------- */
2812 /* initialize collect score values for custom elements from initial value */
2813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2814 if (IS_CUSTOM_ELEMENT(i))
2815 element_info[i].collect_score = element_info[i].collect_score_initial;
2817 /* ---------- initialize collect count ----------------------------------- */
2819 /* initialize collect count values for non-custom elements */
2820 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2821 if (!IS_CUSTOM_ELEMENT(i))
2822 element_info[i].collect_count_initial = 0;
2824 /* add collect count values for all elements from pre-defined list */
2825 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2826 element_info[collect_count_list[i].element].collect_count_initial =
2827 collect_count_list[i].count;
2829 /* ---------- initialize access direction -------------------------------- */
2831 /* initialize access direction values to default (access from every side) */
2832 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2833 if (!IS_CUSTOM_ELEMENT(i))
2834 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2836 /* set access direction value for certain elements from pre-defined list */
2837 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2838 element_info[access_direction_list[i].element].access_direction =
2839 access_direction_list[i].direction;
2841 /* ---------- initialize explosion content ------------------------------- */
2842 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2844 if (IS_CUSTOM_ELEMENT(i))
2847 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2849 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2851 element_info[i].content.e[x][y] =
2852 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2853 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2854 i == EL_PLAYER_3 ? EL_EMERALD :
2855 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2856 i == EL_MOLE ? EL_EMERALD_RED :
2857 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2858 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2859 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2860 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2861 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2862 i == EL_WALL_EMERALD ? EL_EMERALD :
2863 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2864 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2865 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2866 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2867 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2868 i == EL_WALL_PEARL ? EL_PEARL :
2869 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2874 /* ---------- initialize recursion detection ------------------------------ */
2875 recursion_loop_depth = 0;
2876 recursion_loop_detected = FALSE;
2877 recursion_loop_element = EL_UNDEFINED;
2879 /* ---------- initialize graphics engine ---------------------------------- */
2880 game.scroll_delay_value =
2881 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
2882 setup.scroll_delay ? setup.scroll_delay_value : 0);
2883 game.scroll_delay_value =
2884 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
2887 int get_num_special_action(int element, int action_first, int action_last)
2889 int num_special_action = 0;
2892 for (i = action_first; i <= action_last; i++)
2894 boolean found = FALSE;
2896 for (j = 0; j < NUM_DIRECTIONS; j++)
2897 if (el_act_dir2img(element, i, j) !=
2898 el_act_dir2img(element, ACTION_DEFAULT, j))
2902 num_special_action++;
2907 return num_special_action;
2912 =============================================================================
2914 -----------------------------------------------------------------------------
2915 initialize and start new game
2916 =============================================================================
2921 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2922 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2923 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2925 boolean do_fading = (game_status == GAME_MODE_MAIN);
2929 game_status = GAME_MODE_PLAYING;
2932 InitGameControlValues();
2934 /* don't play tapes over network */
2935 network_playing = (options.network && !tape.playing);
2937 for (i = 0; i < MAX_PLAYERS; i++)
2939 struct PlayerInfo *player = &stored_player[i];
2941 player->index_nr = i;
2942 player->index_bit = (1 << i);
2943 player->element_nr = EL_PLAYER_1 + i;
2945 player->present = FALSE;
2946 player->active = FALSE;
2947 player->killed = FALSE;
2950 player->effective_action = 0;
2951 player->programmed_action = 0;
2954 player->score_final = 0;
2956 player->gems_still_needed = level.gems_needed;
2957 player->sokobanfields_still_needed = 0;
2958 player->lights_still_needed = 0;
2959 player->friends_still_needed = 0;
2961 for (j = 0; j < MAX_NUM_KEYS; j++)
2962 player->key[j] = FALSE;
2964 player->num_white_keys = 0;
2966 player->dynabomb_count = 0;
2967 player->dynabomb_size = 1;
2968 player->dynabombs_left = 0;
2969 player->dynabomb_xl = FALSE;
2971 player->MovDir = MV_NONE;
2974 player->GfxDir = MV_NONE;
2975 player->GfxAction = ACTION_DEFAULT;
2977 player->StepFrame = 0;
2979 player->use_murphy = FALSE;
2980 player->artwork_element =
2981 (level.use_artwork_element[i] ? level.artwork_element[i] :
2982 player->element_nr);
2984 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2985 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2987 player->gravity = level.initial_player_gravity[i];
2989 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2991 player->actual_frame_counter = 0;
2993 player->step_counter = 0;
2995 player->last_move_dir = MV_NONE;
2997 player->is_active = FALSE;
2999 player->is_waiting = FALSE;
3000 player->is_moving = FALSE;
3001 player->is_auto_moving = FALSE;
3002 player->is_digging = FALSE;
3003 player->is_snapping = FALSE;
3004 player->is_collecting = FALSE;
3005 player->is_pushing = FALSE;
3006 player->is_switching = FALSE;
3007 player->is_dropping = FALSE;
3008 player->is_dropping_pressed = FALSE;
3010 player->is_bored = FALSE;
3011 player->is_sleeping = FALSE;
3013 player->frame_counter_bored = -1;
3014 player->frame_counter_sleeping = -1;
3016 player->anim_delay_counter = 0;
3017 player->post_delay_counter = 0;
3019 player->dir_waiting = MV_NONE;
3020 player->action_waiting = ACTION_DEFAULT;
3021 player->last_action_waiting = ACTION_DEFAULT;
3022 player->special_action_bored = ACTION_DEFAULT;
3023 player->special_action_sleeping = ACTION_DEFAULT;
3025 player->switch_x = -1;
3026 player->switch_y = -1;
3028 player->drop_x = -1;
3029 player->drop_y = -1;
3031 player->show_envelope = 0;
3033 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3035 player->push_delay = -1; /* initialized when pushing starts */
3036 player->push_delay_value = game.initial_push_delay_value;
3038 player->drop_delay = 0;
3039 player->drop_pressed_delay = 0;
3041 player->last_jx = -1;
3042 player->last_jy = -1;
3046 player->shield_normal_time_left = 0;
3047 player->shield_deadly_time_left = 0;
3049 player->inventory_infinite_element = EL_UNDEFINED;
3050 player->inventory_size = 0;
3052 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3053 SnapField(player, 0, 0);
3055 player->LevelSolved = FALSE;
3056 player->GameOver = FALSE;
3058 player->LevelSolved_GameWon = FALSE;
3059 player->LevelSolved_GameEnd = FALSE;
3060 player->LevelSolved_PanelOff = FALSE;
3061 player->LevelSolved_SaveTape = FALSE;
3062 player->LevelSolved_SaveScore = FALSE;
3065 network_player_action_received = FALSE;
3067 #if defined(NETWORK_AVALIABLE)
3068 /* initial null action */
3069 if (network_playing)
3070 SendToServer_MovePlayer(MV_NONE);
3079 TimeLeft = level.time;
3082 ScreenMovDir = MV_NONE;
3086 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3088 AllPlayersGone = FALSE;
3090 game.yamyam_content_nr = 0;
3091 game.magic_wall_active = FALSE;
3092 game.magic_wall_time_left = 0;
3093 game.light_time_left = 0;
3094 game.timegate_time_left = 0;
3095 game.switchgate_pos = 0;
3096 game.wind_direction = level.wind_direction_initial;
3098 #if !USE_PLAYER_GRAVITY
3099 game.gravity = FALSE;
3100 game.explosions_delayed = TRUE;
3103 game.lenses_time_left = 0;
3104 game.magnify_time_left = 0;
3106 game.ball_state = level.ball_state_initial;
3107 game.ball_content_nr = 0;
3109 game.envelope_active = FALSE;
3111 /* set focus to local player for network games, else to all players */
3112 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3113 game.centered_player_nr_next = game.centered_player_nr;
3114 game.set_centered_player = FALSE;
3116 if (network_playing && tape.recording)
3118 /* store client dependent player focus when recording network games */
3119 tape.centered_player_nr_next = game.centered_player_nr_next;
3120 tape.set_centered_player = TRUE;
3123 for (i = 0; i < NUM_BELTS; i++)
3125 game.belt_dir[i] = MV_NONE;
3126 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3129 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3130 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3132 SCAN_PLAYFIELD(x, y)
3134 Feld[x][y] = level.field[x][y];
3135 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3136 ChangeDelay[x][y] = 0;
3137 ChangePage[x][y] = -1;
3138 #if USE_NEW_CUSTOM_VALUE
3139 CustomValue[x][y] = 0; /* initialized in InitField() */
3141 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3143 WasJustMoving[x][y] = 0;
3144 WasJustFalling[x][y] = 0;
3145 CheckCollision[x][y] = 0;
3146 CheckImpact[x][y] = 0;
3148 Pushed[x][y] = FALSE;
3150 ChangeCount[x][y] = 0;
3151 ChangeEvent[x][y] = -1;
3153 ExplodePhase[x][y] = 0;
3154 ExplodeDelay[x][y] = 0;
3155 ExplodeField[x][y] = EX_TYPE_NONE;
3157 RunnerVisit[x][y] = 0;
3158 PlayerVisit[x][y] = 0;
3161 GfxRandom[x][y] = INIT_GFX_RANDOM();
3162 GfxElement[x][y] = EL_UNDEFINED;
3163 GfxAction[x][y] = ACTION_DEFAULT;
3164 GfxDir[x][y] = MV_NONE;
3167 SCAN_PLAYFIELD(x, y)
3169 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3171 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3173 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3176 InitField(x, y, TRUE);
3181 for (i = 0; i < MAX_PLAYERS; i++)
3183 struct PlayerInfo *player = &stored_player[i];
3185 /* set number of special actions for bored and sleeping animation */
3186 player->num_special_action_bored =
3187 get_num_special_action(player->artwork_element,
3188 ACTION_BORING_1, ACTION_BORING_LAST);
3189 player->num_special_action_sleeping =
3190 get_num_special_action(player->artwork_element,
3191 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3194 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3195 emulate_sb ? EMU_SOKOBAN :
3196 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3198 #if USE_NEW_ALL_SLIPPERY
3199 /* initialize type of slippery elements */
3200 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202 if (!IS_CUSTOM_ELEMENT(i))
3204 /* default: elements slip down either to the left or right randomly */
3205 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3207 /* SP style elements prefer to slip down on the left side */
3208 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3209 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3211 /* BD style elements prefer to slip down on the left side */
3212 if (game.emulation == EMU_BOULDERDASH)
3213 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3218 /* initialize explosion and ignition delay */
3219 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3221 if (!IS_CUSTOM_ELEMENT(i))
3224 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3225 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3226 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3227 int last_phase = (num_phase + 1) * delay;
3228 int half_phase = (num_phase / 2) * delay;
3230 element_info[i].explosion_delay = last_phase - 1;
3231 element_info[i].ignition_delay = half_phase;
3233 if (i == EL_BLACK_ORB)
3234 element_info[i].ignition_delay = 1;
3238 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3239 element_info[i].explosion_delay = 1;
3241 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3242 element_info[i].ignition_delay = 1;
3246 /* correct non-moving belts to start moving left */
3247 for (i = 0; i < NUM_BELTS; i++)
3248 if (game.belt_dir[i] == MV_NONE)
3249 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3251 /* check if any connected player was not found in playfield */
3252 for (i = 0; i < MAX_PLAYERS; i++)
3254 struct PlayerInfo *player = &stored_player[i];
3256 if (player->connected && !player->present)
3258 for (j = 0; j < MAX_PLAYERS; j++)
3260 struct PlayerInfo *some_player = &stored_player[j];
3261 int jx = some_player->jx, jy = some_player->jy;
3263 /* assign first free player found that is present in the playfield */
3264 if (some_player->present && !some_player->connected)
3266 player->present = TRUE;
3267 player->active = TRUE;
3269 some_player->present = FALSE;
3270 some_player->active = FALSE;
3272 player->artwork_element = some_player->artwork_element;
3274 player->block_last_field = some_player->block_last_field;
3275 player->block_delay_adjustment = some_player->block_delay_adjustment;
3277 StorePlayer[jx][jy] = player->element_nr;
3278 player->jx = player->last_jx = jx;
3279 player->jy = player->last_jy = jy;
3289 /* when playing a tape, eliminate all players who do not participate */
3291 for (i = 0; i < MAX_PLAYERS; i++)
3293 if (stored_player[i].active && !tape.player_participates[i])
3295 struct PlayerInfo *player = &stored_player[i];
3296 int jx = player->jx, jy = player->jy;
3298 player->active = FALSE;
3299 StorePlayer[jx][jy] = 0;
3300 Feld[jx][jy] = EL_EMPTY;
3304 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3306 /* when in single player mode, eliminate all but the first active player */
3308 for (i = 0; i < MAX_PLAYERS; i++)
3310 if (stored_player[i].active)
3312 for (j = i + 1; j < MAX_PLAYERS; j++)
3314 if (stored_player[j].active)
3316 struct PlayerInfo *player = &stored_player[j];
3317 int jx = player->jx, jy = player->jy;
3319 player->active = FALSE;
3320 player->present = FALSE;
3322 StorePlayer[jx][jy] = 0;
3323 Feld[jx][jy] = EL_EMPTY;
3330 /* when recording the game, store which players take part in the game */
3333 for (i = 0; i < MAX_PLAYERS; i++)
3334 if (stored_player[i].active)
3335 tape.player_participates[i] = TRUE;
3340 for (i = 0; i < MAX_PLAYERS; i++)
3342 struct PlayerInfo *player = &stored_player[i];
3344 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3349 if (local_player == player)
3350 printf("Player %d is local player.\n", i+1);
3354 if (BorderElement == EL_EMPTY)
3357 SBX_Right = lev_fieldx - SCR_FIELDX;
3359 SBY_Lower = lev_fieldy - SCR_FIELDY;
3364 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3366 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3369 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3370 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3372 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3373 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3375 /* if local player not found, look for custom element that might create
3376 the player (make some assumptions about the right custom element) */
3377 if (!local_player->present)
3379 int start_x = 0, start_y = 0;
3380 int found_rating = 0;
3381 int found_element = EL_UNDEFINED;
3382 int player_nr = local_player->index_nr;
3384 SCAN_PLAYFIELD(x, y)
3386 int element = Feld[x][y];
3391 if (level.use_start_element[player_nr] &&
3392 level.start_element[player_nr] == element &&
3399 found_element = element;
3402 if (!IS_CUSTOM_ELEMENT(element))
3405 if (CAN_CHANGE(element))
3407 for (i = 0; i < element_info[element].num_change_pages; i++)
3409 /* check for player created from custom element as single target */
3410 content = element_info[element].change_page[i].target_element;
3411 is_player = ELEM_IS_PLAYER(content);
3413 if (is_player && (found_rating < 3 ||
3414 (found_rating == 3 && element < found_element)))
3420 found_element = element;
3425 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3427 /* check for player created from custom element as explosion content */
3428 content = element_info[element].content.e[xx][yy];
3429 is_player = ELEM_IS_PLAYER(content);
3431 if (is_player && (found_rating < 2 ||
3432 (found_rating == 2 && element < found_element)))
3434 start_x = x + xx - 1;
3435 start_y = y + yy - 1;
3438 found_element = element;
3441 if (!CAN_CHANGE(element))
3444 for (i = 0; i < element_info[element].num_change_pages; i++)
3446 /* check for player created from custom element as extended target */
3448 element_info[element].change_page[i].target_content.e[xx][yy];
3450 is_player = ELEM_IS_PLAYER(content);
3452 if (is_player && (found_rating < 1 ||
3453 (found_rating == 1 && element < found_element)))
3455 start_x = x + xx - 1;
3456 start_y = y + yy - 1;
3459 found_element = element;
3465 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3466 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3469 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3470 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3475 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3476 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3477 local_player->jx - MIDPOSX);
3479 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3480 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3481 local_player->jy - MIDPOSY);
3486 if (!game.restart_level)
3487 CloseDoor(DOOR_CLOSE_1);
3490 if (level_editor_test_game)
3491 FadeSkipNextFadeIn();
3493 FadeSetEnterScreen();
3495 if (level_editor_test_game)
3496 fading = fading_none;
3498 fading = menu.destination;
3502 FadeOut(REDRAW_FIELD);
3505 FadeOut(REDRAW_FIELD);
3508 /* !!! FIX THIS (START) !!! */
3509 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3511 InitGameEngine_EM();
3513 /* blit playfield from scroll buffer to normal back buffer for fading in */
3514 BlitScreenToBitmap_EM(backbuffer);
3521 /* after drawing the level, correct some elements */
3522 if (game.timegate_time_left == 0)
3523 CloseAllOpenTimegates();
3525 /* blit playfield from scroll buffer to normal back buffer for fading in */
3526 if (setup.soft_scrolling)
3527 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3529 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3531 /* !!! FIX THIS (END) !!! */
3534 FadeIn(REDRAW_FIELD);
3537 FadeIn(REDRAW_FIELD);
3542 if (!game.restart_level)
3544 /* copy default game door content to main double buffer */
3545 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3546 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3549 SetPanelBackground();
3550 SetDrawBackgroundMask(REDRAW_DOOR_1);
3552 DrawGameDoorValues();
3554 if (!game.restart_level)
3558 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3559 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3560 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3564 /* copy actual game door content to door double buffer for OpenDoor() */
3565 BlitBitmap(drawto, bitmap_db_door,
3566 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3568 OpenDoor(DOOR_OPEN_ALL);
3570 PlaySound(SND_GAME_STARTING);
3572 if (setup.sound_music)
3575 KeyboardAutoRepeatOffUnlessAutoplay();
3579 for (i = 0; i < MAX_PLAYERS; i++)
3580 printf("Player %d %sactive.\n",
3581 i + 1, (stored_player[i].active ? "" : "not "));
3592 game.restart_level = FALSE;
3595 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3597 /* this is used for non-R'n'D game engines to update certain engine values */
3599 /* needed to determine if sounds are played within the visible screen area */
3600 scroll_x = actual_scroll_x;
3601 scroll_y = actual_scroll_y;
3604 void InitMovDir(int x, int y)
3606 int i, element = Feld[x][y];
3607 static int xy[4][2] =
3614 static int direction[3][4] =
3616 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3617 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3618 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3627 Feld[x][y] = EL_BUG;
3628 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3631 case EL_SPACESHIP_RIGHT:
3632 case EL_SPACESHIP_UP:
3633 case EL_SPACESHIP_LEFT:
3634 case EL_SPACESHIP_DOWN:
3635 Feld[x][y] = EL_SPACESHIP;
3636 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3639 case EL_BD_BUTTERFLY_RIGHT:
3640 case EL_BD_BUTTERFLY_UP:
3641 case EL_BD_BUTTERFLY_LEFT:
3642 case EL_BD_BUTTERFLY_DOWN:
3643 Feld[x][y] = EL_BD_BUTTERFLY;
3644 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3647 case EL_BD_FIREFLY_RIGHT:
3648 case EL_BD_FIREFLY_UP:
3649 case EL_BD_FIREFLY_LEFT:
3650 case EL_BD_FIREFLY_DOWN:
3651 Feld[x][y] = EL_BD_FIREFLY;
3652 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3655 case EL_PACMAN_RIGHT:
3657 case EL_PACMAN_LEFT:
3658 case EL_PACMAN_DOWN:
3659 Feld[x][y] = EL_PACMAN;
3660 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3663 case EL_YAMYAM_LEFT:
3664 case EL_YAMYAM_RIGHT:
3666 case EL_YAMYAM_DOWN:
3667 Feld[x][y] = EL_YAMYAM;
3668 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3671 case EL_SP_SNIKSNAK:
3672 MovDir[x][y] = MV_UP;
3675 case EL_SP_ELECTRON:
3676 MovDir[x][y] = MV_LEFT;
3683 Feld[x][y] = EL_MOLE;
3684 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3688 if (IS_CUSTOM_ELEMENT(element))
3690 struct ElementInfo *ei = &element_info[element];
3691 int move_direction_initial = ei->move_direction_initial;
3692 int move_pattern = ei->move_pattern;
3694 if (move_direction_initial == MV_START_PREVIOUS)
3696 if (MovDir[x][y] != MV_NONE)
3699 move_direction_initial = MV_START_AUTOMATIC;
3702 if (move_direction_initial == MV_START_RANDOM)
3703 MovDir[x][y] = 1 << RND(4);
3704 else if (move_direction_initial & MV_ANY_DIRECTION)
3705 MovDir[x][y] = move_direction_initial;
3706 else if (move_pattern == MV_ALL_DIRECTIONS ||
3707 move_pattern == MV_TURNING_LEFT ||
3708 move_pattern == MV_TURNING_RIGHT ||
3709 move_pattern == MV_TURNING_LEFT_RIGHT ||
3710 move_pattern == MV_TURNING_RIGHT_LEFT ||
3711 move_pattern == MV_TURNING_RANDOM)
3712 MovDir[x][y] = 1 << RND(4);
3713 else if (move_pattern == MV_HORIZONTAL)
3714 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3715 else if (move_pattern == MV_VERTICAL)
3716 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3717 else if (move_pattern & MV_ANY_DIRECTION)
3718 MovDir[x][y] = element_info[element].move_pattern;
3719 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3720 move_pattern == MV_ALONG_RIGHT_SIDE)
3722 /* use random direction as default start direction */
3723 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3724 MovDir[x][y] = 1 << RND(4);
3726 for (i = 0; i < NUM_DIRECTIONS; i++)
3728 int x1 = x + xy[i][0];
3729 int y1 = y + xy[i][1];
3731 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3733 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3734 MovDir[x][y] = direction[0][i];
3736 MovDir[x][y] = direction[1][i];
3745 MovDir[x][y] = 1 << RND(4);
3747 if (element != EL_BUG &&
3748 element != EL_SPACESHIP &&
3749 element != EL_BD_BUTTERFLY &&
3750 element != EL_BD_FIREFLY)
3753 for (i = 0; i < NUM_DIRECTIONS; i++)
3755 int x1 = x + xy[i][0];
3756 int y1 = y + xy[i][1];
3758 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3760 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3762 MovDir[x][y] = direction[0][i];
3765 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3766 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3768 MovDir[x][y] = direction[1][i];
3777 GfxDir[x][y] = MovDir[x][y];
3780 void InitAmoebaNr(int x, int y)
3783 int group_nr = AmoebeNachbarNr(x, y);
3787 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3789 if (AmoebaCnt[i] == 0)
3797 AmoebaNr[x][y] = group_nr;
3798 AmoebaCnt[group_nr]++;
3799 AmoebaCnt2[group_nr]++;
3802 static void PlayerWins(struct PlayerInfo *player)
3804 player->LevelSolved = TRUE;
3805 player->GameOver = TRUE;
3807 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3808 level.native_em_level->lev->score : player->score);
3813 static int time, time_final;
3814 static int score, score_final;
3815 static int game_over_delay_1 = 0;
3816 static int game_over_delay_2 = 0;
3817 int game_over_delay_value_1 = 50;
3818 int game_over_delay_value_2 = 50;
3820 if (!local_player->LevelSolved_GameWon)
3824 /* do not start end game actions before the player stops moving (to exit) */
3825 if (local_player->MovPos)
3828 local_player->LevelSolved_GameWon = TRUE;
3829 local_player->LevelSolved_SaveTape = tape.recording;
3830 local_player->LevelSolved_SaveScore = !tape.playing;
3832 if (tape.auto_play) /* tape might already be stopped here */
3833 tape.auto_play_level_solved = TRUE;
3839 game_over_delay_1 = game_over_delay_value_1;
3840 game_over_delay_2 = game_over_delay_value_2;
3842 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3843 score = score_final = local_player->score_final;
3848 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3850 else if (level.time == 0 && TimePlayed < 999)
3853 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3856 local_player->score_final = score_final;
3858 if (level_editor_test_game)
3861 score = score_final;
3864 game_control_value[GAME_CONTROL_TIME] = time;
3865 game_control_value[GAME_CONTROL_SCORE] = score;
3867 DisplayGameControlValues();
3869 DrawGameValue_Time(time);
3870 DrawGameValue_Score(score);
3874 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3876 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3878 /* close exit door after last player */
3879 if ((AllPlayersGone &&
3880 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3881 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3882 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3883 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3884 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3886 int element = Feld[ExitX][ExitY];
3889 if (element == EL_EM_EXIT_OPEN ||
3890 element == EL_EM_STEEL_EXIT_OPEN)
3897 Feld[ExitX][ExitY] =
3898 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3899 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3900 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3901 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3902 EL_EM_STEEL_EXIT_CLOSING);
3904 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3908 /* player disappears */
3909 DrawLevelField(ExitX, ExitY);
3912 for (i = 0; i < MAX_PLAYERS; i++)
3914 struct PlayerInfo *player = &stored_player[i];
3916 if (player->present)
3918 RemovePlayer(player);
3920 /* player disappears */
3921 DrawLevelField(player->jx, player->jy);
3926 PlaySound(SND_GAME_WINNING);
3929 if (game_over_delay_1 > 0)
3931 game_over_delay_1--;
3936 if (time != time_final)
3938 int time_to_go = ABS(time_final - time);
3939 int time_count_dir = (time < time_final ? +1 : -1);
3940 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3942 time += time_count_steps * time_count_dir;
3943 score += time_count_steps * level.score[SC_TIME_BONUS];
3946 game_control_value[GAME_CONTROL_TIME] = time;
3947 game_control_value[GAME_CONTROL_SCORE] = score;
3949 DisplayGameControlValues();
3951 DrawGameValue_Time(time);
3952 DrawGameValue_Score(score);
3955 if (time == time_final)
3956 StopSound(SND_GAME_LEVELTIME_BONUS);
3957 else if (setup.sound_loops)
3958 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3960 PlaySound(SND_GAME_LEVELTIME_BONUS);
3965 local_player->LevelSolved_PanelOff = TRUE;
3967 if (game_over_delay_2 > 0)
3969 game_over_delay_2--;
3982 boolean raise_level = FALSE;
3984 local_player->LevelSolved_GameEnd = TRUE;
3986 CloseDoor(DOOR_CLOSE_1);
3988 if (local_player->LevelSolved_SaveTape)
3995 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3997 SaveTape(tape.level_nr); /* ask to save tape */
4001 if (level_editor_test_game)
4003 game_status = GAME_MODE_MAIN;
4006 DrawAndFadeInMainMenu(REDRAW_FIELD);
4014 if (!local_player->LevelSolved_SaveScore)
4017 FadeOut(REDRAW_FIELD);
4020 game_status = GAME_MODE_MAIN;
4022 DrawAndFadeInMainMenu(REDRAW_FIELD);
4027 if (level_nr == leveldir_current->handicap_level)
4029 leveldir_current->handicap_level++;
4030 SaveLevelSetup_SeriesInfo();
4033 if (level_nr < leveldir_current->last_level)
4034 raise_level = TRUE; /* advance to next level */
4036 if ((hi_pos = NewHiScore()) >= 0)
4038 game_status = GAME_MODE_SCORES;
4040 DrawHallOfFame(hi_pos);
4051 FadeOut(REDRAW_FIELD);
4054 game_status = GAME_MODE_MAIN;
4062 DrawAndFadeInMainMenu(REDRAW_FIELD);
4071 LoadScore(level_nr);
4073 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4074 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4077 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4079 if (local_player->score_final > highscore[k].Score)
4081 /* player has made it to the hall of fame */
4083 if (k < MAX_SCORE_ENTRIES - 1)
4085 int m = MAX_SCORE_ENTRIES - 1;
4088 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4089 if (strEqual(setup.player_name, highscore[l].Name))
4091 if (m == k) /* player's new highscore overwrites his old one */
4095 for (l = m; l > k; l--)
4097 strcpy(highscore[l].Name, highscore[l - 1].Name);
4098 highscore[l].Score = highscore[l - 1].Score;
4105 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4106 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4107 highscore[k].Score = local_player->score_final;
4113 else if (!strncmp(setup.player_name, highscore[k].Name,
4114 MAX_PLAYER_NAME_LEN))
4115 break; /* player already there with a higher score */
4121 SaveScore(level_nr);
4126 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4128 int element = Feld[x][y];
4129 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4130 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4131 int horiz_move = (dx != 0);
4132 int sign = (horiz_move ? dx : dy);
4133 int step = sign * element_info[element].move_stepsize;
4135 /* special values for move stepsize for spring and things on conveyor belt */
4138 if (CAN_FALL(element) &&
4139 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4140 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4141 else if (element == EL_SPRING)
4142 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4148 inline static int getElementMoveStepsize(int x, int y)
4150 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4153 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4155 if (player->GfxAction != action || player->GfxDir != dir)
4158 printf("Player frame reset! (%d => %d, %d => %d)\n",
4159 player->GfxAction, action, player->GfxDir, dir);
4162 player->GfxAction = action;
4163 player->GfxDir = dir;
4165 player->StepFrame = 0;
4169 #if USE_GFX_RESET_GFX_ANIMATION
4170 static void ResetGfxFrame(int x, int y, boolean redraw)
4172 int element = Feld[x][y];
4173 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4174 int last_gfx_frame = GfxFrame[x][y];
4176 if (graphic_info[graphic].anim_global_sync)
4177 GfxFrame[x][y] = FrameCounter;
4178 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4179 GfxFrame[x][y] = CustomValue[x][y];
4180 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4181 GfxFrame[x][y] = element_info[element].collect_score;
4182 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4183 GfxFrame[x][y] = ChangeDelay[x][y];
4185 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4186 DrawLevelGraphicAnimation(x, y, graphic);
4190 static void ResetGfxAnimation(int x, int y)
4192 GfxAction[x][y] = ACTION_DEFAULT;
4193 GfxDir[x][y] = MovDir[x][y];
4196 #if USE_GFX_RESET_GFX_ANIMATION
4197 ResetGfxFrame(x, y, FALSE);
4201 static void ResetRandomAnimationValue(int x, int y)
4203 GfxRandom[x][y] = INIT_GFX_RANDOM();
4206 void InitMovingField(int x, int y, int direction)
4208 int element = Feld[x][y];
4209 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4210 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4213 boolean is_moving_before, is_moving_after;
4215 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4218 /* check if element was/is moving or being moved before/after mode change */
4221 is_moving_before = (WasJustMoving[x][y] != 0);
4223 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4224 is_moving_before = WasJustMoving[x][y];
4227 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4229 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4231 /* reset animation only for moving elements which change direction of moving
4232 or which just started or stopped moving
4233 (else CEs with property "can move" / "not moving" are reset each frame) */
4234 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4236 if (is_moving_before != is_moving_after ||
4237 direction != MovDir[x][y])
4238 ResetGfxAnimation(x, y);
4240 if ((is_moving_before || is_moving_after) && !continues_moving)
4241 ResetGfxAnimation(x, y);
4244 if (!continues_moving)
4245 ResetGfxAnimation(x, y);
4248 MovDir[x][y] = direction;
4249 GfxDir[x][y] = direction;
4251 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4252 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4253 direction == MV_DOWN && CAN_FALL(element) ?
4254 ACTION_FALLING : ACTION_MOVING);
4256 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4257 ACTION_FALLING : ACTION_MOVING);
4260 /* this is needed for CEs with property "can move" / "not moving" */
4262 if (is_moving_after)
4264 if (Feld[newx][newy] == EL_EMPTY)
4265 Feld[newx][newy] = EL_BLOCKED;
4267 MovDir[newx][newy] = MovDir[x][y];
4269 #if USE_NEW_CUSTOM_VALUE
4270 CustomValue[newx][newy] = CustomValue[x][y];
4273 GfxFrame[newx][newy] = GfxFrame[x][y];
4274 GfxRandom[newx][newy] = GfxRandom[x][y];
4275 GfxAction[newx][newy] = GfxAction[x][y];
4276 GfxDir[newx][newy] = GfxDir[x][y];
4280 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4282 int direction = MovDir[x][y];
4283 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4284 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4290 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4292 int oldx = x, oldy = y;
4293 int direction = MovDir[x][y];
4295 if (direction == MV_LEFT)
4297 else if (direction == MV_RIGHT)
4299 else if (direction == MV_UP)
4301 else if (direction == MV_DOWN)
4304 *comes_from_x = oldx;
4305 *comes_from_y = oldy;
4308 int MovingOrBlocked2Element(int x, int y)
4310 int element = Feld[x][y];
4312 if (element == EL_BLOCKED)
4316 Blocked2Moving(x, y, &oldx, &oldy);
4317 return Feld[oldx][oldy];
4323 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4325 /* like MovingOrBlocked2Element(), but if element is moving
4326 and (x,y) is the field the moving element is just leaving,
4327 return EL_BLOCKED instead of the element value */
4328 int element = Feld[x][y];
4330 if (IS_MOVING(x, y))
4332 if (element == EL_BLOCKED)
4336 Blocked2Moving(x, y, &oldx, &oldy);
4337 return Feld[oldx][oldy];
4346 static void RemoveField(int x, int y)
4348 Feld[x][y] = EL_EMPTY;
4354 #if USE_NEW_CUSTOM_VALUE
4355 CustomValue[x][y] = 0;
4359 ChangeDelay[x][y] = 0;
4360 ChangePage[x][y] = -1;
4361 Pushed[x][y] = FALSE;
4364 ExplodeField[x][y] = EX_TYPE_NONE;
4367 GfxElement[x][y] = EL_UNDEFINED;
4368 GfxAction[x][y] = ACTION_DEFAULT;
4369 GfxDir[x][y] = MV_NONE;
4372 void RemoveMovingField(int x, int y)
4374 int oldx = x, oldy = y, newx = x, newy = y;
4375 int element = Feld[x][y];
4376 int next_element = EL_UNDEFINED;
4378 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4381 if (IS_MOVING(x, y))
4383 Moving2Blocked(x, y, &newx, &newy);
4385 if (Feld[newx][newy] != EL_BLOCKED)
4387 /* element is moving, but target field is not free (blocked), but
4388 already occupied by something different (example: acid pool);
4389 in this case, only remove the moving field, but not the target */
4391 RemoveField(oldx, oldy);
4393 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4395 DrawLevelField(oldx, oldy);
4400 else if (element == EL_BLOCKED)
4402 Blocked2Moving(x, y, &oldx, &oldy);
4403 if (!IS_MOVING(oldx, oldy))
4407 if (element == EL_BLOCKED &&
4408 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4409 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4410 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4411 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4412 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4413 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4414 next_element = get_next_element(Feld[oldx][oldy]);
4416 RemoveField(oldx, oldy);
4417 RemoveField(newx, newy);
4419 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4421 if (next_element != EL_UNDEFINED)
4422 Feld[oldx][oldy] = next_element;
4424 DrawLevelField(oldx, oldy);
4425 DrawLevelField(newx, newy);
4428 void DrawDynamite(int x, int y)
4430 int sx = SCREENX(x), sy = SCREENY(y);
4431 int graphic = el2img(Feld[x][y]);
4434 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4437 if (IS_WALKABLE_INSIDE(Back[x][y]))
4441 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4442 else if (Store[x][y])
4443 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4445 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4447 if (Back[x][y] || Store[x][y])
4448 DrawGraphicThruMask(sx, sy, graphic, frame);
4450 DrawGraphic(sx, sy, graphic, frame);
4453 void CheckDynamite(int x, int y)
4455 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4459 if (MovDelay[x][y] != 0)
4462 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4468 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4473 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4475 boolean num_checked_players = 0;
4478 for (i = 0; i < MAX_PLAYERS; i++)
4480 if (stored_player[i].active)
4482 int sx = stored_player[i].jx;
4483 int sy = stored_player[i].jy;
4485 if (num_checked_players == 0)
4492 *sx1 = MIN(*sx1, sx);
4493 *sy1 = MIN(*sy1, sy);
4494 *sx2 = MAX(*sx2, sx);
4495 *sy2 = MAX(*sy2, sy);
4498 num_checked_players++;
4503 static boolean checkIfAllPlayersFitToScreen_RND()
4505 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4507 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4509 return (sx2 - sx1 < SCR_FIELDX &&
4510 sy2 - sy1 < SCR_FIELDY);
4513 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4515 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4517 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4519 *sx = (sx1 + sx2) / 2;
4520 *sy = (sy1 + sy2) / 2;
4523 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4524 boolean center_screen, boolean quick_relocation)
4526 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4527 boolean no_delay = (tape.warp_forward);
4528 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4529 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4531 if (quick_relocation)
4533 int offset = game.scroll_delay_value;
4535 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4537 if (!level.shifted_relocation || center_screen)
4539 /* quick relocation (without scrolling), with centering of screen */
4541 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4542 x > SBX_Right + MIDPOSX ? SBX_Right :
4545 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4546 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4551 /* quick relocation (without scrolling), but do not center screen */
4553 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4554 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4557 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4558 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4561 int offset_x = x + (scroll_x - center_scroll_x);
4562 int offset_y = y + (scroll_y - center_scroll_y);
4564 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4565 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4566 offset_x - MIDPOSX);
4568 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4569 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4570 offset_y - MIDPOSY);
4575 /* quick relocation (without scrolling), inside visible screen area */
4577 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4578 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4579 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4581 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4582 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4583 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4585 /* don't scroll over playfield boundaries */
4586 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4587 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4589 /* don't scroll over playfield boundaries */
4590 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4591 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4594 RedrawPlayfield(TRUE, 0,0,0,0);
4599 int scroll_xx, scroll_yy;
4601 if (!level.shifted_relocation || center_screen)
4603 /* visible relocation (with scrolling), with centering of screen */
4605 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4606 x > SBX_Right + MIDPOSX ? SBX_Right :
4609 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4610 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4615 /* visible relocation (with scrolling), but do not center screen */
4617 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4618 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4621 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4622 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4625 int offset_x = x + (scroll_x - center_scroll_x);
4626 int offset_y = y + (scroll_y - center_scroll_y);
4628 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4629 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4630 offset_x - MIDPOSX);
4632 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4633 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4634 offset_y - MIDPOSY);
4639 /* visible relocation (with scrolling), with centering of screen */
4641 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4642 x > SBX_Right + MIDPOSX ? SBX_Right :
4645 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4646 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4650 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4652 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4655 int fx = FX, fy = FY;
4657 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4658 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4660 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4666 fx += dx * TILEX / 2;
4667 fy += dy * TILEY / 2;
4669 ScrollLevel(dx, dy);
4672 /* scroll in two steps of half tile size to make things smoother */
4673 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4675 Delay(wait_delay_value);
4677 /* scroll second step to align at full tile size */
4679 Delay(wait_delay_value);
4684 Delay(wait_delay_value);
4688 void RelocatePlayer(int jx, int jy, int el_player_raw)
4690 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4691 int player_nr = GET_PLAYER_NR(el_player);
4692 struct PlayerInfo *player = &stored_player[player_nr];
4693 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4694 boolean no_delay = (tape.warp_forward);
4695 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4696 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4697 int old_jx = player->jx;
4698 int old_jy = player->jy;
4699 int old_element = Feld[old_jx][old_jy];
4700 int element = Feld[jx][jy];
4701 boolean player_relocated = (old_jx != jx || old_jy != jy);
4703 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4704 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4705 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4706 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4707 int leave_side_horiz = move_dir_horiz;
4708 int leave_side_vert = move_dir_vert;
4709 int enter_side = enter_side_horiz | enter_side_vert;
4710 int leave_side = leave_side_horiz | leave_side_vert;
4712 if (player->GameOver) /* do not reanimate dead player */
4715 if (!player_relocated) /* no need to relocate the player */
4718 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4720 RemoveField(jx, jy); /* temporarily remove newly placed player */
4721 DrawLevelField(jx, jy);
4724 if (player->present)
4726 while (player->MovPos)
4728 ScrollPlayer(player, SCROLL_GO_ON);
4729 ScrollScreen(NULL, SCROLL_GO_ON);
4731 AdvanceFrameAndPlayerCounters(player->index_nr);
4736 Delay(wait_delay_value);
4739 DrawPlayer(player); /* needed here only to cleanup last field */
4740 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4742 player->is_moving = FALSE;
4745 if (IS_CUSTOM_ELEMENT(old_element))
4746 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4748 player->index_bit, leave_side);
4750 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4752 player->index_bit, leave_side);
4754 Feld[jx][jy] = el_player;
4755 InitPlayerField(jx, jy, el_player, TRUE);
4757 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4759 Feld[jx][jy] = element;
4760 InitField(jx, jy, FALSE);
4763 /* only visually relocate centered player */
4764 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4765 FALSE, level.instant_relocation);
4767 TestIfPlayerTouchesBadThing(jx, jy);
4768 TestIfPlayerTouchesCustomElement(jx, jy);
4770 if (IS_CUSTOM_ELEMENT(element))
4771 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4772 player->index_bit, enter_side);
4774 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4775 player->index_bit, enter_side);
4778 void Explode(int ex, int ey, int phase, int mode)
4784 /* !!! eliminate this variable !!! */
4785 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4787 if (game.explosions_delayed)
4789 ExplodeField[ex][ey] = mode;
4793 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4795 int center_element = Feld[ex][ey];
4796 int artwork_element, explosion_element; /* set these values later */
4799 /* --- This is only really needed (and now handled) in "Impact()". --- */
4800 /* do not explode moving elements that left the explode field in time */
4801 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4802 center_element == EL_EMPTY &&
4803 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4808 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4809 if (mode == EX_TYPE_NORMAL ||
4810 mode == EX_TYPE_CENTER ||
4811 mode == EX_TYPE_CROSS)
4812 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4815 /* remove things displayed in background while burning dynamite */
4816 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4819 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4821 /* put moving element to center field (and let it explode there) */
4822 center_element = MovingOrBlocked2Element(ex, ey);
4823 RemoveMovingField(ex, ey);
4824 Feld[ex][ey] = center_element;
4827 /* now "center_element" is finally determined -- set related values now */
4828 artwork_element = center_element; /* for custom player artwork */
4829 explosion_element = center_element; /* for custom player artwork */
4831 if (IS_PLAYER(ex, ey))
4833 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4835 artwork_element = stored_player[player_nr].artwork_element;
4837 if (level.use_explosion_element[player_nr])
4839 explosion_element = level.explosion_element[player_nr];
4840 artwork_element = explosion_element;
4845 if (mode == EX_TYPE_NORMAL ||
4846 mode == EX_TYPE_CENTER ||
4847 mode == EX_TYPE_CROSS)
4848 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4851 last_phase = element_info[explosion_element].explosion_delay + 1;
4853 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4855 int xx = x - ex + 1;
4856 int yy = y - ey + 1;
4859 if (!IN_LEV_FIELD(x, y) ||
4860 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4861 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4864 element = Feld[x][y];
4866 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4868 element = MovingOrBlocked2Element(x, y);
4870 if (!IS_EXPLOSION_PROOF(element))
4871 RemoveMovingField(x, y);
4874 /* indestructible elements can only explode in center (but not flames) */
4875 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4876 mode == EX_TYPE_BORDER)) ||
4877 element == EL_FLAMES)
4880 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4881 behaviour, for example when touching a yamyam that explodes to rocks
4882 with active deadly shield, a rock is created under the player !!! */
4883 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4885 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4886 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4887 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4889 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4892 if (IS_ACTIVE_BOMB(element))
4894 /* re-activate things under the bomb like gate or penguin */
4895 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4902 /* save walkable background elements while explosion on same tile */
4903 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4904 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4905 Back[x][y] = element;
4907 /* ignite explodable elements reached by other explosion */
4908 if (element == EL_EXPLOSION)
4909 element = Store2[x][y];
4911 if (AmoebaNr[x][y] &&
4912 (element == EL_AMOEBA_FULL ||
4913 element == EL_BD_AMOEBA ||
4914 element == EL_AMOEBA_GROWING))
4916 AmoebaCnt[AmoebaNr[x][y]]--;
4917 AmoebaCnt2[AmoebaNr[x][y]]--;
4922 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4924 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4926 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4928 if (PLAYERINFO(ex, ey)->use_murphy)
4929 Store[x][y] = EL_EMPTY;
4932 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4933 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4934 else if (ELEM_IS_PLAYER(center_element))
4935 Store[x][y] = EL_EMPTY;
4936 else if (center_element == EL_YAMYAM)
4937 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4938 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4939 Store[x][y] = element_info[center_element].content.e[xx][yy];
4941 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4942 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4943 otherwise) -- FIX THIS !!! */
4944 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4945 Store[x][y] = element_info[element].content.e[1][1];
4947 else if (!CAN_EXPLODE(element))
4948 Store[x][y] = element_info[element].content.e[1][1];
4951 Store[x][y] = EL_EMPTY;
4953 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4954 center_element == EL_AMOEBA_TO_DIAMOND)
4955 Store2[x][y] = element;
4957 Feld[x][y] = EL_EXPLOSION;
4958 GfxElement[x][y] = artwork_element;
4960 ExplodePhase[x][y] = 1;
4961 ExplodeDelay[x][y] = last_phase;
4966 if (center_element == EL_YAMYAM)
4967 game.yamyam_content_nr =
4968 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4980 GfxFrame[x][y] = 0; /* restart explosion animation */
4982 last_phase = ExplodeDelay[x][y];
4984 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4988 /* activate this even in non-DEBUG version until cause for crash in
4989 getGraphicAnimationFrame() (see below) is found and eliminated */
4995 /* this can happen if the player leaves an explosion just in time */
4996 if (GfxElement[x][y] == EL_UNDEFINED)
4997 GfxElement[x][y] = EL_EMPTY;
4999 if (GfxElement[x][y] == EL_UNDEFINED)
5002 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5003 printf("Explode(): This should never happen!\n");
5006 GfxElement[x][y] = EL_EMPTY;
5012 border_element = Store2[x][y];
5013 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5014 border_element = StorePlayer[x][y];
5016 if (phase == element_info[border_element].ignition_delay ||
5017 phase == last_phase)
5019 boolean border_explosion = FALSE;
5021 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5022 !PLAYER_EXPLOSION_PROTECTED(x, y))
5024 KillPlayerUnlessExplosionProtected(x, y);
5025 border_explosion = TRUE;
5027 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5029 Feld[x][y] = Store2[x][y];
5032 border_explosion = TRUE;
5034 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5036 AmoebeUmwandeln(x, y);
5038 border_explosion = TRUE;
5041 /* if an element just explodes due to another explosion (chain-reaction),
5042 do not immediately end the new explosion when it was the last frame of
5043 the explosion (as it would be done in the following "if"-statement!) */
5044 if (border_explosion && phase == last_phase)
5048 if (phase == last_phase)
5052 element = Feld[x][y] = Store[x][y];
5053 Store[x][y] = Store2[x][y] = 0;
5054 GfxElement[x][y] = EL_UNDEFINED;
5056 /* player can escape from explosions and might therefore be still alive */
5057 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5058 element <= EL_PLAYER_IS_EXPLODING_4)
5060 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5061 int explosion_element = EL_PLAYER_1 + player_nr;
5062 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5063 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5065 if (level.use_explosion_element[player_nr])
5066 explosion_element = level.explosion_element[player_nr];
5068 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5069 element_info[explosion_element].content.e[xx][yy]);
5072 /* restore probably existing indestructible background element */
5073 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5074 element = Feld[x][y] = Back[x][y];
5077 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5078 GfxDir[x][y] = MV_NONE;
5079 ChangeDelay[x][y] = 0;
5080 ChangePage[x][y] = -1;
5082 #if USE_NEW_CUSTOM_VALUE
5083 CustomValue[x][y] = 0;
5086 InitField_WithBug2(x, y, FALSE);
5088 DrawLevelField(x, y);
5090 TestIfElementTouchesCustomElement(x, y);
5092 if (GFX_CRUMBLED(element))
5093 DrawLevelFieldCrumbledSandNeighbours(x, y);
5095 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5096 StorePlayer[x][y] = 0;
5098 if (ELEM_IS_PLAYER(element))
5099 RelocatePlayer(x, y, element);
5101 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5103 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5104 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5107 DrawLevelFieldCrumbledSand(x, y);
5109 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5111 DrawLevelElement(x, y, Back[x][y]);
5112 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5114 else if (IS_WALKABLE_UNDER(Back[x][y]))
5116 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5117 DrawLevelElementThruMask(x, y, Back[x][y]);
5119 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5120 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5124 void DynaExplode(int ex, int ey)
5127 int dynabomb_element = Feld[ex][ey];
5128 int dynabomb_size = 1;
5129 boolean dynabomb_xl = FALSE;
5130 struct PlayerInfo *player;
5131 static int xy[4][2] =
5139 if (IS_ACTIVE_BOMB(dynabomb_element))
5141 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5142 dynabomb_size = player->dynabomb_size;
5143 dynabomb_xl = player->dynabomb_xl;
5144 player->dynabombs_left++;
5147 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5149 for (i = 0; i < NUM_DIRECTIONS; i++)
5151 for (j = 1; j <= dynabomb_size; j++)
5153 int x = ex + j * xy[i][0];
5154 int y = ey + j * xy[i][1];
5157 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5160 element = Feld[x][y];
5162 /* do not restart explosions of fields with active bombs */
5163 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5166 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5168 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5169 !IS_DIGGABLE(element) && !dynabomb_xl)
5175 void Bang(int x, int y)
5177 int element = MovingOrBlocked2Element(x, y);
5178 int explosion_type = EX_TYPE_NORMAL;
5180 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5182 struct PlayerInfo *player = PLAYERINFO(x, y);
5184 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5185 player->element_nr);
5187 if (level.use_explosion_element[player->index_nr])
5189 int explosion_element = level.explosion_element[player->index_nr];
5191 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5192 explosion_type = EX_TYPE_CROSS;
5193 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5194 explosion_type = EX_TYPE_CENTER;
5202 case EL_BD_BUTTERFLY:
5205 case EL_DARK_YAMYAM:
5209 RaiseScoreElement(element);
5212 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5213 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5214 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5215 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5216 case EL_DYNABOMB_INCREASE_NUMBER:
5217 case EL_DYNABOMB_INCREASE_SIZE:
5218 case EL_DYNABOMB_INCREASE_POWER:
5219 explosion_type = EX_TYPE_DYNA;
5222 case EL_DC_LANDMINE:
5224 case EL_EM_EXIT_OPEN:
5225 case EL_EM_STEEL_EXIT_OPEN:
5227 explosion_type = EX_TYPE_CENTER;
5232 case EL_LAMP_ACTIVE:
5233 case EL_AMOEBA_TO_DIAMOND:
5234 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5235 explosion_type = EX_TYPE_CENTER;
5239 if (element_info[element].explosion_type == EXPLODES_CROSS)
5240 explosion_type = EX_TYPE_CROSS;
5241 else if (element_info[element].explosion_type == EXPLODES_1X1)
5242 explosion_type = EX_TYPE_CENTER;
5246 if (explosion_type == EX_TYPE_DYNA)
5249 Explode(x, y, EX_PHASE_START, explosion_type);
5251 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5254 void SplashAcid(int x, int y)
5256 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5257 (!IN_LEV_FIELD(x - 1, y - 2) ||
5258 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5259 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5261 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5262 (!IN_LEV_FIELD(x + 1, y - 2) ||
5263 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5264 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5266 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5269 static void InitBeltMovement()
5271 static int belt_base_element[4] =
5273 EL_CONVEYOR_BELT_1_LEFT,
5274 EL_CONVEYOR_BELT_2_LEFT,
5275 EL_CONVEYOR_BELT_3_LEFT,
5276 EL_CONVEYOR_BELT_4_LEFT
5278 static int belt_base_active_element[4] =
5280 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5281 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5282 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5283 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5288 /* set frame order for belt animation graphic according to belt direction */
5289 for (i = 0; i < NUM_BELTS; i++)
5293 for (j = 0; j < NUM_BELT_PARTS; j++)
5295 int element = belt_base_active_element[belt_nr] + j;
5296 int graphic = el2img(element);
5298 if (game.belt_dir[i] == MV_LEFT)
5299 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5301 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5305 SCAN_PLAYFIELD(x, y)
5307 int element = Feld[x][y];
5309 for (i = 0; i < NUM_BELTS; i++)
5311 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5313 int e_belt_nr = getBeltNrFromBeltElement(element);
5316 if (e_belt_nr == belt_nr)
5318 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5320 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5327 static void ToggleBeltSwitch(int x, int y)
5329 static int belt_base_element[4] =
5331 EL_CONVEYOR_BELT_1_LEFT,
5332 EL_CONVEYOR_BELT_2_LEFT,
5333 EL_CONVEYOR_BELT_3_LEFT,
5334 EL_CONVEYOR_BELT_4_LEFT
5336 static int belt_base_active_element[4] =
5338 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5339 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5340 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5341 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5343 static int belt_base_switch_element[4] =
5345 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5346 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5347 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5348 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5350 static int belt_move_dir[4] =
5358 int element = Feld[x][y];
5359 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5360 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5361 int belt_dir = belt_move_dir[belt_dir_nr];
5364 if (!IS_BELT_SWITCH(element))
5367 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5368 game.belt_dir[belt_nr] = belt_dir;
5370 if (belt_dir_nr == 3)
5373 /* set frame order for belt animation graphic according to belt direction */
5374 for (i = 0; i < NUM_BELT_PARTS; i++)
5376 int element = belt_base_active_element[belt_nr] + i;
5377 int graphic = el2img(element);
5379 if (belt_dir == MV_LEFT)
5380 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5382 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5385 SCAN_PLAYFIELD(xx, yy)
5387 int element = Feld[xx][yy];
5389 if (IS_BELT_SWITCH(element))
5391 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5393 if (e_belt_nr == belt_nr)
5395 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5396 DrawLevelField(xx, yy);
5399 else if (IS_BELT(element) && belt_dir != MV_NONE)
5401 int e_belt_nr = getBeltNrFromBeltElement(element);
5403 if (e_belt_nr == belt_nr)
5405 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5407 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5408 DrawLevelField(xx, yy);
5411 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5413 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5415 if (e_belt_nr == belt_nr)
5417 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5419 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5420 DrawLevelField(xx, yy);
5426 static void ToggleSwitchgateSwitch(int x, int y)
5430 game.switchgate_pos = !game.switchgate_pos;
5432 SCAN_PLAYFIELD(xx, yy)
5434 int element = Feld[xx][yy];
5436 #if !USE_BOTH_SWITCHGATE_SWITCHES
5437 if (element == EL_SWITCHGATE_SWITCH_UP ||
5438 element == EL_SWITCHGATE_SWITCH_DOWN)
5440 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5441 DrawLevelField(xx, yy);
5443 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5444 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5446 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5447 DrawLevelField(xx, yy);
5450 if (element == EL_SWITCHGATE_SWITCH_UP)
5452 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5453 DrawLevelField(xx, yy);
5455 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5457 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5458 DrawLevelField(xx, yy);
5460 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5462 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5463 DrawLevelField(xx, yy);
5465 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5467 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5468 DrawLevelField(xx, yy);
5471 else if (element == EL_SWITCHGATE_OPEN ||
5472 element == EL_SWITCHGATE_OPENING)
5474 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5476 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5478 else if (element == EL_SWITCHGATE_CLOSED ||
5479 element == EL_SWITCHGATE_CLOSING)
5481 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5483 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5488 static int getInvisibleActiveFromInvisibleElement(int element)
5490 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5491 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5492 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5496 static int getInvisibleFromInvisibleActiveElement(int element)
5498 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5499 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5500 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5504 static void RedrawAllLightSwitchesAndInvisibleElements()
5508 SCAN_PLAYFIELD(x, y)
5510 int element = Feld[x][y];
5512 if (element == EL_LIGHT_SWITCH &&
5513 game.light_time_left > 0)
5515 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5516 DrawLevelField(x, y);
5518 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5519 game.light_time_left == 0)
5521 Feld[x][y] = EL_LIGHT_SWITCH;
5522 DrawLevelField(x, y);
5524 else if (element == EL_EMC_DRIPPER &&
5525 game.light_time_left > 0)
5527 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5528 DrawLevelField(x, y);
5530 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5531 game.light_time_left == 0)
5533 Feld[x][y] = EL_EMC_DRIPPER;
5534 DrawLevelField(x, y);
5536 else if (element == EL_INVISIBLE_STEELWALL ||
5537 element == EL_INVISIBLE_WALL ||
5538 element == EL_INVISIBLE_SAND)
5540 if (game.light_time_left > 0)
5541 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5543 DrawLevelField(x, y);
5545 /* uncrumble neighbour fields, if needed */
5546 if (element == EL_INVISIBLE_SAND)
5547 DrawLevelFieldCrumbledSandNeighbours(x, y);
5549 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5550 element == EL_INVISIBLE_WALL_ACTIVE ||
5551 element == EL_INVISIBLE_SAND_ACTIVE)
5553 if (game.light_time_left == 0)
5554 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5556 DrawLevelField(x, y);
5558 /* re-crumble neighbour fields, if needed */
5559 if (element == EL_INVISIBLE_SAND)
5560 DrawLevelFieldCrumbledSandNeighbours(x, y);
5565 static void RedrawAllInvisibleElementsForLenses()
5569 SCAN_PLAYFIELD(x, y)
5571 int element = Feld[x][y];
5573 if (element == EL_EMC_DRIPPER &&
5574 game.lenses_time_left > 0)
5576 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5577 DrawLevelField(x, y);
5579 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5580 game.lenses_time_left == 0)
5582 Feld[x][y] = EL_EMC_DRIPPER;
5583 DrawLevelField(x, y);
5585 else if (element == EL_INVISIBLE_STEELWALL ||
5586 element == EL_INVISIBLE_WALL ||
5587 element == EL_INVISIBLE_SAND)
5589 if (game.lenses_time_left > 0)
5590 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5592 DrawLevelField(x, y);
5594 /* uncrumble neighbour fields, if needed */
5595 if (element == EL_INVISIBLE_SAND)
5596 DrawLevelFieldCrumbledSandNeighbours(x, y);
5598 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5599 element == EL_INVISIBLE_WALL_ACTIVE ||
5600 element == EL_INVISIBLE_SAND_ACTIVE)
5602 if (game.lenses_time_left == 0)
5603 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5605 DrawLevelField(x, y);
5607 /* re-crumble neighbour fields, if needed */
5608 if (element == EL_INVISIBLE_SAND)
5609 DrawLevelFieldCrumbledSandNeighbours(x, y);
5614 static void RedrawAllInvisibleElementsForMagnifier()
5618 SCAN_PLAYFIELD(x, y)
5620 int element = Feld[x][y];
5622 if (element == EL_EMC_FAKE_GRASS &&
5623 game.magnify_time_left > 0)
5625 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5626 DrawLevelField(x, y);
5628 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5629 game.magnify_time_left == 0)
5631 Feld[x][y] = EL_EMC_FAKE_GRASS;
5632 DrawLevelField(x, y);
5634 else if (IS_GATE_GRAY(element) &&
5635 game.magnify_time_left > 0)
5637 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5638 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5639 IS_EM_GATE_GRAY(element) ?
5640 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5641 IS_EMC_GATE_GRAY(element) ?
5642 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5644 DrawLevelField(x, y);
5646 else if (IS_GATE_GRAY_ACTIVE(element) &&
5647 game.magnify_time_left == 0)
5649 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5650 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5651 IS_EM_GATE_GRAY_ACTIVE(element) ?
5652 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5653 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5654 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5656 DrawLevelField(x, y);
5661 static void ToggleLightSwitch(int x, int y)
5663 int element = Feld[x][y];
5665 game.light_time_left =
5666 (element == EL_LIGHT_SWITCH ?
5667 level.time_light * FRAMES_PER_SECOND : 0);
5669 RedrawAllLightSwitchesAndInvisibleElements();
5672 static void ActivateTimegateSwitch(int x, int y)
5676 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5678 SCAN_PLAYFIELD(xx, yy)
5680 int element = Feld[xx][yy];
5682 if (element == EL_TIMEGATE_CLOSED ||
5683 element == EL_TIMEGATE_CLOSING)
5685 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5686 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5690 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5692 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5693 DrawLevelField(xx, yy);
5700 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5701 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5703 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5707 void Impact(int x, int y)
5709 boolean last_line = (y == lev_fieldy - 1);
5710 boolean object_hit = FALSE;
5711 boolean impact = (last_line || object_hit);
5712 int element = Feld[x][y];
5713 int smashed = EL_STEELWALL;
5715 if (!last_line) /* check if element below was hit */
5717 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5720 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5721 MovDir[x][y + 1] != MV_DOWN ||
5722 MovPos[x][y + 1] <= TILEY / 2));
5724 /* do not smash moving elements that left the smashed field in time */
5725 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5726 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5729 #if USE_QUICKSAND_IMPACT_BUGFIX
5730 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5732 RemoveMovingField(x, y + 1);
5733 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5734 Feld[x][y + 2] = EL_ROCK;
5735 DrawLevelField(x, y + 2);
5740 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5742 RemoveMovingField(x, y + 1);
5743 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5744 Feld[x][y + 2] = EL_ROCK;
5745 DrawLevelField(x, y + 2);
5752 smashed = MovingOrBlocked2Element(x, y + 1);
5754 impact = (last_line || object_hit);
5757 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5759 SplashAcid(x, y + 1);
5763 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5764 /* only reset graphic animation if graphic really changes after impact */
5766 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5768 ResetGfxAnimation(x, y);
5769 DrawLevelField(x, y);
5772 if (impact && CAN_EXPLODE_IMPACT(element))
5777 else if (impact && element == EL_PEARL &&
5778 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5780 ResetGfxAnimation(x, y);
5782 Feld[x][y] = EL_PEARL_BREAKING;
5783 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5786 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5788 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5793 if (impact && element == EL_AMOEBA_DROP)
5795 if (object_hit && IS_PLAYER(x, y + 1))
5796 KillPlayerUnlessEnemyProtected(x, y + 1);
5797 else if (object_hit && smashed == EL_PENGUIN)
5801 Feld[x][y] = EL_AMOEBA_GROWING;
5802 Store[x][y] = EL_AMOEBA_WET;
5804 ResetRandomAnimationValue(x, y);
5809 if (object_hit) /* check which object was hit */
5811 if ((CAN_PASS_MAGIC_WALL(element) &&
5812 (smashed == EL_MAGIC_WALL ||
5813 smashed == EL_BD_MAGIC_WALL)) ||
5814 (CAN_PASS_DC_MAGIC_WALL(element) &&
5815 smashed == EL_DC_MAGIC_WALL))
5818 int activated_magic_wall =
5819 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5820 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5821 EL_DC_MAGIC_WALL_ACTIVE);
5823 /* activate magic wall / mill */
5824 SCAN_PLAYFIELD(xx, yy)
5826 if (Feld[xx][yy] == smashed)
5827 Feld[xx][yy] = activated_magic_wall;
5830 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5831 game.magic_wall_active = TRUE;
5833 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5834 SND_MAGIC_WALL_ACTIVATING :
5835 smashed == EL_BD_MAGIC_WALL ?
5836 SND_BD_MAGIC_WALL_ACTIVATING :
5837 SND_DC_MAGIC_WALL_ACTIVATING));
5840 if (IS_PLAYER(x, y + 1))
5842 if (CAN_SMASH_PLAYER(element))
5844 KillPlayerUnlessEnemyProtected(x, y + 1);
5848 else if (smashed == EL_PENGUIN)
5850 if (CAN_SMASH_PLAYER(element))
5856 else if (element == EL_BD_DIAMOND)
5858 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5864 else if (((element == EL_SP_INFOTRON ||
5865 element == EL_SP_ZONK) &&
5866 (smashed == EL_SP_SNIKSNAK ||
5867 smashed == EL_SP_ELECTRON ||
5868 smashed == EL_SP_DISK_ORANGE)) ||
5869 (element == EL_SP_INFOTRON &&
5870 smashed == EL_SP_DISK_YELLOW))
5875 else if (CAN_SMASH_EVERYTHING(element))
5877 if (IS_CLASSIC_ENEMY(smashed) ||
5878 CAN_EXPLODE_SMASHED(smashed))
5883 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5885 if (smashed == EL_LAMP ||
5886 smashed == EL_LAMP_ACTIVE)
5891 else if (smashed == EL_NUT)
5893 Feld[x][y + 1] = EL_NUT_BREAKING;
5894 PlayLevelSound(x, y, SND_NUT_BREAKING);
5895 RaiseScoreElement(EL_NUT);
5898 else if (smashed == EL_PEARL)
5900 ResetGfxAnimation(x, y);
5902 Feld[x][y + 1] = EL_PEARL_BREAKING;
5903 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5906 else if (smashed == EL_DIAMOND)
5908 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5909 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5912 else if (IS_BELT_SWITCH(smashed))
5914 ToggleBeltSwitch(x, y + 1);
5916 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5917 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5918 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5919 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5921 ToggleSwitchgateSwitch(x, y + 1);
5923 else if (smashed == EL_LIGHT_SWITCH ||
5924 smashed == EL_LIGHT_SWITCH_ACTIVE)
5926 ToggleLightSwitch(x, y + 1);
5931 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5934 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5936 CheckElementChangeBySide(x, y + 1, smashed, element,
5937 CE_SWITCHED, CH_SIDE_TOP);
5938 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5944 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5949 /* play sound of magic wall / mill */
5951 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5952 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5953 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5955 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5956 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5957 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5958 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5959 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5960 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5965 /* play sound of object that hits the ground */
5966 if (last_line || object_hit)
5967 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5970 inline static void TurnRoundExt(int x, int y)
5982 { 0, 0 }, { 0, 0 }, { 0, 0 },
5987 int left, right, back;
5991 { MV_DOWN, MV_UP, MV_RIGHT },
5992 { MV_UP, MV_DOWN, MV_LEFT },
5994 { MV_LEFT, MV_RIGHT, MV_DOWN },
5998 { MV_RIGHT, MV_LEFT, MV_UP }
6001 int element = Feld[x][y];
6002 int move_pattern = element_info[element].move_pattern;
6004 int old_move_dir = MovDir[x][y];
6005 int left_dir = turn[old_move_dir].left;
6006 int right_dir = turn[old_move_dir].right;
6007 int back_dir = turn[old_move_dir].back;
6009 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6010 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6011 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6012 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6014 int left_x = x + left_dx, left_y = y + left_dy;
6015 int right_x = x + right_dx, right_y = y + right_dy;
6016 int move_x = x + move_dx, move_y = y + move_dy;
6020 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6022 TestIfBadThingTouchesOtherBadThing(x, y);
6024 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6025 MovDir[x][y] = right_dir;
6026 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6027 MovDir[x][y] = left_dir;
6029 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6031 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6034 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6036 TestIfBadThingTouchesOtherBadThing(x, y);
6038 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6039 MovDir[x][y] = left_dir;
6040 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6041 MovDir[x][y] = right_dir;
6043 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6045 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6048 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6050 TestIfBadThingTouchesOtherBadThing(x, y);
6052 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6053 MovDir[x][y] = left_dir;
6054 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6055 MovDir[x][y] = right_dir;
6057 if (MovDir[x][y] != old_move_dir)
6060 else if (element == EL_YAMYAM)
6062 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6063 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6065 if (can_turn_left && can_turn_right)
6066 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6067 else if (can_turn_left)
6068 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6069 else if (can_turn_right)
6070 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6072 MovDir[x][y] = back_dir;
6074 MovDelay[x][y] = 16 + 16 * RND(3);
6076 else if (element == EL_DARK_YAMYAM)
6078 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6080 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6083 if (can_turn_left && can_turn_right)
6084 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6085 else if (can_turn_left)
6086 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6087 else if (can_turn_right)
6088 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6090 MovDir[x][y] = back_dir;
6092 MovDelay[x][y] = 16 + 16 * RND(3);
6094 else if (element == EL_PACMAN)
6096 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6097 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6099 if (can_turn_left && can_turn_right)
6100 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6101 else if (can_turn_left)
6102 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6103 else if (can_turn_right)
6104 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6106 MovDir[x][y] = back_dir;
6108 MovDelay[x][y] = 6 + RND(40);
6110 else if (element == EL_PIG)
6112 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6113 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6114 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6115 boolean should_turn_left, should_turn_right, should_move_on;
6117 int rnd = RND(rnd_value);
6119 should_turn_left = (can_turn_left &&
6121 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6122 y + back_dy + left_dy)));
6123 should_turn_right = (can_turn_right &&
6125 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6126 y + back_dy + right_dy)));
6127 should_move_on = (can_move_on &&
6130 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6131 y + move_dy + left_dy) ||
6132 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6133 y + move_dy + right_dy)));
6135 if (should_turn_left || should_turn_right || should_move_on)
6137 if (should_turn_left && should_turn_right && should_move_on)
6138 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6139 rnd < 2 * rnd_value / 3 ? right_dir :
6141 else if (should_turn_left && should_turn_right)
6142 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6143 else if (should_turn_left && should_move_on)
6144 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6145 else if (should_turn_right && should_move_on)
6146 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6147 else if (should_turn_left)
6148 MovDir[x][y] = left_dir;
6149 else if (should_turn_right)
6150 MovDir[x][y] = right_dir;
6151 else if (should_move_on)
6152 MovDir[x][y] = old_move_dir;
6154 else if (can_move_on && rnd > rnd_value / 8)
6155 MovDir[x][y] = old_move_dir;
6156 else if (can_turn_left && can_turn_right)
6157 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6158 else if (can_turn_left && rnd > rnd_value / 8)
6159 MovDir[x][y] = left_dir;
6160 else if (can_turn_right && rnd > rnd_value/8)
6161 MovDir[x][y] = right_dir;
6163 MovDir[x][y] = back_dir;
6165 xx = x + move_xy[MovDir[x][y]].dx;
6166 yy = y + move_xy[MovDir[x][y]].dy;
6168 if (!IN_LEV_FIELD(xx, yy) ||
6169 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6170 MovDir[x][y] = old_move_dir;
6174 else if (element == EL_DRAGON)
6176 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6177 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6178 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6180 int rnd = RND(rnd_value);
6182 if (can_move_on && rnd > rnd_value / 8)
6183 MovDir[x][y] = old_move_dir;
6184 else if (can_turn_left && can_turn_right)
6185 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6186 else if (can_turn_left && rnd > rnd_value / 8)
6187 MovDir[x][y] = left_dir;
6188 else if (can_turn_right && rnd > rnd_value / 8)
6189 MovDir[x][y] = right_dir;
6191 MovDir[x][y] = back_dir;
6193 xx = x + move_xy[MovDir[x][y]].dx;
6194 yy = y + move_xy[MovDir[x][y]].dy;
6196 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6197 MovDir[x][y] = old_move_dir;
6201 else if (element == EL_MOLE)
6203 boolean can_move_on =
6204 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6205 IS_AMOEBOID(Feld[move_x][move_y]) ||
6206 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6209 boolean can_turn_left =
6210 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6211 IS_AMOEBOID(Feld[left_x][left_y])));
6213 boolean can_turn_right =
6214 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6215 IS_AMOEBOID(Feld[right_x][right_y])));
6217 if (can_turn_left && can_turn_right)
6218 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6219 else if (can_turn_left)
6220 MovDir[x][y] = left_dir;
6222 MovDir[x][y] = right_dir;
6225 if (MovDir[x][y] != old_move_dir)
6228 else if (element == EL_BALLOON)
6230 MovDir[x][y] = game.wind_direction;
6233 else if (element == EL_SPRING)
6235 #if USE_NEW_SPRING_BUMPER
6236 if (MovDir[x][y] & MV_HORIZONTAL)
6238 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6239 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6241 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6242 ResetGfxAnimation(move_x, move_y);
6243 DrawLevelField(move_x, move_y);
6245 MovDir[x][y] = back_dir;
6247 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6248 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6249 MovDir[x][y] = MV_NONE;
6252 if (MovDir[x][y] & MV_HORIZONTAL &&
6253 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6254 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6255 MovDir[x][y] = MV_NONE;
6260 else if (element == EL_ROBOT ||
6261 element == EL_SATELLITE ||
6262 element == EL_PENGUIN ||
6263 element == EL_EMC_ANDROID)
6265 int attr_x = -1, attr_y = -1;
6276 for (i = 0; i < MAX_PLAYERS; i++)
6278 struct PlayerInfo *player = &stored_player[i];
6279 int jx = player->jx, jy = player->jy;
6281 if (!player->active)
6285 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6293 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6294 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6295 game.engine_version < VERSION_IDENT(3,1,0,0)))
6301 if (element == EL_PENGUIN)
6304 static int xy[4][2] =
6312 for (i = 0; i < NUM_DIRECTIONS; i++)
6314 int ex = x + xy[i][0];
6315 int ey = y + xy[i][1];
6317 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6318 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6319 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6320 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6329 MovDir[x][y] = MV_NONE;
6331 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6332 else if (attr_x > x)
6333 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6335 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6336 else if (attr_y > y)
6337 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6339 if (element == EL_ROBOT)
6343 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6344 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6345 Moving2Blocked(x, y, &newx, &newy);
6347 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6348 MovDelay[x][y] = 8 + 8 * !RND(3);
6350 MovDelay[x][y] = 16;
6352 else if (element == EL_PENGUIN)
6358 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6360 boolean first_horiz = RND(2);
6361 int new_move_dir = MovDir[x][y];
6364 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6365 Moving2Blocked(x, y, &newx, &newy);
6367 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6371 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6372 Moving2Blocked(x, y, &newx, &newy);
6374 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6377 MovDir[x][y] = old_move_dir;
6381 else if (element == EL_SATELLITE)
6387 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6389 boolean first_horiz = RND(2);
6390 int new_move_dir = MovDir[x][y];
6393 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6394 Moving2Blocked(x, y, &newx, &newy);
6396 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6400 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6401 Moving2Blocked(x, y, &newx, &newy);
6403 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6406 MovDir[x][y] = old_move_dir;
6410 else if (element == EL_EMC_ANDROID)
6412 static int check_pos[16] =
6414 -1, /* 0 => (invalid) */
6415 7, /* 1 => MV_LEFT */
6416 3, /* 2 => MV_RIGHT */
6417 -1, /* 3 => (invalid) */
6419 0, /* 5 => MV_LEFT | MV_UP */
6420 2, /* 6 => MV_RIGHT | MV_UP */
6421 -1, /* 7 => (invalid) */
6422 5, /* 8 => MV_DOWN */
6423 6, /* 9 => MV_LEFT | MV_DOWN */
6424 4, /* 10 => MV_RIGHT | MV_DOWN */
6425 -1, /* 11 => (invalid) */
6426 -1, /* 12 => (invalid) */
6427 -1, /* 13 => (invalid) */
6428 -1, /* 14 => (invalid) */
6429 -1, /* 15 => (invalid) */
6437 { -1, -1, MV_LEFT | MV_UP },
6439 { +1, -1, MV_RIGHT | MV_UP },
6440 { +1, 0, MV_RIGHT },
6441 { +1, +1, MV_RIGHT | MV_DOWN },
6443 { -1, +1, MV_LEFT | MV_DOWN },
6446 int start_pos, check_order;
6447 boolean can_clone = FALSE;
6450 /* check if there is any free field around current position */
6451 for (i = 0; i < 8; i++)
6453 int newx = x + check_xy[i].dx;
6454 int newy = y + check_xy[i].dy;
6456 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6464 if (can_clone) /* randomly find an element to clone */
6468 start_pos = check_pos[RND(8)];
6469 check_order = (RND(2) ? -1 : +1);
6471 for (i = 0; i < 8; i++)
6473 int pos_raw = start_pos + i * check_order;
6474 int pos = (pos_raw + 8) % 8;
6475 int newx = x + check_xy[pos].dx;
6476 int newy = y + check_xy[pos].dy;
6478 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6480 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6481 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6483 Store[x][y] = Feld[newx][newy];
6492 if (can_clone) /* randomly find a direction to move */
6496 start_pos = check_pos[RND(8)];
6497 check_order = (RND(2) ? -1 : +1);
6499 for (i = 0; i < 8; i++)
6501 int pos_raw = start_pos + i * check_order;
6502 int pos = (pos_raw + 8) % 8;
6503 int newx = x + check_xy[pos].dx;
6504 int newy = y + check_xy[pos].dy;
6505 int new_move_dir = check_xy[pos].dir;
6507 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6509 MovDir[x][y] = new_move_dir;
6510 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6519 if (can_clone) /* cloning and moving successful */
6522 /* cannot clone -- try to move towards player */
6524 start_pos = check_pos[MovDir[x][y] & 0x0f];
6525 check_order = (RND(2) ? -1 : +1);
6527 for (i = 0; i < 3; i++)
6529 /* first check start_pos, then previous/next or (next/previous) pos */
6530 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6531 int pos = (pos_raw + 8) % 8;
6532 int newx = x + check_xy[pos].dx;
6533 int newy = y + check_xy[pos].dy;
6534 int new_move_dir = check_xy[pos].dir;
6536 if (IS_PLAYER(newx, newy))
6539 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6541 MovDir[x][y] = new_move_dir;
6542 MovDelay[x][y] = level.android_move_time * 8 + 1;
6549 else if (move_pattern == MV_TURNING_LEFT ||
6550 move_pattern == MV_TURNING_RIGHT ||
6551 move_pattern == MV_TURNING_LEFT_RIGHT ||
6552 move_pattern == MV_TURNING_RIGHT_LEFT ||
6553 move_pattern == MV_TURNING_RANDOM ||
6554 move_pattern == MV_ALL_DIRECTIONS)
6556 boolean can_turn_left =
6557 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6558 boolean can_turn_right =
6559 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6561 if (element_info[element].move_stepsize == 0) /* "not moving" */
6564 if (move_pattern == MV_TURNING_LEFT)
6565 MovDir[x][y] = left_dir;
6566 else if (move_pattern == MV_TURNING_RIGHT)
6567 MovDir[x][y] = right_dir;
6568 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6569 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6570 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6571 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6572 else if (move_pattern == MV_TURNING_RANDOM)
6573 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6574 can_turn_right && !can_turn_left ? right_dir :
6575 RND(2) ? left_dir : right_dir);
6576 else if (can_turn_left && can_turn_right)
6577 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6578 else if (can_turn_left)
6579 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6580 else if (can_turn_right)
6581 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6583 MovDir[x][y] = back_dir;
6585 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6587 else if (move_pattern == MV_HORIZONTAL ||
6588 move_pattern == MV_VERTICAL)
6590 if (move_pattern & old_move_dir)
6591 MovDir[x][y] = back_dir;
6592 else if (move_pattern == MV_HORIZONTAL)
6593 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6594 else if (move_pattern == MV_VERTICAL)
6595 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6597 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6599 else if (move_pattern & MV_ANY_DIRECTION)
6601 MovDir[x][y] = move_pattern;
6602 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6604 else if (move_pattern & MV_WIND_DIRECTION)
6606 MovDir[x][y] = game.wind_direction;
6607 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6609 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6611 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6612 MovDir[x][y] = left_dir;
6613 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6614 MovDir[x][y] = right_dir;
6616 if (MovDir[x][y] != old_move_dir)
6617 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6619 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6621 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6622 MovDir[x][y] = right_dir;
6623 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6624 MovDir[x][y] = left_dir;
6626 if (MovDir[x][y] != old_move_dir)
6627 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6629 else if (move_pattern == MV_TOWARDS_PLAYER ||
6630 move_pattern == MV_AWAY_FROM_PLAYER)
6632 int attr_x = -1, attr_y = -1;
6634 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6645 for (i = 0; i < MAX_PLAYERS; i++)
6647 struct PlayerInfo *player = &stored_player[i];
6648 int jx = player->jx, jy = player->jy;
6650 if (!player->active)
6654 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6662 MovDir[x][y] = MV_NONE;
6664 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6665 else if (attr_x > x)
6666 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6668 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6669 else if (attr_y > y)
6670 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6672 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6674 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6676 boolean first_horiz = RND(2);
6677 int new_move_dir = MovDir[x][y];
6679 if (element_info[element].move_stepsize == 0) /* "not moving" */
6681 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6682 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6688 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6689 Moving2Blocked(x, y, &newx, &newy);
6691 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6695 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6696 Moving2Blocked(x, y, &newx, &newy);
6698 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6701 MovDir[x][y] = old_move_dir;
6704 else if (move_pattern == MV_WHEN_PUSHED ||
6705 move_pattern == MV_WHEN_DROPPED)
6707 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6708 MovDir[x][y] = MV_NONE;
6712 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6714 static int test_xy[7][2] =
6724 static int test_dir[7] =
6734 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6735 int move_preference = -1000000; /* start with very low preference */
6736 int new_move_dir = MV_NONE;
6737 int start_test = RND(4);
6740 for (i = 0; i < NUM_DIRECTIONS; i++)
6742 int move_dir = test_dir[start_test + i];
6743 int move_dir_preference;
6745 xx = x + test_xy[start_test + i][0];
6746 yy = y + test_xy[start_test + i][1];
6748 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6749 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6751 new_move_dir = move_dir;
6756 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6759 move_dir_preference = -1 * RunnerVisit[xx][yy];
6760 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6761 move_dir_preference = PlayerVisit[xx][yy];
6763 if (move_dir_preference > move_preference)
6765 /* prefer field that has not been visited for the longest time */
6766 move_preference = move_dir_preference;
6767 new_move_dir = move_dir;
6769 else if (move_dir_preference == move_preference &&
6770 move_dir == old_move_dir)
6772 /* prefer last direction when all directions are preferred equally */
6773 move_preference = move_dir_preference;
6774 new_move_dir = move_dir;
6778 MovDir[x][y] = new_move_dir;
6779 if (old_move_dir != new_move_dir)
6780 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6784 static void TurnRound(int x, int y)
6786 int direction = MovDir[x][y];
6790 GfxDir[x][y] = MovDir[x][y];
6792 if (direction != MovDir[x][y])
6796 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6798 ResetGfxFrame(x, y, FALSE);
6801 static boolean JustBeingPushed(int x, int y)
6805 for (i = 0; i < MAX_PLAYERS; i++)
6807 struct PlayerInfo *player = &stored_player[i];
6809 if (player->active && player->is_pushing && player->MovPos)
6811 int next_jx = player->jx + (player->jx - player->last_jx);
6812 int next_jy = player->jy + (player->jy - player->last_jy);
6814 if (x == next_jx && y == next_jy)
6822 void StartMoving(int x, int y)
6824 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6825 int element = Feld[x][y];
6830 if (MovDelay[x][y] == 0)
6831 GfxAction[x][y] = ACTION_DEFAULT;
6833 if (CAN_FALL(element) && y < lev_fieldy - 1)
6835 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6836 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6837 if (JustBeingPushed(x, y))
6840 if (element == EL_QUICKSAND_FULL)
6842 if (IS_FREE(x, y + 1))
6844 InitMovingField(x, y, MV_DOWN);
6845 started_moving = TRUE;
6847 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6848 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6849 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6850 Store[x][y] = EL_ROCK;
6852 Store[x][y] = EL_ROCK;
6855 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6857 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6859 if (!MovDelay[x][y])
6860 MovDelay[x][y] = TILEY + 1;
6869 Feld[x][y] = EL_QUICKSAND_EMPTY;
6870 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6871 Store[x][y + 1] = Store[x][y];
6874 PlayLevelSoundAction(x, y, ACTION_FILLING);
6877 else if (element == EL_QUICKSAND_FAST_FULL)
6879 if (IS_FREE(x, y + 1))
6881 InitMovingField(x, y, MV_DOWN);
6882 started_moving = TRUE;
6884 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6885 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6886 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6887 Store[x][y] = EL_ROCK;
6889 Store[x][y] = EL_ROCK;
6892 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6894 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6896 if (!MovDelay[x][y])
6897 MovDelay[x][y] = TILEY + 1;
6906 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6907 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6908 Store[x][y + 1] = Store[x][y];
6911 PlayLevelSoundAction(x, y, ACTION_FILLING);
6914 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6915 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6917 InitMovingField(x, y, MV_DOWN);
6918 started_moving = TRUE;
6920 Feld[x][y] = EL_QUICKSAND_FILLING;
6921 Store[x][y] = element;
6923 PlayLevelSoundAction(x, y, ACTION_FILLING);
6925 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6926 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6928 InitMovingField(x, y, MV_DOWN);
6929 started_moving = TRUE;
6931 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6932 Store[x][y] = element;
6934 PlayLevelSoundAction(x, y, ACTION_FILLING);
6936 else if (element == EL_MAGIC_WALL_FULL)
6938 if (IS_FREE(x, y + 1))
6940 InitMovingField(x, y, MV_DOWN);
6941 started_moving = TRUE;
6943 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6944 Store[x][y] = EL_CHANGED(Store[x][y]);
6946 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6948 if (!MovDelay[x][y])
6949 MovDelay[x][y] = TILEY/4 + 1;
6958 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6959 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6960 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6964 else if (element == EL_BD_MAGIC_WALL_FULL)
6966 if (IS_FREE(x, y + 1))
6968 InitMovingField(x, y, MV_DOWN);
6969 started_moving = TRUE;
6971 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6972 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6974 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6976 if (!MovDelay[x][y])
6977 MovDelay[x][y] = TILEY/4 + 1;
6986 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6987 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6988 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6992 else if (element == EL_DC_MAGIC_WALL_FULL)
6994 if (IS_FREE(x, y + 1))
6996 InitMovingField(x, y, MV_DOWN);
6997 started_moving = TRUE;
6999 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7000 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7002 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7004 if (!MovDelay[x][y])
7005 MovDelay[x][y] = TILEY/4 + 1;
7014 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7015 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7016 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7020 else if ((CAN_PASS_MAGIC_WALL(element) &&
7021 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7022 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7023 (CAN_PASS_DC_MAGIC_WALL(element) &&
7024 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7027 InitMovingField(x, y, MV_DOWN);
7028 started_moving = TRUE;
7031 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7032 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7033 EL_DC_MAGIC_WALL_FILLING);
7034 Store[x][y] = element;
7036 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7038 SplashAcid(x, y + 1);
7040 InitMovingField(x, y, MV_DOWN);
7041 started_moving = TRUE;
7043 Store[x][y] = EL_ACID;
7046 #if USE_FIX_IMPACT_COLLISION
7047 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7048 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7050 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7051 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7053 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7054 CAN_FALL(element) && WasJustFalling[x][y] &&
7055 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7057 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7058 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7059 (Feld[x][y + 1] == EL_BLOCKED)))
7061 /* this is needed for a special case not covered by calling "Impact()"
7062 from "ContinueMoving()": if an element moves to a tile directly below
7063 another element which was just falling on that tile (which was empty
7064 in the previous frame), the falling element above would just stop
7065 instead of smashing the element below (in previous version, the above
7066 element was just checked for "moving" instead of "falling", resulting
7067 in incorrect smashes caused by horizontal movement of the above
7068 element; also, the case of the player being the element to smash was
7069 simply not covered here... :-/ ) */
7071 CheckCollision[x][y] = 0;
7072 CheckImpact[x][y] = 0;
7076 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7078 if (MovDir[x][y] == MV_NONE)
7080 InitMovingField(x, y, MV_DOWN);
7081 started_moving = TRUE;
7084 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7086 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7087 MovDir[x][y] = MV_DOWN;
7089 InitMovingField(x, y, MV_DOWN);
7090 started_moving = TRUE;
7092 else if (element == EL_AMOEBA_DROP)
7094 Feld[x][y] = EL_AMOEBA_GROWING;
7095 Store[x][y] = EL_AMOEBA_WET;
7097 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7098 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7099 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7100 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7102 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7103 (IS_FREE(x - 1, y + 1) ||
7104 Feld[x - 1][y + 1] == EL_ACID));
7105 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7106 (IS_FREE(x + 1, y + 1) ||
7107 Feld[x + 1][y + 1] == EL_ACID));
7108 boolean can_fall_any = (can_fall_left || can_fall_right);
7109 boolean can_fall_both = (can_fall_left && can_fall_right);
7110 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7112 #if USE_NEW_ALL_SLIPPERY
7113 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7115 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7116 can_fall_right = FALSE;
7117 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7118 can_fall_left = FALSE;
7119 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7120 can_fall_right = FALSE;
7121 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7122 can_fall_left = FALSE;
7124 can_fall_any = (can_fall_left || can_fall_right);
7125 can_fall_both = FALSE;
7128 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7130 if (slippery_type == SLIPPERY_ONLY_LEFT)
7131 can_fall_right = FALSE;
7132 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7133 can_fall_left = FALSE;
7134 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7135 can_fall_right = FALSE;
7136 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7137 can_fall_left = FALSE;
7139 can_fall_any = (can_fall_left || can_fall_right);
7140 can_fall_both = (can_fall_left && can_fall_right);
7144 #if USE_NEW_ALL_SLIPPERY
7146 #if USE_NEW_SP_SLIPPERY
7147 /* !!! better use the same properties as for custom elements here !!! */
7148 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7149 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7151 can_fall_right = FALSE; /* slip down on left side */
7152 can_fall_both = FALSE;
7157 #if USE_NEW_ALL_SLIPPERY
7160 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7161 can_fall_right = FALSE; /* slip down on left side */
7163 can_fall_left = !(can_fall_right = RND(2));
7165 can_fall_both = FALSE;
7170 if (game.emulation == EMU_BOULDERDASH ||
7171 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7172 can_fall_right = FALSE; /* slip down on left side */
7174 can_fall_left = !(can_fall_right = RND(2));
7176 can_fall_both = FALSE;
7182 /* if not determined otherwise, prefer left side for slipping down */
7183 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7184 started_moving = TRUE;
7188 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7190 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7193 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7194 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7195 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7196 int belt_dir = game.belt_dir[belt_nr];
7198 if ((belt_dir == MV_LEFT && left_is_free) ||
7199 (belt_dir == MV_RIGHT && right_is_free))
7201 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7203 InitMovingField(x, y, belt_dir);
7204 started_moving = TRUE;
7206 Pushed[x][y] = TRUE;
7207 Pushed[nextx][y] = TRUE;
7209 GfxAction[x][y] = ACTION_DEFAULT;
7213 MovDir[x][y] = 0; /* if element was moving, stop it */
7218 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7220 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7222 if (CAN_MOVE(element) && !started_moving)
7225 int move_pattern = element_info[element].move_pattern;
7230 if (MovDir[x][y] == MV_NONE)
7232 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7233 x, y, element, element_info[element].token_name);
7234 printf("StartMoving(): This should never happen!\n");
7239 Moving2Blocked(x, y, &newx, &newy);
7241 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7244 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7245 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7247 WasJustMoving[x][y] = 0;
7248 CheckCollision[x][y] = 0;
7250 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7252 if (Feld[x][y] != element) /* element has changed */
7256 if (!MovDelay[x][y]) /* start new movement phase */
7258 /* all objects that can change their move direction after each step
7259 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7261 if (element != EL_YAMYAM &&
7262 element != EL_DARK_YAMYAM &&
7263 element != EL_PACMAN &&
7264 !(move_pattern & MV_ANY_DIRECTION) &&
7265 move_pattern != MV_TURNING_LEFT &&
7266 move_pattern != MV_TURNING_RIGHT &&
7267 move_pattern != MV_TURNING_LEFT_RIGHT &&
7268 move_pattern != MV_TURNING_RIGHT_LEFT &&
7269 move_pattern != MV_TURNING_RANDOM)
7273 if (MovDelay[x][y] && (element == EL_BUG ||
7274 element == EL_SPACESHIP ||
7275 element == EL_SP_SNIKSNAK ||
7276 element == EL_SP_ELECTRON ||
7277 element == EL_MOLE))
7278 DrawLevelField(x, y);
7282 if (MovDelay[x][y]) /* wait some time before next movement */
7286 if (element == EL_ROBOT ||
7287 element == EL_YAMYAM ||
7288 element == EL_DARK_YAMYAM)
7290 DrawLevelElementAnimationIfNeeded(x, y, element);
7291 PlayLevelSoundAction(x, y, ACTION_WAITING);
7293 else if (element == EL_SP_ELECTRON)
7294 DrawLevelElementAnimationIfNeeded(x, y, element);
7295 else if (element == EL_DRAGON)
7298 int dir = MovDir[x][y];
7299 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7300 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7301 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7302 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7303 dir == MV_UP ? IMG_FLAMES_1_UP :
7304 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7305 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7307 GfxAction[x][y] = ACTION_ATTACKING;
7309 if (IS_PLAYER(x, y))
7310 DrawPlayerField(x, y);
7312 DrawLevelField(x, y);
7314 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7316 for (i = 1; i <= 3; i++)
7318 int xx = x + i * dx;
7319 int yy = y + i * dy;
7320 int sx = SCREENX(xx);
7321 int sy = SCREENY(yy);
7322 int flame_graphic = graphic + (i - 1);
7324 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7329 int flamed = MovingOrBlocked2Element(xx, yy);
7333 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7335 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7336 RemoveMovingField(xx, yy);
7338 RemoveField(xx, yy);
7340 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7343 RemoveMovingField(xx, yy);
7346 ChangeDelay[xx][yy] = 0;
7348 Feld[xx][yy] = EL_FLAMES;
7350 if (IN_SCR_FIELD(sx, sy))
7352 DrawLevelFieldCrumbledSand(xx, yy);
7353 DrawGraphic(sx, sy, flame_graphic, frame);
7358 if (Feld[xx][yy] == EL_FLAMES)
7359 Feld[xx][yy] = EL_EMPTY;
7360 DrawLevelField(xx, yy);
7365 if (MovDelay[x][y]) /* element still has to wait some time */
7367 PlayLevelSoundAction(x, y, ACTION_WAITING);
7373 /* now make next step */
7375 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7377 if (DONT_COLLIDE_WITH(element) &&
7378 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7379 !PLAYER_ENEMY_PROTECTED(newx, newy))
7381 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7386 else if (CAN_MOVE_INTO_ACID(element) &&
7387 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7388 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7389 (MovDir[x][y] == MV_DOWN ||
7390 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7392 SplashAcid(newx, newy);
7393 Store[x][y] = EL_ACID;
7395 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7397 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7398 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7399 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7400 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7403 DrawLevelField(x, y);
7405 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7406 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7407 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7409 local_player->friends_still_needed--;
7410 if (!local_player->friends_still_needed &&
7411 !local_player->GameOver && AllPlayersGone)
7412 PlayerWins(local_player);
7416 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7418 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7419 DrawLevelField(newx, newy);
7421 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7423 else if (!IS_FREE(newx, newy))
7425 GfxAction[x][y] = ACTION_WAITING;
7427 if (IS_PLAYER(x, y))
7428 DrawPlayerField(x, y);
7430 DrawLevelField(x, y);
7435 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7437 if (IS_FOOD_PIG(Feld[newx][newy]))
7439 if (IS_MOVING(newx, newy))
7440 RemoveMovingField(newx, newy);
7443 Feld[newx][newy] = EL_EMPTY;
7444 DrawLevelField(newx, newy);
7447 PlayLevelSound(x, y, SND_PIG_DIGGING);
7449 else if (!IS_FREE(newx, newy))
7451 if (IS_PLAYER(x, y))
7452 DrawPlayerField(x, y);
7454 DrawLevelField(x, y);
7459 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7461 if (Store[x][y] != EL_EMPTY)
7463 boolean can_clone = FALSE;
7466 /* check if element to clone is still there */
7467 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7469 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7477 /* cannot clone or target field not free anymore -- do not clone */
7478 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7479 Store[x][y] = EL_EMPTY;
7482 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7484 if (IS_MV_DIAGONAL(MovDir[x][y]))
7486 int diagonal_move_dir = MovDir[x][y];
7487 int stored = Store[x][y];
7488 int change_delay = 8;
7491 /* android is moving diagonally */
7493 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7495 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7496 GfxElement[x][y] = EL_EMC_ANDROID;
7497 GfxAction[x][y] = ACTION_SHRINKING;
7498 GfxDir[x][y] = diagonal_move_dir;
7499 ChangeDelay[x][y] = change_delay;
7501 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7504 DrawLevelGraphicAnimation(x, y, graphic);
7505 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7507 if (Feld[newx][newy] == EL_ACID)
7509 SplashAcid(newx, newy);
7514 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7516 Store[newx][newy] = EL_EMC_ANDROID;
7517 GfxElement[newx][newy] = EL_EMC_ANDROID;
7518 GfxAction[newx][newy] = ACTION_GROWING;
7519 GfxDir[newx][newy] = diagonal_move_dir;
7520 ChangeDelay[newx][newy] = change_delay;
7522 graphic = el_act_dir2img(GfxElement[newx][newy],
7523 GfxAction[newx][newy], GfxDir[newx][newy]);
7525 DrawLevelGraphicAnimation(newx, newy, graphic);
7526 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7532 Feld[newx][newy] = EL_EMPTY;
7533 DrawLevelField(newx, newy);
7535 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7538 else if (!IS_FREE(newx, newy))
7541 if (IS_PLAYER(x, y))
7542 DrawPlayerField(x, y);
7544 DrawLevelField(x, y);
7550 else if (IS_CUSTOM_ELEMENT(element) &&
7551 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7553 int new_element = Feld[newx][newy];
7555 if (!IS_FREE(newx, newy))
7557 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7558 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7561 /* no element can dig solid indestructible elements */
7562 if (IS_INDESTRUCTIBLE(new_element) &&
7563 !IS_DIGGABLE(new_element) &&
7564 !IS_COLLECTIBLE(new_element))
7567 if (AmoebaNr[newx][newy] &&
7568 (new_element == EL_AMOEBA_FULL ||
7569 new_element == EL_BD_AMOEBA ||
7570 new_element == EL_AMOEBA_GROWING))
7572 AmoebaCnt[AmoebaNr[newx][newy]]--;
7573 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7576 if (IS_MOVING(newx, newy))
7577 RemoveMovingField(newx, newy);
7580 RemoveField(newx, newy);
7581 DrawLevelField(newx, newy);
7584 /* if digged element was about to explode, prevent the explosion */
7585 ExplodeField[newx][newy] = EX_TYPE_NONE;
7587 PlayLevelSoundAction(x, y, action);
7590 Store[newx][newy] = EL_EMPTY;
7592 /* this makes it possible to leave the removed element again */
7593 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7594 Store[newx][newy] = new_element;
7596 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7598 int move_leave_element = element_info[element].move_leave_element;
7600 /* this makes it possible to leave the removed element again */
7601 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7602 new_element : move_leave_element);
7606 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7608 RunnerVisit[x][y] = FrameCounter;
7609 PlayerVisit[x][y] /= 8; /* expire player visit path */
7612 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7614 if (!IS_FREE(newx, newy))
7616 if (IS_PLAYER(x, y))
7617 DrawPlayerField(x, y);
7619 DrawLevelField(x, y);
7625 boolean wanna_flame = !RND(10);
7626 int dx = newx - x, dy = newy - y;
7627 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7628 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7629 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7630 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7631 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7632 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7635 IS_CLASSIC_ENEMY(element1) ||
7636 IS_CLASSIC_ENEMY(element2)) &&
7637 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7638 element1 != EL_FLAMES && element2 != EL_FLAMES)
7640 ResetGfxAnimation(x, y);
7641 GfxAction[x][y] = ACTION_ATTACKING;
7643 if (IS_PLAYER(x, y))
7644 DrawPlayerField(x, y);
7646 DrawLevelField(x, y);
7648 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7650 MovDelay[x][y] = 50;
7654 RemoveField(newx, newy);
7656 Feld[newx][newy] = EL_FLAMES;
7657 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7660 RemoveField(newx1, newy1);
7662 Feld[newx1][newy1] = EL_FLAMES;
7664 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7667 RemoveField(newx2, newy2);
7669 Feld[newx2][newy2] = EL_FLAMES;
7676 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7677 Feld[newx][newy] == EL_DIAMOND)
7679 if (IS_MOVING(newx, newy))
7680 RemoveMovingField(newx, newy);
7683 Feld[newx][newy] = EL_EMPTY;
7684 DrawLevelField(newx, newy);
7687 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7689 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7690 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7692 if (AmoebaNr[newx][newy])
7694 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7695 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7696 Feld[newx][newy] == EL_BD_AMOEBA)
7697 AmoebaCnt[AmoebaNr[newx][newy]]--;
7702 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7704 RemoveMovingField(newx, newy);
7707 if (IS_MOVING(newx, newy))
7709 RemoveMovingField(newx, newy);
7714 Feld[newx][newy] = EL_EMPTY;
7715 DrawLevelField(newx, newy);
7718 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7720 else if ((element == EL_PACMAN || element == EL_MOLE)
7721 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7723 if (AmoebaNr[newx][newy])
7725 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7726 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7727 Feld[newx][newy] == EL_BD_AMOEBA)
7728 AmoebaCnt[AmoebaNr[newx][newy]]--;
7731 if (element == EL_MOLE)
7733 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7734 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7736 ResetGfxAnimation(x, y);
7737 GfxAction[x][y] = ACTION_DIGGING;
7738 DrawLevelField(x, y);
7740 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7742 return; /* wait for shrinking amoeba */
7744 else /* element == EL_PACMAN */
7746 Feld[newx][newy] = EL_EMPTY;
7747 DrawLevelField(newx, newy);
7748 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7751 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7752 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7753 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7755 /* wait for shrinking amoeba to completely disappear */
7758 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7760 /* object was running against a wall */
7765 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7766 if (move_pattern & MV_ANY_DIRECTION &&
7767 move_pattern == MovDir[x][y])
7769 int blocking_element =
7770 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7772 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7775 element = Feld[x][y]; /* element might have changed */
7779 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7780 DrawLevelElementAnimation(x, y, element);
7782 if (DONT_TOUCH(element))
7783 TestIfBadThingTouchesPlayer(x, y);
7788 InitMovingField(x, y, MovDir[x][y]);
7790 PlayLevelSoundAction(x, y, ACTION_MOVING);
7794 ContinueMoving(x, y);
7797 void ContinueMoving(int x, int y)
7799 int element = Feld[x][y];
7800 struct ElementInfo *ei = &element_info[element];
7801 int direction = MovDir[x][y];
7802 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7803 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7804 int newx = x + dx, newy = y + dy;
7805 int stored = Store[x][y];
7806 int stored_new = Store[newx][newy];
7807 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7808 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7809 boolean last_line = (newy == lev_fieldy - 1);
7811 MovPos[x][y] += getElementMoveStepsize(x, y);
7813 if (pushed_by_player) /* special case: moving object pushed by player */
7814 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7816 if (ABS(MovPos[x][y]) < TILEX)
7819 int ee = Feld[x][y];
7820 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7821 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7823 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7824 x, y, ABS(MovPos[x][y]),
7826 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7829 DrawLevelField(x, y);
7831 return; /* element is still moving */
7834 /* element reached destination field */
7836 Feld[x][y] = EL_EMPTY;
7837 Feld[newx][newy] = element;
7838 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7840 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7842 element = Feld[newx][newy] = EL_ACID;
7844 else if (element == EL_MOLE)
7846 Feld[x][y] = EL_SAND;
7848 DrawLevelFieldCrumbledSandNeighbours(x, y);
7850 else if (element == EL_QUICKSAND_FILLING)
7852 element = Feld[newx][newy] = get_next_element(element);
7853 Store[newx][newy] = Store[x][y];
7855 else if (element == EL_QUICKSAND_EMPTYING)
7857 Feld[x][y] = get_next_element(element);
7858 element = Feld[newx][newy] = Store[x][y];
7860 else if (element == EL_QUICKSAND_FAST_FILLING)
7862 element = Feld[newx][newy] = get_next_element(element);
7863 Store[newx][newy] = Store[x][y];
7865 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7867 Feld[x][y] = get_next_element(element);
7868 element = Feld[newx][newy] = Store[x][y];
7870 else if (element == EL_MAGIC_WALL_FILLING)
7872 element = Feld[newx][newy] = get_next_element(element);
7873 if (!game.magic_wall_active)
7874 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7875 Store[newx][newy] = Store[x][y];
7877 else if (element == EL_MAGIC_WALL_EMPTYING)
7879 Feld[x][y] = get_next_element(element);
7880 if (!game.magic_wall_active)
7881 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7882 element = Feld[newx][newy] = Store[x][y];
7884 #if USE_NEW_CUSTOM_VALUE
7885 InitField(newx, newy, FALSE);
7888 else if (element == EL_BD_MAGIC_WALL_FILLING)
7890 element = Feld[newx][newy] = get_next_element(element);
7891 if (!game.magic_wall_active)
7892 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7893 Store[newx][newy] = Store[x][y];
7895 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7897 Feld[x][y] = get_next_element(element);
7898 if (!game.magic_wall_active)
7899 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7900 element = Feld[newx][newy] = Store[x][y];
7902 #if USE_NEW_CUSTOM_VALUE
7903 InitField(newx, newy, FALSE);
7906 else if (element == EL_DC_MAGIC_WALL_FILLING)
7908 element = Feld[newx][newy] = get_next_element(element);
7909 if (!game.magic_wall_active)
7910 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7911 Store[newx][newy] = Store[x][y];
7913 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7915 Feld[x][y] = get_next_element(element);
7916 if (!game.magic_wall_active)
7917 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7918 element = Feld[newx][newy] = Store[x][y];
7920 #if USE_NEW_CUSTOM_VALUE
7921 InitField(newx, newy, FALSE);
7924 else if (element == EL_AMOEBA_DROPPING)
7926 Feld[x][y] = get_next_element(element);
7927 element = Feld[newx][newy] = Store[x][y];
7929 else if (element == EL_SOKOBAN_OBJECT)
7932 Feld[x][y] = Back[x][y];
7934 if (Back[newx][newy])
7935 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7937 Back[x][y] = Back[newx][newy] = 0;
7940 Store[x][y] = EL_EMPTY;
7945 MovDelay[newx][newy] = 0;
7947 if (CAN_CHANGE_OR_HAS_ACTION(element))
7949 /* copy element change control values to new field */
7950 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7951 ChangePage[newx][newy] = ChangePage[x][y];
7952 ChangeCount[newx][newy] = ChangeCount[x][y];
7953 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7956 #if USE_NEW_CUSTOM_VALUE
7957 CustomValue[newx][newy] = CustomValue[x][y];
7960 ChangeDelay[x][y] = 0;
7961 ChangePage[x][y] = -1;
7962 ChangeCount[x][y] = 0;
7963 ChangeEvent[x][y] = -1;
7965 #if USE_NEW_CUSTOM_VALUE
7966 CustomValue[x][y] = 0;
7969 /* copy animation control values to new field */
7970 GfxFrame[newx][newy] = GfxFrame[x][y];
7971 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7972 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7973 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7975 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7977 /* some elements can leave other elements behind after moving */
7979 if (ei->move_leave_element != EL_EMPTY &&
7980 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7981 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7983 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7984 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7985 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7988 int move_leave_element = ei->move_leave_element;
7992 /* this makes it possible to leave the removed element again */
7993 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7994 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7996 /* this makes it possible to leave the removed element again */
7997 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7998 move_leave_element = stored;
8001 /* this makes it possible to leave the removed element again */
8002 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8003 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8004 move_leave_element = stored;
8007 Feld[x][y] = move_leave_element;
8009 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8010 MovDir[x][y] = direction;
8012 InitField(x, y, FALSE);
8014 if (GFX_CRUMBLED(Feld[x][y]))
8015 DrawLevelFieldCrumbledSandNeighbours(x, y);
8017 if (ELEM_IS_PLAYER(move_leave_element))
8018 RelocatePlayer(x, y, move_leave_element);
8021 /* do this after checking for left-behind element */
8022 ResetGfxAnimation(x, y); /* reset animation values for old field */
8024 if (!CAN_MOVE(element) ||
8025 (CAN_FALL(element) && direction == MV_DOWN &&
8026 (element == EL_SPRING ||
8027 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8028 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8029 GfxDir[x][y] = MovDir[newx][newy] = 0;
8031 DrawLevelField(x, y);
8032 DrawLevelField(newx, newy);
8034 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8036 /* prevent pushed element from moving on in pushed direction */
8037 if (pushed_by_player && CAN_MOVE(element) &&
8038 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8039 !(element_info[element].move_pattern & direction))
8040 TurnRound(newx, newy);
8042 /* prevent elements on conveyor belt from moving on in last direction */
8043 if (pushed_by_conveyor && CAN_FALL(element) &&
8044 direction & MV_HORIZONTAL)
8045 MovDir[newx][newy] = 0;
8047 if (!pushed_by_player)
8049 int nextx = newx + dx, nexty = newy + dy;
8050 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8052 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8054 if (CAN_FALL(element) && direction == MV_DOWN)
8055 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8057 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8058 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8060 #if USE_FIX_IMPACT_COLLISION
8061 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8062 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8066 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8068 TestIfBadThingTouchesPlayer(newx, newy);
8069 TestIfBadThingTouchesFriend(newx, newy);
8071 if (!IS_CUSTOM_ELEMENT(element))
8072 TestIfBadThingTouchesOtherBadThing(newx, newy);
8074 else if (element == EL_PENGUIN)
8075 TestIfFriendTouchesBadThing(newx, newy);
8077 /* give the player one last chance (one more frame) to move away */
8078 if (CAN_FALL(element) && direction == MV_DOWN &&
8079 (last_line || (!IS_FREE(x, newy + 1) &&
8080 (!IS_PLAYER(x, newy + 1) ||
8081 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8084 if (pushed_by_player && !game.use_change_when_pushing_bug)
8086 int push_side = MV_DIR_OPPOSITE(direction);
8087 struct PlayerInfo *player = PLAYERINFO(x, y);
8089 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8090 player->index_bit, push_side);
8091 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8092 player->index_bit, push_side);
8095 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8096 MovDelay[newx][newy] = 1;
8098 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8100 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8103 if (ChangePage[newx][newy] != -1) /* delayed change */
8105 int page = ChangePage[newx][newy];
8106 struct ElementChangeInfo *change = &ei->change_page[page];
8108 ChangePage[newx][newy] = -1;
8110 if (change->can_change)
8112 if (ChangeElement(newx, newy, element, page))
8114 if (change->post_change_function)
8115 change->post_change_function(newx, newy);
8119 if (change->has_action)
8120 ExecuteCustomElementAction(newx, newy, element, page);
8124 TestIfElementHitsCustomElement(newx, newy, direction);
8125 TestIfPlayerTouchesCustomElement(newx, newy);
8126 TestIfElementTouchesCustomElement(newx, newy);
8128 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8129 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8130 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8131 MV_DIR_OPPOSITE(direction));
8134 int AmoebeNachbarNr(int ax, int ay)
8137 int element = Feld[ax][ay];
8139 static int xy[4][2] =
8147 for (i = 0; i < NUM_DIRECTIONS; i++)
8149 int x = ax + xy[i][0];
8150 int y = ay + xy[i][1];
8152 if (!IN_LEV_FIELD(x, y))
8155 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8156 group_nr = AmoebaNr[x][y];
8162 void AmoebenVereinigen(int ax, int ay)
8164 int i, x, y, xx, yy;
8165 int new_group_nr = AmoebaNr[ax][ay];
8166 static int xy[4][2] =
8174 if (new_group_nr == 0)
8177 for (i = 0; i < NUM_DIRECTIONS; i++)
8182 if (!IN_LEV_FIELD(x, y))
8185 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8186 Feld[x][y] == EL_BD_AMOEBA ||
8187 Feld[x][y] == EL_AMOEBA_DEAD) &&
8188 AmoebaNr[x][y] != new_group_nr)
8190 int old_group_nr = AmoebaNr[x][y];
8192 if (old_group_nr == 0)
8195 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8196 AmoebaCnt[old_group_nr] = 0;
8197 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8198 AmoebaCnt2[old_group_nr] = 0;
8200 SCAN_PLAYFIELD(xx, yy)
8202 if (AmoebaNr[xx][yy] == old_group_nr)
8203 AmoebaNr[xx][yy] = new_group_nr;
8209 void AmoebeUmwandeln(int ax, int ay)
8213 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8215 int group_nr = AmoebaNr[ax][ay];
8220 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8221 printf("AmoebeUmwandeln(): This should never happen!\n");
8226 SCAN_PLAYFIELD(x, y)
8228 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8231 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8235 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8236 SND_AMOEBA_TURNING_TO_GEM :
8237 SND_AMOEBA_TURNING_TO_ROCK));
8242 static int xy[4][2] =
8250 for (i = 0; i < NUM_DIRECTIONS; i++)
8255 if (!IN_LEV_FIELD(x, y))
8258 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8260 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8261 SND_AMOEBA_TURNING_TO_GEM :
8262 SND_AMOEBA_TURNING_TO_ROCK));
8269 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8272 int group_nr = AmoebaNr[ax][ay];
8273 boolean done = FALSE;
8278 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8279 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8284 SCAN_PLAYFIELD(x, y)
8286 if (AmoebaNr[x][y] == group_nr &&
8287 (Feld[x][y] == EL_AMOEBA_DEAD ||
8288 Feld[x][y] == EL_BD_AMOEBA ||
8289 Feld[x][y] == EL_AMOEBA_GROWING))
8292 Feld[x][y] = new_element;
8293 InitField(x, y, FALSE);
8294 DrawLevelField(x, y);
8300 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8301 SND_BD_AMOEBA_TURNING_TO_ROCK :
8302 SND_BD_AMOEBA_TURNING_TO_GEM));
8305 void AmoebeWaechst(int x, int y)
8307 static unsigned long sound_delay = 0;
8308 static unsigned long sound_delay_value = 0;
8310 if (!MovDelay[x][y]) /* start new growing cycle */
8314 if (DelayReached(&sound_delay, sound_delay_value))
8316 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8317 sound_delay_value = 30;
8321 if (MovDelay[x][y]) /* wait some time before growing bigger */
8324 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8326 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8327 6 - MovDelay[x][y]);
8329 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8332 if (!MovDelay[x][y])
8334 Feld[x][y] = Store[x][y];
8336 DrawLevelField(x, y);
8341 void AmoebaDisappearing(int x, int y)
8343 static unsigned long sound_delay = 0;
8344 static unsigned long sound_delay_value = 0;
8346 if (!MovDelay[x][y]) /* start new shrinking cycle */
8350 if (DelayReached(&sound_delay, sound_delay_value))
8351 sound_delay_value = 30;
8354 if (MovDelay[x][y]) /* wait some time before shrinking */
8357 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8359 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8360 6 - MovDelay[x][y]);
8362 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8365 if (!MovDelay[x][y])
8367 Feld[x][y] = EL_EMPTY;
8368 DrawLevelField(x, y);
8370 /* don't let mole enter this field in this cycle;
8371 (give priority to objects falling to this field from above) */
8377 void AmoebeAbleger(int ax, int ay)
8380 int element = Feld[ax][ay];
8381 int graphic = el2img(element);
8382 int newax = ax, neway = ay;
8383 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8384 static int xy[4][2] =
8392 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8394 Feld[ax][ay] = EL_AMOEBA_DEAD;
8395 DrawLevelField(ax, ay);
8399 if (IS_ANIMATED(graphic))
8400 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8402 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8403 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8405 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8408 if (MovDelay[ax][ay])
8412 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8415 int x = ax + xy[start][0];
8416 int y = ay + xy[start][1];
8418 if (!IN_LEV_FIELD(x, y))
8421 if (IS_FREE(x, y) ||
8422 CAN_GROW_INTO(Feld[x][y]) ||
8423 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8424 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8430 if (newax == ax && neway == ay)
8433 else /* normal or "filled" (BD style) amoeba */
8436 boolean waiting_for_player = FALSE;
8438 for (i = 0; i < NUM_DIRECTIONS; i++)
8440 int j = (start + i) % 4;
8441 int x = ax + xy[j][0];
8442 int y = ay + xy[j][1];
8444 if (!IN_LEV_FIELD(x, y))
8447 if (IS_FREE(x, y) ||
8448 CAN_GROW_INTO(Feld[x][y]) ||
8449 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8450 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8456 else if (IS_PLAYER(x, y))
8457 waiting_for_player = TRUE;
8460 if (newax == ax && neway == ay) /* amoeba cannot grow */
8462 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8464 Feld[ax][ay] = EL_AMOEBA_DEAD;
8465 DrawLevelField(ax, ay);
8466 AmoebaCnt[AmoebaNr[ax][ay]]--;
8468 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8470 if (element == EL_AMOEBA_FULL)
8471 AmoebeUmwandeln(ax, ay);
8472 else if (element == EL_BD_AMOEBA)
8473 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8478 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8480 /* amoeba gets larger by growing in some direction */
8482 int new_group_nr = AmoebaNr[ax][ay];
8485 if (new_group_nr == 0)
8487 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8488 printf("AmoebeAbleger(): This should never happen!\n");
8493 AmoebaNr[newax][neway] = new_group_nr;
8494 AmoebaCnt[new_group_nr]++;
8495 AmoebaCnt2[new_group_nr]++;
8497 /* if amoeba touches other amoeba(s) after growing, unify them */
8498 AmoebenVereinigen(newax, neway);
8500 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8502 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8508 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8509 (neway == lev_fieldy - 1 && newax != ax))
8511 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8512 Store[newax][neway] = element;
8514 else if (neway == ay || element == EL_EMC_DRIPPER)
8516 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8518 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8522 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8523 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8524 Store[ax][ay] = EL_AMOEBA_DROP;
8525 ContinueMoving(ax, ay);
8529 DrawLevelField(newax, neway);
8532 void Life(int ax, int ay)
8536 int element = Feld[ax][ay];
8537 int graphic = el2img(element);
8538 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8540 boolean changed = FALSE;
8542 if (IS_ANIMATED(graphic))
8543 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8548 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8549 MovDelay[ax][ay] = life_time;
8551 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8554 if (MovDelay[ax][ay])
8558 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8560 int xx = ax+x1, yy = ay+y1;
8563 if (!IN_LEV_FIELD(xx, yy))
8566 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8568 int x = xx+x2, y = yy+y2;
8570 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8573 if (((Feld[x][y] == element ||
8574 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8576 (IS_FREE(x, y) && Stop[x][y]))
8580 if (xx == ax && yy == ay) /* field in the middle */
8582 if (nachbarn < life_parameter[0] ||
8583 nachbarn > life_parameter[1])
8585 Feld[xx][yy] = EL_EMPTY;
8587 DrawLevelField(xx, yy);
8588 Stop[xx][yy] = TRUE;
8592 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8593 { /* free border field */
8594 if (nachbarn >= life_parameter[2] &&
8595 nachbarn <= life_parameter[3])
8597 Feld[xx][yy] = element;
8598 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8600 DrawLevelField(xx, yy);
8601 Stop[xx][yy] = TRUE;
8608 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8609 SND_GAME_OF_LIFE_GROWING);
8612 static void InitRobotWheel(int x, int y)
8614 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8617 static void RunRobotWheel(int x, int y)
8619 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8622 static void StopRobotWheel(int x, int y)
8624 if (ZX == x && ZY == y)
8628 static void InitTimegateWheel(int x, int y)
8630 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8633 static void RunTimegateWheel(int x, int y)
8635 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8638 static void InitMagicBallDelay(int x, int y)
8641 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8643 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8647 static void ActivateMagicBall(int bx, int by)
8651 if (level.ball_random)
8653 int pos_border = RND(8); /* select one of the eight border elements */
8654 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8655 int xx = pos_content % 3;
8656 int yy = pos_content / 3;
8661 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8662 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8666 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8668 int xx = x - bx + 1;
8669 int yy = y - by + 1;
8671 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8672 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8676 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8679 void CheckExit(int x, int y)
8681 if (local_player->gems_still_needed > 0 ||
8682 local_player->sokobanfields_still_needed > 0 ||
8683 local_player->lights_still_needed > 0)
8685 int element = Feld[x][y];
8686 int graphic = el2img(element);
8688 if (IS_ANIMATED(graphic))
8689 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8694 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8697 Feld[x][y] = EL_EXIT_OPENING;
8699 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8702 void CheckExitEM(int x, int y)
8704 if (local_player->gems_still_needed > 0 ||
8705 local_player->sokobanfields_still_needed > 0 ||
8706 local_player->lights_still_needed > 0)
8708 int element = Feld[x][y];
8709 int graphic = el2img(element);
8711 if (IS_ANIMATED(graphic))
8712 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8717 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8720 Feld[x][y] = EL_EM_EXIT_OPENING;
8722 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8725 void CheckExitSteel(int x, int y)
8727 if (local_player->gems_still_needed > 0 ||
8728 local_player->sokobanfields_still_needed > 0 ||
8729 local_player->lights_still_needed > 0)
8731 int element = Feld[x][y];
8732 int graphic = el2img(element);
8734 if (IS_ANIMATED(graphic))
8735 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8740 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8743 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8745 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8748 void CheckExitSteelEM(int x, int y)
8750 if (local_player->gems_still_needed > 0 ||
8751 local_player->sokobanfields_still_needed > 0 ||
8752 local_player->lights_still_needed > 0)
8754 int element = Feld[x][y];
8755 int graphic = el2img(element);
8757 if (IS_ANIMATED(graphic))
8758 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8763 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8766 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8768 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8771 void CheckExitSP(int x, int y)
8773 if (local_player->gems_still_needed > 0)
8775 int element = Feld[x][y];
8776 int graphic = el2img(element);
8778 if (IS_ANIMATED(graphic))
8779 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8784 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8787 Feld[x][y] = EL_SP_EXIT_OPENING;
8789 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8792 static void CloseAllOpenTimegates()
8796 SCAN_PLAYFIELD(x, y)
8798 int element = Feld[x][y];
8800 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8802 Feld[x][y] = EL_TIMEGATE_CLOSING;
8804 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8809 void DrawTwinkleOnField(int x, int y)
8811 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8814 if (Feld[x][y] == EL_BD_DIAMOND)
8817 if (MovDelay[x][y] == 0) /* next animation frame */
8818 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8820 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8824 if (setup.direct_draw && MovDelay[x][y])
8825 SetDrawtoField(DRAW_BUFFERED);
8827 DrawLevelElementAnimation(x, y, Feld[x][y]);
8829 if (MovDelay[x][y] != 0)
8831 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8832 10 - MovDelay[x][y]);
8834 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8836 if (setup.direct_draw)
8840 dest_x = FX + SCREENX(x) * TILEX;
8841 dest_y = FY + SCREENY(y) * TILEY;
8843 BlitBitmap(drawto_field, window,
8844 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8845 SetDrawtoField(DRAW_DIRECT);
8851 void MauerWaechst(int x, int y)
8855 if (!MovDelay[x][y]) /* next animation frame */
8856 MovDelay[x][y] = 3 * delay;
8858 if (MovDelay[x][y]) /* wait some time before next frame */
8862 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8864 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8865 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8867 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8870 if (!MovDelay[x][y])
8872 if (MovDir[x][y] == MV_LEFT)
8874 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8875 DrawLevelField(x - 1, y);
8877 else if (MovDir[x][y] == MV_RIGHT)
8879 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8880 DrawLevelField(x + 1, y);
8882 else if (MovDir[x][y] == MV_UP)
8884 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8885 DrawLevelField(x, y - 1);
8889 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8890 DrawLevelField(x, y + 1);
8893 Feld[x][y] = Store[x][y];
8895 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8896 DrawLevelField(x, y);
8901 void MauerAbleger(int ax, int ay)
8903 int element = Feld[ax][ay];
8904 int graphic = el2img(element);
8905 boolean oben_frei = FALSE, unten_frei = FALSE;
8906 boolean links_frei = FALSE, rechts_frei = FALSE;
8907 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8908 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8909 boolean new_wall = FALSE;
8911 if (IS_ANIMATED(graphic))
8912 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8914 if (!MovDelay[ax][ay]) /* start building new wall */
8915 MovDelay[ax][ay] = 6;
8917 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8920 if (MovDelay[ax][ay])
8924 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8926 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8928 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8930 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8933 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8934 element == EL_EXPANDABLE_WALL_ANY)
8938 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8939 Store[ax][ay-1] = element;
8940 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8941 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8942 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8943 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8948 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8949 Store[ax][ay+1] = element;
8950 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8951 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8952 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8953 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8958 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8959 element == EL_EXPANDABLE_WALL_ANY ||
8960 element == EL_EXPANDABLE_WALL ||
8961 element == EL_BD_EXPANDABLE_WALL)
8965 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8966 Store[ax-1][ay] = element;
8967 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8968 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8969 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8970 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8976 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8977 Store[ax+1][ay] = element;
8978 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8979 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8980 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8981 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8986 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8987 DrawLevelField(ax, ay);
8989 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8991 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8992 unten_massiv = TRUE;
8993 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8994 links_massiv = TRUE;
8995 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8996 rechts_massiv = TRUE;
8998 if (((oben_massiv && unten_massiv) ||
8999 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9000 element == EL_EXPANDABLE_WALL) &&
9001 ((links_massiv && rechts_massiv) ||
9002 element == EL_EXPANDABLE_WALL_VERTICAL))
9003 Feld[ax][ay] = EL_WALL;
9006 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9009 void MauerAblegerStahl(int ax, int ay)
9011 int element = Feld[ax][ay];
9012 int graphic = el2img(element);
9013 boolean oben_frei = FALSE, unten_frei = FALSE;
9014 boolean links_frei = FALSE, rechts_frei = FALSE;
9015 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9016 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9017 boolean new_wall = FALSE;
9019 if (IS_ANIMATED(graphic))
9020 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9022 if (!MovDelay[ax][ay]) /* start building new wall */
9023 MovDelay[ax][ay] = 6;
9025 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9028 if (MovDelay[ax][ay])
9032 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9034 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9036 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9038 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9041 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9042 element == EL_EXPANDABLE_STEELWALL_ANY)
9046 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9047 Store[ax][ay-1] = element;
9048 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9049 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9050 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9051 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9056 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9057 Store[ax][ay+1] = element;
9058 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9059 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9060 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9061 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9066 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9067 element == EL_EXPANDABLE_STEELWALL_ANY)
9071 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9072 Store[ax-1][ay] = element;
9073 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9074 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9075 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9076 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9082 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9083 Store[ax+1][ay] = element;
9084 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9085 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9086 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9087 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9092 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9094 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9095 unten_massiv = TRUE;
9096 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9097 links_massiv = TRUE;
9098 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9099 rechts_massiv = TRUE;
9101 if (((oben_massiv && unten_massiv) ||
9102 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9103 ((links_massiv && rechts_massiv) ||
9104 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9105 Feld[ax][ay] = EL_WALL;
9108 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9111 void CheckForDragon(int x, int y)
9114 boolean dragon_found = FALSE;
9115 static int xy[4][2] =
9123 for (i = 0; i < NUM_DIRECTIONS; i++)
9125 for (j = 0; j < 4; j++)
9127 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9129 if (IN_LEV_FIELD(xx, yy) &&
9130 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9132 if (Feld[xx][yy] == EL_DRAGON)
9133 dragon_found = TRUE;
9142 for (i = 0; i < NUM_DIRECTIONS; i++)
9144 for (j = 0; j < 3; j++)
9146 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9148 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9150 Feld[xx][yy] = EL_EMPTY;
9151 DrawLevelField(xx, yy);
9160 static void InitBuggyBase(int x, int y)
9162 int element = Feld[x][y];
9163 int activating_delay = FRAMES_PER_SECOND / 4;
9166 (element == EL_SP_BUGGY_BASE ?
9167 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9168 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9170 element == EL_SP_BUGGY_BASE_ACTIVE ?
9171 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9174 static void WarnBuggyBase(int x, int y)
9177 static int xy[4][2] =
9185 for (i = 0; i < NUM_DIRECTIONS; i++)
9187 int xx = x + xy[i][0];
9188 int yy = y + xy[i][1];
9190 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9192 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9199 static void InitTrap(int x, int y)
9201 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9204 static void ActivateTrap(int x, int y)
9206 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9209 static void ChangeActiveTrap(int x, int y)
9211 int graphic = IMG_TRAP_ACTIVE;
9213 /* if new animation frame was drawn, correct crumbled sand border */
9214 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9215 DrawLevelFieldCrumbledSand(x, y);
9218 static int getSpecialActionElement(int element, int number, int base_element)
9220 return (element != EL_EMPTY ? element :
9221 number != -1 ? base_element + number - 1 :
9225 static int getModifiedActionNumber(int value_old, int operator, int operand,
9226 int value_min, int value_max)
9228 int value_new = (operator == CA_MODE_SET ? operand :
9229 operator == CA_MODE_ADD ? value_old + operand :
9230 operator == CA_MODE_SUBTRACT ? value_old - operand :
9231 operator == CA_MODE_MULTIPLY ? value_old * operand :
9232 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9233 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9236 return (value_new < value_min ? value_min :
9237 value_new > value_max ? value_max :
9241 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9243 struct ElementInfo *ei = &element_info[element];
9244 struct ElementChangeInfo *change = &ei->change_page[page];
9245 int target_element = change->target_element;
9246 int action_type = change->action_type;
9247 int action_mode = change->action_mode;
9248 int action_arg = change->action_arg;
9251 if (!change->has_action)
9254 /* ---------- determine action paramater values -------------------------- */
9256 int level_time_value =
9257 (level.time > 0 ? TimeLeft :
9260 int action_arg_element =
9261 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9262 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9263 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9266 int action_arg_direction =
9267 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9268 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9269 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9270 change->actual_trigger_side :
9271 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9272 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9275 int action_arg_number_min =
9276 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9279 int action_arg_number_max =
9280 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9281 action_type == CA_SET_LEVEL_GEMS ? 999 :
9282 action_type == CA_SET_LEVEL_TIME ? 9999 :
9283 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9284 action_type == CA_SET_CE_VALUE ? 9999 :
9285 action_type == CA_SET_CE_SCORE ? 9999 :
9288 int action_arg_number_reset =
9289 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9290 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9291 action_type == CA_SET_LEVEL_TIME ? level.time :
9292 action_type == CA_SET_LEVEL_SCORE ? 0 :
9293 #if USE_NEW_CUSTOM_VALUE
9294 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9296 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9298 action_type == CA_SET_CE_SCORE ? 0 :
9301 int action_arg_number =
9302 (action_arg <= CA_ARG_MAX ? action_arg :
9303 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9304 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9305 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9306 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9307 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9308 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9309 #if USE_NEW_CUSTOM_VALUE
9310 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9312 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9314 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9315 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9316 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9317 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9318 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9319 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9320 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9321 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9322 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9323 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9324 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9327 int action_arg_number_old =
9328 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9329 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9330 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9331 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9332 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9335 int action_arg_number_new =
9336 getModifiedActionNumber(action_arg_number_old,
9337 action_mode, action_arg_number,
9338 action_arg_number_min, action_arg_number_max);
9340 int trigger_player_bits =
9341 (change->actual_trigger_player >= EL_PLAYER_1 &&
9342 change->actual_trigger_player <= EL_PLAYER_4 ?
9343 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9346 int action_arg_player_bits =
9347 (action_arg >= CA_ARG_PLAYER_1 &&
9348 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9349 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9352 /* ---------- execute action -------------------------------------------- */
9354 switch (action_type)
9361 /* ---------- level actions ------------------------------------------- */
9363 case CA_RESTART_LEVEL:
9365 game.restart_level = TRUE;
9370 case CA_SHOW_ENVELOPE:
9372 int element = getSpecialActionElement(action_arg_element,
9373 action_arg_number, EL_ENVELOPE_1);
9375 if (IS_ENVELOPE(element))
9376 local_player->show_envelope = element;
9381 case CA_SET_LEVEL_TIME:
9383 if (level.time > 0) /* only modify limited time value */
9385 TimeLeft = action_arg_number_new;
9388 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9390 DisplayGameControlValues();
9392 DrawGameValue_Time(TimeLeft);
9395 if (!TimeLeft && setup.time_limit)
9396 for (i = 0; i < MAX_PLAYERS; i++)
9397 KillPlayer(&stored_player[i]);
9403 case CA_SET_LEVEL_SCORE:
9405 local_player->score = action_arg_number_new;
9408 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9410 DisplayGameControlValues();
9412 DrawGameValue_Score(local_player->score);
9418 case CA_SET_LEVEL_GEMS:
9420 local_player->gems_still_needed = action_arg_number_new;
9423 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9425 DisplayGameControlValues();
9427 DrawGameValue_Emeralds(local_player->gems_still_needed);
9433 #if !USE_PLAYER_GRAVITY
9434 case CA_SET_LEVEL_GRAVITY:
9436 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9437 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9438 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9444 case CA_SET_LEVEL_WIND:
9446 game.wind_direction = action_arg_direction;
9451 /* ---------- player actions ------------------------------------------ */
9453 case CA_MOVE_PLAYER:
9455 /* automatically move to the next field in specified direction */
9456 for (i = 0; i < MAX_PLAYERS; i++)
9457 if (trigger_player_bits & (1 << i))
9458 stored_player[i].programmed_action = action_arg_direction;
9463 case CA_EXIT_PLAYER:
9465 for (i = 0; i < MAX_PLAYERS; i++)
9466 if (action_arg_player_bits & (1 << i))
9467 PlayerWins(&stored_player[i]);
9472 case CA_KILL_PLAYER:
9474 for (i = 0; i < MAX_PLAYERS; i++)
9475 if (action_arg_player_bits & (1 << i))
9476 KillPlayer(&stored_player[i]);
9481 case CA_SET_PLAYER_KEYS:
9483 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9484 int element = getSpecialActionElement(action_arg_element,
9485 action_arg_number, EL_KEY_1);
9487 if (IS_KEY(element))
9489 for (i = 0; i < MAX_PLAYERS; i++)
9491 if (trigger_player_bits & (1 << i))
9493 stored_player[i].key[KEY_NR(element)] = key_state;
9495 DrawGameDoorValues();
9503 case CA_SET_PLAYER_SPEED:
9505 for (i = 0; i < MAX_PLAYERS; i++)
9507 if (trigger_player_bits & (1 << i))
9509 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9511 if (action_arg == CA_ARG_SPEED_FASTER &&
9512 stored_player[i].cannot_move)
9514 action_arg_number = STEPSIZE_VERY_SLOW;
9516 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9517 action_arg == CA_ARG_SPEED_FASTER)
9519 action_arg_number = 2;
9520 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9523 else if (action_arg == CA_ARG_NUMBER_RESET)
9525 action_arg_number = level.initial_player_stepsize[i];
9529 getModifiedActionNumber(move_stepsize,
9532 action_arg_number_min,
9533 action_arg_number_max);
9535 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9542 case CA_SET_PLAYER_SHIELD:
9544 for (i = 0; i < MAX_PLAYERS; i++)
9546 if (trigger_player_bits & (1 << i))
9548 if (action_arg == CA_ARG_SHIELD_OFF)
9550 stored_player[i].shield_normal_time_left = 0;
9551 stored_player[i].shield_deadly_time_left = 0;
9553 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9555 stored_player[i].shield_normal_time_left = 999999;
9557 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9559 stored_player[i].shield_normal_time_left = 999999;
9560 stored_player[i].shield_deadly_time_left = 999999;
9568 #if USE_PLAYER_GRAVITY
9569 case CA_SET_PLAYER_GRAVITY:
9571 for (i = 0; i < MAX_PLAYERS; i++)
9573 if (trigger_player_bits & (1 << i))
9575 stored_player[i].gravity =
9576 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9577 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9578 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9579 stored_player[i].gravity);
9587 case CA_SET_PLAYER_ARTWORK:
9589 for (i = 0; i < MAX_PLAYERS; i++)
9591 if (trigger_player_bits & (1 << i))
9593 int artwork_element = action_arg_element;
9595 if (action_arg == CA_ARG_ELEMENT_RESET)
9597 (level.use_artwork_element[i] ? level.artwork_element[i] :
9598 stored_player[i].element_nr);
9600 #if USE_GFX_RESET_PLAYER_ARTWORK
9601 if (stored_player[i].artwork_element != artwork_element)
9602 stored_player[i].Frame = 0;
9605 stored_player[i].artwork_element = artwork_element;
9607 SetPlayerWaiting(&stored_player[i], FALSE);
9609 /* set number of special actions for bored and sleeping animation */
9610 stored_player[i].num_special_action_bored =
9611 get_num_special_action(artwork_element,
9612 ACTION_BORING_1, ACTION_BORING_LAST);
9613 stored_player[i].num_special_action_sleeping =
9614 get_num_special_action(artwork_element,
9615 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9622 /* ---------- CE actions ---------------------------------------------- */
9624 case CA_SET_CE_VALUE:
9626 #if USE_NEW_CUSTOM_VALUE
9627 int last_ce_value = CustomValue[x][y];
9629 CustomValue[x][y] = action_arg_number_new;
9631 if (CustomValue[x][y] != last_ce_value)
9633 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9634 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9636 if (CustomValue[x][y] == 0)
9638 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9639 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9647 case CA_SET_CE_SCORE:
9649 #if USE_NEW_CUSTOM_VALUE
9650 int last_ce_score = ei->collect_score;
9652 ei->collect_score = action_arg_number_new;
9654 if (ei->collect_score != last_ce_score)
9656 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9657 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9659 if (ei->collect_score == 0)
9663 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9664 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9667 This is a very special case that seems to be a mixture between
9668 CheckElementChange() and CheckTriggeredElementChange(): while
9669 the first one only affects single elements that are triggered
9670 directly, the second one affects multiple elements in the playfield
9671 that are triggered indirectly by another element. This is a third
9672 case: Changing the CE score always affects multiple identical CEs,
9673 so every affected CE must be checked, not only the single CE for
9674 which the CE score was changed in the first place (as every instance
9675 of that CE shares the same CE score, and therefore also can change)!
9677 SCAN_PLAYFIELD(xx, yy)
9679 if (Feld[xx][yy] == element)
9680 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9681 CE_SCORE_GETS_ZERO);
9690 /* ---------- engine actions ------------------------------------------ */
9692 case CA_SET_ENGINE_SCAN_MODE:
9694 InitPlayfieldScanMode(action_arg);
9704 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9706 int old_element = Feld[x][y];
9707 int new_element = GetElementFromGroupElement(element);
9708 int previous_move_direction = MovDir[x][y];
9709 #if USE_NEW_CUSTOM_VALUE
9710 int last_ce_value = CustomValue[x][y];
9712 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9713 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9714 boolean add_player_onto_element = (new_element_is_player &&
9715 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9716 /* this breaks SnakeBite when a snake is
9717 halfway through a door that closes */
9718 /* NOW FIXED AT LEVEL INIT IN files.c */
9719 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9721 IS_WALKABLE(old_element));
9724 /* check if element under the player changes from accessible to unaccessible
9725 (needed for special case of dropping element which then changes) */
9726 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9727 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9735 if (!add_player_onto_element)
9737 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9738 RemoveMovingField(x, y);
9742 Feld[x][y] = new_element;
9744 #if !USE_GFX_RESET_GFX_ANIMATION
9745 ResetGfxAnimation(x, y);
9746 ResetRandomAnimationValue(x, y);
9749 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9750 MovDir[x][y] = previous_move_direction;
9752 #if USE_NEW_CUSTOM_VALUE
9753 if (element_info[new_element].use_last_ce_value)
9754 CustomValue[x][y] = last_ce_value;
9757 InitField_WithBug1(x, y, FALSE);
9759 new_element = Feld[x][y]; /* element may have changed */
9761 #if USE_GFX_RESET_GFX_ANIMATION
9762 ResetGfxAnimation(x, y);
9763 ResetRandomAnimationValue(x, y);
9766 DrawLevelField(x, y);
9768 if (GFX_CRUMBLED(new_element))
9769 DrawLevelFieldCrumbledSandNeighbours(x, y);
9773 /* check if element under the player changes from accessible to unaccessible
9774 (needed for special case of dropping element which then changes) */
9775 /* (must be checked after creating new element for walkable group elements) */
9776 #if USE_FIX_KILLED_BY_NON_WALKABLE
9777 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9778 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9785 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9786 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9795 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9796 if (new_element_is_player)
9797 RelocatePlayer(x, y, new_element);
9800 ChangeCount[x][y]++; /* count number of changes in the same frame */
9802 TestIfBadThingTouchesPlayer(x, y);
9803 TestIfPlayerTouchesCustomElement(x, y);
9804 TestIfElementTouchesCustomElement(x, y);
9807 static void CreateField(int x, int y, int element)
9809 CreateFieldExt(x, y, element, FALSE);
9812 static void CreateElementFromChange(int x, int y, int element)
9814 element = GET_VALID_RUNTIME_ELEMENT(element);
9816 #if USE_STOP_CHANGED_ELEMENTS
9817 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9819 int old_element = Feld[x][y];
9821 /* prevent changed element from moving in same engine frame
9822 unless both old and new element can either fall or move */
9823 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9824 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9829 CreateFieldExt(x, y, element, TRUE);
9832 static boolean ChangeElement(int x, int y, int element, int page)
9834 struct ElementInfo *ei = &element_info[element];
9835 struct ElementChangeInfo *change = &ei->change_page[page];
9836 int ce_value = CustomValue[x][y];
9837 int ce_score = ei->collect_score;
9839 int old_element = Feld[x][y];
9841 /* always use default change event to prevent running into a loop */
9842 if (ChangeEvent[x][y] == -1)
9843 ChangeEvent[x][y] = CE_DELAY;
9845 if (ChangeEvent[x][y] == CE_DELAY)
9847 /* reset actual trigger element, trigger player and action element */
9848 change->actual_trigger_element = EL_EMPTY;
9849 change->actual_trigger_player = EL_PLAYER_1;
9850 change->actual_trigger_side = CH_SIDE_NONE;
9851 change->actual_trigger_ce_value = 0;
9852 change->actual_trigger_ce_score = 0;
9855 /* do not change elements more than a specified maximum number of changes */
9856 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9859 ChangeCount[x][y]++; /* count number of changes in the same frame */
9861 if (change->explode)
9868 if (change->use_target_content)
9870 boolean complete_replace = TRUE;
9871 boolean can_replace[3][3];
9874 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9877 boolean is_walkable;
9878 boolean is_diggable;
9879 boolean is_collectible;
9880 boolean is_removable;
9881 boolean is_destructible;
9882 int ex = x + xx - 1;
9883 int ey = y + yy - 1;
9884 int content_element = change->target_content.e[xx][yy];
9887 can_replace[xx][yy] = TRUE;
9889 if (ex == x && ey == y) /* do not check changing element itself */
9892 if (content_element == EL_EMPTY_SPACE)
9894 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9899 if (!IN_LEV_FIELD(ex, ey))
9901 can_replace[xx][yy] = FALSE;
9902 complete_replace = FALSE;
9909 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9910 e = MovingOrBlocked2Element(ex, ey);
9912 is_empty = (IS_FREE(ex, ey) ||
9913 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9915 is_walkable = (is_empty || IS_WALKABLE(e));
9916 is_diggable = (is_empty || IS_DIGGABLE(e));
9917 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9918 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9919 is_removable = (is_diggable || is_collectible);
9921 can_replace[xx][yy] =
9922 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9923 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9924 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9925 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9926 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9927 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9928 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9930 if (!can_replace[xx][yy])
9931 complete_replace = FALSE;
9934 if (!change->only_if_complete || complete_replace)
9936 boolean something_has_changed = FALSE;
9938 if (change->only_if_complete && change->use_random_replace &&
9939 RND(100) < change->random_percentage)
9942 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9944 int ex = x + xx - 1;
9945 int ey = y + yy - 1;
9946 int content_element;
9948 if (can_replace[xx][yy] && (!change->use_random_replace ||
9949 RND(100) < change->random_percentage))
9951 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9952 RemoveMovingField(ex, ey);
9954 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9956 content_element = change->target_content.e[xx][yy];
9957 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9958 ce_value, ce_score);
9960 CreateElementFromChange(ex, ey, target_element);
9962 something_has_changed = TRUE;
9964 /* for symmetry reasons, freeze newly created border elements */
9965 if (ex != x || ey != y)
9966 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9970 if (something_has_changed)
9972 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9973 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9979 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9980 ce_value, ce_score);
9982 if (element == EL_DIAGONAL_GROWING ||
9983 element == EL_DIAGONAL_SHRINKING)
9985 target_element = Store[x][y];
9987 Store[x][y] = EL_EMPTY;
9990 CreateElementFromChange(x, y, target_element);
9992 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9993 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9996 /* this uses direct change before indirect change */
9997 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10002 #if USE_NEW_DELAYED_ACTION
10004 static void HandleElementChange(int x, int y, int page)
10006 int element = MovingOrBlocked2Element(x, y);
10007 struct ElementInfo *ei = &element_info[element];
10008 struct ElementChangeInfo *change = &ei->change_page[page];
10011 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10012 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10015 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10016 x, y, element, element_info[element].token_name);
10017 printf("HandleElementChange(): This should never happen!\n");
10022 /* this can happen with classic bombs on walkable, changing elements */
10023 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10026 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10027 ChangeDelay[x][y] = 0;
10033 if (ChangeDelay[x][y] == 0) /* initialize element change */
10035 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10037 if (change->can_change)
10040 /* !!! not clear why graphic animation should be reset at all here !!! */
10041 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10042 #if USE_GFX_RESET_WHEN_NOT_MOVING
10043 /* when a custom element is about to change (for example by change delay),
10044 do not reset graphic animation when the custom element is moving */
10045 if (!IS_MOVING(x, y))
10048 ResetGfxAnimation(x, y);
10049 ResetRandomAnimationValue(x, y);
10053 if (change->pre_change_function)
10054 change->pre_change_function(x, y);
10058 ChangeDelay[x][y]--;
10060 if (ChangeDelay[x][y] != 0) /* continue element change */
10062 if (change->can_change)
10064 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10066 if (IS_ANIMATED(graphic))
10067 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10069 if (change->change_function)
10070 change->change_function(x, y);
10073 else /* finish element change */
10075 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10077 page = ChangePage[x][y];
10078 ChangePage[x][y] = -1;
10080 change = &ei->change_page[page];
10083 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10085 ChangeDelay[x][y] = 1; /* try change after next move step */
10086 ChangePage[x][y] = page; /* remember page to use for change */
10091 if (change->can_change)
10093 if (ChangeElement(x, y, element, page))
10095 if (change->post_change_function)
10096 change->post_change_function(x, y);
10100 if (change->has_action)
10101 ExecuteCustomElementAction(x, y, element, page);
10107 static void HandleElementChange(int x, int y, int page)
10109 int element = MovingOrBlocked2Element(x, y);
10110 struct ElementInfo *ei = &element_info[element];
10111 struct ElementChangeInfo *change = &ei->change_page[page];
10114 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10117 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10118 x, y, element, element_info[element].token_name);
10119 printf("HandleElementChange(): This should never happen!\n");
10124 /* this can happen with classic bombs on walkable, changing elements */
10125 if (!CAN_CHANGE(element))
10128 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10129 ChangeDelay[x][y] = 0;
10135 if (ChangeDelay[x][y] == 0) /* initialize element change */
10137 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10139 ResetGfxAnimation(x, y);
10140 ResetRandomAnimationValue(x, y);
10142 if (change->pre_change_function)
10143 change->pre_change_function(x, y);
10146 ChangeDelay[x][y]--;
10148 if (ChangeDelay[x][y] != 0) /* continue element change */
10150 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10152 if (IS_ANIMATED(graphic))
10153 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10155 if (change->change_function)
10156 change->change_function(x, y);
10158 else /* finish element change */
10160 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10162 page = ChangePage[x][y];
10163 ChangePage[x][y] = -1;
10165 change = &ei->change_page[page];
10168 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10170 ChangeDelay[x][y] = 1; /* try change after next move step */
10171 ChangePage[x][y] = page; /* remember page to use for change */
10176 if (ChangeElement(x, y, element, page))
10178 if (change->post_change_function)
10179 change->post_change_function(x, y);
10186 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10187 int trigger_element,
10189 int trigger_player,
10193 boolean change_done_any = FALSE;
10194 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10197 if (!(trigger_events[trigger_element][trigger_event]))
10201 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10202 trigger_event, recursion_loop_depth, recursion_loop_detected,
10203 recursion_loop_element, EL_NAME(recursion_loop_element));
10206 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10208 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10210 int element = EL_CUSTOM_START + i;
10211 boolean change_done = FALSE;
10214 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10215 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10218 for (p = 0; p < element_info[element].num_change_pages; p++)
10220 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10222 if (change->can_change_or_has_action &&
10223 change->has_event[trigger_event] &&
10224 change->trigger_side & trigger_side &&
10225 change->trigger_player & trigger_player &&
10226 change->trigger_page & trigger_page_bits &&
10227 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10229 change->actual_trigger_element = trigger_element;
10230 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10231 change->actual_trigger_side = trigger_side;
10232 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10233 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10235 if ((change->can_change && !change_done) || change->has_action)
10239 SCAN_PLAYFIELD(x, y)
10241 if (Feld[x][y] == element)
10243 if (change->can_change && !change_done)
10245 ChangeDelay[x][y] = 1;
10246 ChangeEvent[x][y] = trigger_event;
10248 HandleElementChange(x, y, p);
10250 #if USE_NEW_DELAYED_ACTION
10251 else if (change->has_action)
10253 ExecuteCustomElementAction(x, y, element, p);
10254 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10257 if (change->has_action)
10259 ExecuteCustomElementAction(x, y, element, p);
10260 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10266 if (change->can_change)
10268 change_done = TRUE;
10269 change_done_any = TRUE;
10276 RECURSION_LOOP_DETECTION_END();
10278 return change_done_any;
10281 static boolean CheckElementChangeExt(int x, int y,
10283 int trigger_element,
10285 int trigger_player,
10288 boolean change_done = FALSE;
10291 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10292 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10295 if (Feld[x][y] == EL_BLOCKED)
10297 Blocked2Moving(x, y, &x, &y);
10298 element = Feld[x][y];
10302 /* check if element has already changed */
10303 if (Feld[x][y] != element)
10306 /* check if element has already changed or is about to change after moving */
10307 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10308 Feld[x][y] != element) ||
10310 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10311 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10312 ChangePage[x][y] != -1)))
10317 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10318 trigger_event, recursion_loop_depth, recursion_loop_detected,
10319 recursion_loop_element, EL_NAME(recursion_loop_element));
10322 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10324 for (p = 0; p < element_info[element].num_change_pages; p++)
10326 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10328 /* check trigger element for all events where the element that is checked
10329 for changing interacts with a directly adjacent element -- this is
10330 different to element changes that affect other elements to change on the
10331 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10332 boolean check_trigger_element =
10333 (trigger_event == CE_TOUCHING_X ||
10334 trigger_event == CE_HITTING_X ||
10335 trigger_event == CE_HIT_BY_X ||
10337 /* this one was forgotten until 3.2.3 */
10338 trigger_event == CE_DIGGING_X);
10341 if (change->can_change_or_has_action &&
10342 change->has_event[trigger_event] &&
10343 change->trigger_side & trigger_side &&
10344 change->trigger_player & trigger_player &&
10345 (!check_trigger_element ||
10346 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10348 change->actual_trigger_element = trigger_element;
10349 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10350 change->actual_trigger_side = trigger_side;
10351 change->actual_trigger_ce_value = CustomValue[x][y];
10352 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10354 /* special case: trigger element not at (x,y) position for some events */
10355 if (check_trigger_element)
10367 { 0, 0 }, { 0, 0 }, { 0, 0 },
10371 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10372 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10374 change->actual_trigger_ce_value = CustomValue[xx][yy];
10375 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10378 if (change->can_change && !change_done)
10380 ChangeDelay[x][y] = 1;
10381 ChangeEvent[x][y] = trigger_event;
10383 HandleElementChange(x, y, p);
10385 change_done = TRUE;
10387 #if USE_NEW_DELAYED_ACTION
10388 else if (change->has_action)
10390 ExecuteCustomElementAction(x, y, element, p);
10391 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10394 if (change->has_action)
10396 ExecuteCustomElementAction(x, y, element, p);
10397 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10403 RECURSION_LOOP_DETECTION_END();
10405 return change_done;
10408 static void PlayPlayerSound(struct PlayerInfo *player)
10410 int jx = player->jx, jy = player->jy;
10411 int sound_element = player->artwork_element;
10412 int last_action = player->last_action_waiting;
10413 int action = player->action_waiting;
10415 if (player->is_waiting)
10417 if (action != last_action)
10418 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10420 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10424 if (action != last_action)
10425 StopSound(element_info[sound_element].sound[last_action]);
10427 if (last_action == ACTION_SLEEPING)
10428 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10432 static void PlayAllPlayersSound()
10436 for (i = 0; i < MAX_PLAYERS; i++)
10437 if (stored_player[i].active)
10438 PlayPlayerSound(&stored_player[i]);
10441 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10443 boolean last_waiting = player->is_waiting;
10444 int move_dir = player->MovDir;
10446 player->dir_waiting = move_dir;
10447 player->last_action_waiting = player->action_waiting;
10451 if (!last_waiting) /* not waiting -> waiting */
10453 player->is_waiting = TRUE;
10455 player->frame_counter_bored =
10457 game.player_boring_delay_fixed +
10458 GetSimpleRandom(game.player_boring_delay_random);
10459 player->frame_counter_sleeping =
10461 game.player_sleeping_delay_fixed +
10462 GetSimpleRandom(game.player_sleeping_delay_random);
10464 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10467 if (game.player_sleeping_delay_fixed +
10468 game.player_sleeping_delay_random > 0 &&
10469 player->anim_delay_counter == 0 &&
10470 player->post_delay_counter == 0 &&
10471 FrameCounter >= player->frame_counter_sleeping)
10472 player->is_sleeping = TRUE;
10473 else if (game.player_boring_delay_fixed +
10474 game.player_boring_delay_random > 0 &&
10475 FrameCounter >= player->frame_counter_bored)
10476 player->is_bored = TRUE;
10478 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10479 player->is_bored ? ACTION_BORING :
10482 if (player->is_sleeping && player->use_murphy)
10484 /* special case for sleeping Murphy when leaning against non-free tile */
10486 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10487 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10488 !IS_MOVING(player->jx - 1, player->jy)))
10489 move_dir = MV_LEFT;
10490 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10491 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10492 !IS_MOVING(player->jx + 1, player->jy)))
10493 move_dir = MV_RIGHT;
10495 player->is_sleeping = FALSE;
10497 player->dir_waiting = move_dir;
10500 if (player->is_sleeping)
10502 if (player->num_special_action_sleeping > 0)
10504 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10506 int last_special_action = player->special_action_sleeping;
10507 int num_special_action = player->num_special_action_sleeping;
10508 int special_action =
10509 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10510 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10511 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10512 last_special_action + 1 : ACTION_SLEEPING);
10513 int special_graphic =
10514 el_act_dir2img(player->artwork_element, special_action, move_dir);
10516 player->anim_delay_counter =
10517 graphic_info[special_graphic].anim_delay_fixed +
10518 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10519 player->post_delay_counter =
10520 graphic_info[special_graphic].post_delay_fixed +
10521 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10523 player->special_action_sleeping = special_action;
10526 if (player->anim_delay_counter > 0)
10528 player->action_waiting = player->special_action_sleeping;
10529 player->anim_delay_counter--;
10531 else if (player->post_delay_counter > 0)
10533 player->post_delay_counter--;
10537 else if (player->is_bored)
10539 if (player->num_special_action_bored > 0)
10541 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10543 int special_action =
10544 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10545 int special_graphic =
10546 el_act_dir2img(player->artwork_element, special_action, move_dir);
10548 player->anim_delay_counter =
10549 graphic_info[special_graphic].anim_delay_fixed +
10550 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10551 player->post_delay_counter =
10552 graphic_info[special_graphic].post_delay_fixed +
10553 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10555 player->special_action_bored = special_action;
10558 if (player->anim_delay_counter > 0)
10560 player->action_waiting = player->special_action_bored;
10561 player->anim_delay_counter--;
10563 else if (player->post_delay_counter > 0)
10565 player->post_delay_counter--;
10570 else if (last_waiting) /* waiting -> not waiting */
10572 player->is_waiting = FALSE;
10573 player->is_bored = FALSE;
10574 player->is_sleeping = FALSE;
10576 player->frame_counter_bored = -1;
10577 player->frame_counter_sleeping = -1;
10579 player->anim_delay_counter = 0;
10580 player->post_delay_counter = 0;
10582 player->dir_waiting = player->MovDir;
10583 player->action_waiting = ACTION_DEFAULT;
10585 player->special_action_bored = ACTION_DEFAULT;
10586 player->special_action_sleeping = ACTION_DEFAULT;
10590 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10592 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10593 int left = player_action & JOY_LEFT;
10594 int right = player_action & JOY_RIGHT;
10595 int up = player_action & JOY_UP;
10596 int down = player_action & JOY_DOWN;
10597 int button1 = player_action & JOY_BUTTON_1;
10598 int button2 = player_action & JOY_BUTTON_2;
10599 int dx = (left ? -1 : right ? 1 : 0);
10600 int dy = (up ? -1 : down ? 1 : 0);
10602 if (!player->active || tape.pausing)
10608 snapped = SnapField(player, dx, dy);
10612 dropped = DropElement(player);
10614 moved = MovePlayer(player, dx, dy);
10617 if (tape.single_step && tape.recording && !tape.pausing)
10619 if (button1 || (dropped && !moved))
10621 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10622 SnapField(player, 0, 0); /* stop snapping */
10626 SetPlayerWaiting(player, FALSE);
10628 return player_action;
10632 /* no actions for this player (no input at player's configured device) */
10634 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10635 SnapField(player, 0, 0);
10636 CheckGravityMovementWhenNotMoving(player);
10638 if (player->MovPos == 0)
10639 SetPlayerWaiting(player, TRUE);
10641 if (player->MovPos == 0) /* needed for tape.playing */
10642 player->is_moving = FALSE;
10644 player->is_dropping = FALSE;
10645 player->is_dropping_pressed = FALSE;
10646 player->drop_pressed_delay = 0;
10652 static void CheckLevelTime()
10656 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10658 if (level.native_em_level->lev->home == 0) /* all players at home */
10660 PlayerWins(local_player);
10662 AllPlayersGone = TRUE;
10664 level.native_em_level->lev->home = -1;
10667 if (level.native_em_level->ply[0]->alive == 0 &&
10668 level.native_em_level->ply[1]->alive == 0 &&
10669 level.native_em_level->ply[2]->alive == 0 &&
10670 level.native_em_level->ply[3]->alive == 0) /* all dead */
10671 AllPlayersGone = TRUE;
10674 if (TimeFrames >= FRAMES_PER_SECOND)
10679 for (i = 0; i < MAX_PLAYERS; i++)
10681 struct PlayerInfo *player = &stored_player[i];
10683 if (SHIELD_ON(player))
10685 player->shield_normal_time_left--;
10687 if (player->shield_deadly_time_left > 0)
10688 player->shield_deadly_time_left--;
10692 if (!local_player->LevelSolved && !level.use_step_counter)
10700 if (TimeLeft <= 10 && setup.time_limit)
10701 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10704 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10706 DisplayGameControlValues();
10708 DrawGameValue_Time(TimeLeft);
10711 if (!TimeLeft && setup.time_limit)
10713 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10714 level.native_em_level->lev->killed_out_of_time = TRUE;
10716 for (i = 0; i < MAX_PLAYERS; i++)
10717 KillPlayer(&stored_player[i]);
10721 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10723 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10725 DisplayGameControlValues();
10728 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10729 DrawGameValue_Time(TimePlayed);
10732 level.native_em_level->lev->time =
10733 (level.time == 0 ? TimePlayed : TimeLeft);
10736 if (tape.recording || tape.playing)
10737 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10740 DrawGameDoorValues();
10743 void AdvanceFrameAndPlayerCounters(int player_nr)
10747 /* advance frame counters (global frame counter and time frame counter) */
10751 /* advance player counters (counters for move delay, move animation etc.) */
10752 for (i = 0; i < MAX_PLAYERS; i++)
10754 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10755 int move_delay_value = stored_player[i].move_delay_value;
10756 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10758 if (!advance_player_counters) /* not all players may be affected */
10761 #if USE_NEW_PLAYER_ANIM
10762 if (move_frames == 0) /* less than one move per game frame */
10764 int stepsize = TILEX / move_delay_value;
10765 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10766 int count = (stored_player[i].is_moving ?
10767 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10769 if (count % delay == 0)
10774 stored_player[i].Frame += move_frames;
10776 if (stored_player[i].MovPos != 0)
10777 stored_player[i].StepFrame += move_frames;
10779 if (stored_player[i].move_delay > 0)
10780 stored_player[i].move_delay--;
10782 /* due to bugs in previous versions, counter must count up, not down */
10783 if (stored_player[i].push_delay != -1)
10784 stored_player[i].push_delay++;
10786 if (stored_player[i].drop_delay > 0)
10787 stored_player[i].drop_delay--;
10789 if (stored_player[i].is_dropping_pressed)
10790 stored_player[i].drop_pressed_delay++;
10794 void StartGameActions(boolean init_network_game, boolean record_tape,
10797 unsigned long new_random_seed = InitRND(random_seed);
10800 TapeStartRecording(new_random_seed);
10802 #if defined(NETWORK_AVALIABLE)
10803 if (init_network_game)
10805 SendToServer_StartPlaying();
10816 static unsigned long game_frame_delay = 0;
10817 unsigned long game_frame_delay_value;
10818 byte *recorded_player_action;
10819 byte summarized_player_action = 0;
10820 byte tape_action[MAX_PLAYERS];
10823 /* detect endless loops, caused by custom element programming */
10824 if (recursion_loop_detected && recursion_loop_depth == 0)
10826 char *message = getStringCat3("Internal Error ! Element ",
10827 EL_NAME(recursion_loop_element),
10828 " caused endless loop ! Quit the game ?");
10830 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10831 EL_NAME(recursion_loop_element));
10833 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10835 recursion_loop_detected = FALSE; /* if game should be continued */
10842 if (game.restart_level)
10843 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10845 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10847 if (level.native_em_level->lev->home == 0) /* all players at home */
10849 PlayerWins(local_player);
10851 AllPlayersGone = TRUE;
10853 level.native_em_level->lev->home = -1;
10856 if (level.native_em_level->ply[0]->alive == 0 &&
10857 level.native_em_level->ply[1]->alive == 0 &&
10858 level.native_em_level->ply[2]->alive == 0 &&
10859 level.native_em_level->ply[3]->alive == 0) /* all dead */
10860 AllPlayersGone = TRUE;
10863 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10866 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10869 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10872 game_frame_delay_value =
10873 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10875 if (tape.playing && tape.warp_forward && !tape.pausing)
10876 game_frame_delay_value = 0;
10878 /* ---------- main game synchronization point ---------- */
10880 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10882 if (network_playing && !network_player_action_received)
10884 /* try to get network player actions in time */
10886 #if defined(NETWORK_AVALIABLE)
10887 /* last chance to get network player actions without main loop delay */
10888 HandleNetworking();
10891 /* game was quit by network peer */
10892 if (game_status != GAME_MODE_PLAYING)
10895 if (!network_player_action_received)
10896 return; /* failed to get network player actions in time */
10898 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10904 /* at this point we know that we really continue executing the game */
10906 network_player_action_received = FALSE;
10908 /* when playing tape, read previously recorded player input from tape data */
10909 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10912 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10917 if (tape.set_centered_player)
10919 game.centered_player_nr_next = tape.centered_player_nr_next;
10920 game.set_centered_player = TRUE;
10923 for (i = 0; i < MAX_PLAYERS; i++)
10925 summarized_player_action |= stored_player[i].action;
10927 if (!network_playing)
10928 stored_player[i].effective_action = stored_player[i].action;
10931 #if defined(NETWORK_AVALIABLE)
10932 if (network_playing)
10933 SendToServer_MovePlayer(summarized_player_action);
10936 if (!options.network && !setup.team_mode)
10937 local_player->effective_action = summarized_player_action;
10939 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10941 for (i = 0; i < MAX_PLAYERS; i++)
10942 stored_player[i].effective_action =
10943 (i == game.centered_player_nr ? summarized_player_action : 0);
10946 if (recorded_player_action != NULL)
10947 for (i = 0; i < MAX_PLAYERS; i++)
10948 stored_player[i].effective_action = recorded_player_action[i];
10950 for (i = 0; i < MAX_PLAYERS; i++)
10952 tape_action[i] = stored_player[i].effective_action;
10954 /* (this can only happen in the R'n'D game engine) */
10955 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10956 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10959 /* only record actions from input devices, but not programmed actions */
10960 if (tape.recording)
10961 TapeRecordAction(tape_action);
10963 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10965 GameActions_EM_Main();
10973 void GameActions_EM_Main()
10975 byte effective_action[MAX_PLAYERS];
10976 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10979 for (i = 0; i < MAX_PLAYERS; i++)
10980 effective_action[i] = stored_player[i].effective_action;
10982 GameActions_EM(effective_action, warp_mode);
10986 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10989 void GameActions_RND()
10991 int magic_wall_x = 0, magic_wall_y = 0;
10992 int i, x, y, element, graphic;
10994 InitPlayfieldScanModeVars();
10996 #if USE_ONE_MORE_CHANGE_PER_FRAME
10997 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10999 SCAN_PLAYFIELD(x, y)
11001 ChangeCount[x][y] = 0;
11002 ChangeEvent[x][y] = -1;
11007 if (game.set_centered_player)
11009 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11011 /* switching to "all players" only possible if all players fit to screen */
11012 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11014 game.centered_player_nr_next = game.centered_player_nr;
11015 game.set_centered_player = FALSE;
11018 /* do not switch focus to non-existing (or non-active) player */
11019 if (game.centered_player_nr_next >= 0 &&
11020 !stored_player[game.centered_player_nr_next].active)
11022 game.centered_player_nr_next = game.centered_player_nr;
11023 game.set_centered_player = FALSE;
11027 if (game.set_centered_player &&
11028 ScreenMovPos == 0) /* screen currently aligned at tile position */
11032 if (game.centered_player_nr_next == -1)
11034 setScreenCenteredToAllPlayers(&sx, &sy);
11038 sx = stored_player[game.centered_player_nr_next].jx;
11039 sy = stored_player[game.centered_player_nr_next].jy;
11042 game.centered_player_nr = game.centered_player_nr_next;
11043 game.set_centered_player = FALSE;
11045 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11046 DrawGameDoorValues();
11049 for (i = 0; i < MAX_PLAYERS; i++)
11051 int actual_player_action = stored_player[i].effective_action;
11054 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11055 - rnd_equinox_tetrachloride 048
11056 - rnd_equinox_tetrachloride_ii 096
11057 - rnd_emanuel_schmieg 002
11058 - doctor_sloan_ww 001, 020
11060 if (stored_player[i].MovPos == 0)
11061 CheckGravityMovement(&stored_player[i]);
11064 /* overwrite programmed action with tape action */
11065 if (stored_player[i].programmed_action)
11066 actual_player_action = stored_player[i].programmed_action;
11068 PlayerActions(&stored_player[i], actual_player_action);
11070 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11073 ScrollScreen(NULL, SCROLL_GO_ON);
11075 /* for backwards compatibility, the following code emulates a fixed bug that
11076 occured when pushing elements (causing elements that just made their last
11077 pushing step to already (if possible) make their first falling step in the
11078 same game frame, which is bad); this code is also needed to use the famous
11079 "spring push bug" which is used in older levels and might be wanted to be
11080 used also in newer levels, but in this case the buggy pushing code is only
11081 affecting the "spring" element and no other elements */
11083 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11085 for (i = 0; i < MAX_PLAYERS; i++)
11087 struct PlayerInfo *player = &stored_player[i];
11088 int x = player->jx;
11089 int y = player->jy;
11091 if (player->active && player->is_pushing && player->is_moving &&
11093 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11094 Feld[x][y] == EL_SPRING))
11096 ContinueMoving(x, y);
11098 /* continue moving after pushing (this is actually a bug) */
11099 if (!IS_MOVING(x, y))
11100 Stop[x][y] = FALSE;
11106 debug_print_timestamp(0, "start main loop profiling");
11109 SCAN_PLAYFIELD(x, y)
11111 ChangeCount[x][y] = 0;
11112 ChangeEvent[x][y] = -1;
11114 /* this must be handled before main playfield loop */
11115 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11118 if (MovDelay[x][y] <= 0)
11122 #if USE_NEW_SNAP_DELAY
11123 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11126 if (MovDelay[x][y] <= 0)
11129 DrawLevelField(x, y);
11131 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11137 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11139 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11140 printf("GameActions(): This should never happen!\n");
11142 ChangePage[x][y] = -1;
11146 Stop[x][y] = FALSE;
11147 if (WasJustMoving[x][y] > 0)
11148 WasJustMoving[x][y]--;
11149 if (WasJustFalling[x][y] > 0)
11150 WasJustFalling[x][y]--;
11151 if (CheckCollision[x][y] > 0)
11152 CheckCollision[x][y]--;
11153 if (CheckImpact[x][y] > 0)
11154 CheckImpact[x][y]--;
11158 /* reset finished pushing action (not done in ContinueMoving() to allow
11159 continuous pushing animation for elements with zero push delay) */
11160 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11162 ResetGfxAnimation(x, y);
11163 DrawLevelField(x, y);
11167 if (IS_BLOCKED(x, y))
11171 Blocked2Moving(x, y, &oldx, &oldy);
11172 if (!IS_MOVING(oldx, oldy))
11174 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11175 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11176 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11177 printf("GameActions(): This should never happen!\n");
11184 debug_print_timestamp(0, "- time for pre-main loop:");
11187 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11188 SCAN_PLAYFIELD(x, y)
11190 element = Feld[x][y];
11191 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11196 int element2 = element;
11197 int graphic2 = graphic;
11199 int element2 = Feld[x][y];
11200 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11202 int last_gfx_frame = GfxFrame[x][y];
11204 if (graphic_info[graphic2].anim_global_sync)
11205 GfxFrame[x][y] = FrameCounter;
11206 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11207 GfxFrame[x][y] = CustomValue[x][y];
11208 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11209 GfxFrame[x][y] = element_info[element2].collect_score;
11210 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11211 GfxFrame[x][y] = ChangeDelay[x][y];
11213 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11214 DrawLevelGraphicAnimation(x, y, graphic2);
11217 ResetGfxFrame(x, y, TRUE);
11221 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11222 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11223 ResetRandomAnimationValue(x, y);
11227 SetRandomAnimationValue(x, y);
11231 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11234 #endif // -------------------- !!! TEST ONLY !!! --------------------
11237 debug_print_timestamp(0, "- time for TEST loop: -->");
11240 SCAN_PLAYFIELD(x, y)
11242 element = Feld[x][y];
11243 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11245 ResetGfxFrame(x, y, TRUE);
11247 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11248 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11249 ResetRandomAnimationValue(x, y);
11251 SetRandomAnimationValue(x, y);
11253 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11255 if (IS_INACTIVE(element))
11257 if (IS_ANIMATED(graphic))
11258 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11263 /* this may take place after moving, so 'element' may have changed */
11264 if (IS_CHANGING(x, y) &&
11265 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11267 int page = element_info[element].event_page_nr[CE_DELAY];
11270 HandleElementChange(x, y, page);
11272 if (CAN_CHANGE(element))
11273 HandleElementChange(x, y, page);
11275 if (HAS_ACTION(element))
11276 ExecuteCustomElementAction(x, y, element, page);
11279 element = Feld[x][y];
11280 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11283 #if 0 // ---------------------------------------------------------------------
11285 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11289 element = Feld[x][y];
11290 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11292 if (IS_ANIMATED(graphic) &&
11293 !IS_MOVING(x, y) &&
11295 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11297 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11298 DrawTwinkleOnField(x, y);
11300 else if (IS_MOVING(x, y))
11301 ContinueMoving(x, y);
11308 case EL_EM_EXIT_OPEN:
11309 case EL_SP_EXIT_OPEN:
11310 case EL_STEEL_EXIT_OPEN:
11311 case EL_EM_STEEL_EXIT_OPEN:
11312 case EL_SP_TERMINAL:
11313 case EL_SP_TERMINAL_ACTIVE:
11314 case EL_EXTRA_TIME:
11315 case EL_SHIELD_NORMAL:
11316 case EL_SHIELD_DEADLY:
11317 if (IS_ANIMATED(graphic))
11318 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11321 case EL_DYNAMITE_ACTIVE:
11322 case EL_EM_DYNAMITE_ACTIVE:
11323 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11324 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11325 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11326 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11327 case EL_SP_DISK_RED_ACTIVE:
11328 CheckDynamite(x, y);
11331 case EL_AMOEBA_GROWING:
11332 AmoebeWaechst(x, y);
11335 case EL_AMOEBA_SHRINKING:
11336 AmoebaDisappearing(x, y);
11339 #if !USE_NEW_AMOEBA_CODE
11340 case EL_AMOEBA_WET:
11341 case EL_AMOEBA_DRY:
11342 case EL_AMOEBA_FULL:
11344 case EL_EMC_DRIPPER:
11345 AmoebeAbleger(x, y);
11349 case EL_GAME_OF_LIFE:
11354 case EL_EXIT_CLOSED:
11358 case EL_EM_EXIT_CLOSED:
11362 case EL_STEEL_EXIT_CLOSED:
11363 CheckExitSteel(x, y);
11366 case EL_EM_STEEL_EXIT_CLOSED:
11367 CheckExitSteelEM(x, y);
11370 case EL_SP_EXIT_CLOSED:
11374 case EL_EXPANDABLE_WALL_GROWING:
11375 case EL_EXPANDABLE_STEELWALL_GROWING:
11376 MauerWaechst(x, y);
11379 case EL_EXPANDABLE_WALL:
11380 case EL_EXPANDABLE_WALL_HORIZONTAL:
11381 case EL_EXPANDABLE_WALL_VERTICAL:
11382 case EL_EXPANDABLE_WALL_ANY:
11383 case EL_BD_EXPANDABLE_WALL:
11384 MauerAbleger(x, y);
11387 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11388 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11389 case EL_EXPANDABLE_STEELWALL_ANY:
11390 MauerAblegerStahl(x, y);
11394 CheckForDragon(x, y);
11400 case EL_ELEMENT_SNAPPING:
11401 case EL_DIAGONAL_SHRINKING:
11402 case EL_DIAGONAL_GROWING:
11405 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11407 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11412 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11413 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11418 #else // ---------------------------------------------------------------------
11420 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11424 element = Feld[x][y];
11425 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11427 if (IS_ANIMATED(graphic) &&
11428 !IS_MOVING(x, y) &&
11430 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11432 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11433 DrawTwinkleOnField(x, y);
11435 else if ((element == EL_ACID ||
11436 element == EL_EXIT_OPEN ||
11437 element == EL_EM_EXIT_OPEN ||
11438 element == EL_SP_EXIT_OPEN ||
11439 element == EL_STEEL_EXIT_OPEN ||
11440 element == EL_EM_STEEL_EXIT_OPEN ||
11441 element == EL_SP_TERMINAL ||
11442 element == EL_SP_TERMINAL_ACTIVE ||
11443 element == EL_EXTRA_TIME ||
11444 element == EL_SHIELD_NORMAL ||
11445 element == EL_SHIELD_DEADLY) &&
11446 IS_ANIMATED(graphic))
11447 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11448 else if (IS_MOVING(x, y))
11449 ContinueMoving(x, y);
11450 else if (IS_ACTIVE_BOMB(element))
11451 CheckDynamite(x, y);
11452 else if (element == EL_AMOEBA_GROWING)
11453 AmoebeWaechst(x, y);
11454 else if (element == EL_AMOEBA_SHRINKING)
11455 AmoebaDisappearing(x, y);
11457 #if !USE_NEW_AMOEBA_CODE
11458 else if (IS_AMOEBALIVE(element))
11459 AmoebeAbleger(x, y);
11462 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11464 else if (element == EL_EXIT_CLOSED)
11466 else if (element == EL_EM_EXIT_CLOSED)
11468 else if (element == EL_STEEL_EXIT_CLOSED)
11469 CheckExitSteel(x, y);
11470 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11471 CheckExitSteelEM(x, y);
11472 else if (element == EL_SP_EXIT_CLOSED)
11474 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11475 element == EL_EXPANDABLE_STEELWALL_GROWING)
11476 MauerWaechst(x, y);
11477 else if (element == EL_EXPANDABLE_WALL ||
11478 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11479 element == EL_EXPANDABLE_WALL_VERTICAL ||
11480 element == EL_EXPANDABLE_WALL_ANY ||
11481 element == EL_BD_EXPANDABLE_WALL)
11482 MauerAbleger(x, y);
11483 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11484 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11485 element == EL_EXPANDABLE_STEELWALL_ANY)
11486 MauerAblegerStahl(x, y);
11487 else if (element == EL_FLAMES)
11488 CheckForDragon(x, y);
11489 else if (element == EL_EXPLOSION)
11490 ; /* drawing of correct explosion animation is handled separately */
11491 else if (element == EL_ELEMENT_SNAPPING ||
11492 element == EL_DIAGONAL_SHRINKING ||
11493 element == EL_DIAGONAL_GROWING)
11495 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11497 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11499 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11500 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11502 #endif // ---------------------------------------------------------------------
11504 if (IS_BELT_ACTIVE(element))
11505 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11507 if (game.magic_wall_active)
11509 int jx = local_player->jx, jy = local_player->jy;
11511 /* play the element sound at the position nearest to the player */
11512 if ((element == EL_MAGIC_WALL_FULL ||
11513 element == EL_MAGIC_WALL_ACTIVE ||
11514 element == EL_MAGIC_WALL_EMPTYING ||
11515 element == EL_BD_MAGIC_WALL_FULL ||
11516 element == EL_BD_MAGIC_WALL_ACTIVE ||
11517 element == EL_BD_MAGIC_WALL_EMPTYING ||
11518 element == EL_DC_MAGIC_WALL_FULL ||
11519 element == EL_DC_MAGIC_WALL_ACTIVE ||
11520 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11521 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11530 debug_print_timestamp(0, "- time for MAIN loop: -->");
11533 #if USE_NEW_AMOEBA_CODE
11534 /* new experimental amoeba growth stuff */
11535 if (!(FrameCounter % 8))
11537 static unsigned long random = 1684108901;
11539 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11541 x = RND(lev_fieldx);
11542 y = RND(lev_fieldy);
11543 element = Feld[x][y];
11545 if (!IS_PLAYER(x,y) &&
11546 (element == EL_EMPTY ||
11547 CAN_GROW_INTO(element) ||
11548 element == EL_QUICKSAND_EMPTY ||
11549 element == EL_QUICKSAND_FAST_EMPTY ||
11550 element == EL_ACID_SPLASH_LEFT ||
11551 element == EL_ACID_SPLASH_RIGHT))
11553 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11554 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11555 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11556 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11557 Feld[x][y] = EL_AMOEBA_DROP;
11560 random = random * 129 + 1;
11566 if (game.explosions_delayed)
11569 game.explosions_delayed = FALSE;
11571 SCAN_PLAYFIELD(x, y)
11573 element = Feld[x][y];
11575 if (ExplodeField[x][y])
11576 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11577 else if (element == EL_EXPLOSION)
11578 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11580 ExplodeField[x][y] = EX_TYPE_NONE;
11583 game.explosions_delayed = TRUE;
11586 if (game.magic_wall_active)
11588 if (!(game.magic_wall_time_left % 4))
11590 int element = Feld[magic_wall_x][magic_wall_y];
11592 if (element == EL_BD_MAGIC_WALL_FULL ||
11593 element == EL_BD_MAGIC_WALL_ACTIVE ||
11594 element == EL_BD_MAGIC_WALL_EMPTYING)
11595 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11596 else if (element == EL_DC_MAGIC_WALL_FULL ||
11597 element == EL_DC_MAGIC_WALL_ACTIVE ||
11598 element == EL_DC_MAGIC_WALL_EMPTYING)
11599 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11601 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11604 if (game.magic_wall_time_left > 0)
11606 game.magic_wall_time_left--;
11607 if (!game.magic_wall_time_left)
11609 SCAN_PLAYFIELD(x, y)
11611 element = Feld[x][y];
11613 if (element == EL_MAGIC_WALL_ACTIVE ||
11614 element == EL_MAGIC_WALL_FULL)
11616 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11617 DrawLevelField(x, y);
11619 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11620 element == EL_BD_MAGIC_WALL_FULL)
11622 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11623 DrawLevelField(x, y);
11625 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11626 element == EL_DC_MAGIC_WALL_FULL)
11628 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11629 DrawLevelField(x, y);
11633 game.magic_wall_active = FALSE;
11638 if (game.light_time_left > 0)
11640 game.light_time_left--;
11642 if (game.light_time_left == 0)
11643 RedrawAllLightSwitchesAndInvisibleElements();
11646 if (game.timegate_time_left > 0)
11648 game.timegate_time_left--;
11650 if (game.timegate_time_left == 0)
11651 CloseAllOpenTimegates();
11654 if (game.lenses_time_left > 0)
11656 game.lenses_time_left--;
11658 if (game.lenses_time_left == 0)
11659 RedrawAllInvisibleElementsForLenses();
11662 if (game.magnify_time_left > 0)
11664 game.magnify_time_left--;
11666 if (game.magnify_time_left == 0)
11667 RedrawAllInvisibleElementsForMagnifier();
11670 for (i = 0; i < MAX_PLAYERS; i++)
11672 struct PlayerInfo *player = &stored_player[i];
11674 if (SHIELD_ON(player))
11676 if (player->shield_deadly_time_left)
11677 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11678 else if (player->shield_normal_time_left)
11679 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11686 PlayAllPlayersSound();
11688 if (options.debug) /* calculate frames per second */
11690 static unsigned long fps_counter = 0;
11691 static int fps_frames = 0;
11692 unsigned long fps_delay_ms = Counter() - fps_counter;
11696 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11698 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11701 fps_counter = Counter();
11704 redraw_mask |= REDRAW_FPS;
11707 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11709 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11711 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11713 local_player->show_envelope = 0;
11717 debug_print_timestamp(0, "stop main loop profiling ");
11718 printf("----------------------------------------------------------\n");
11721 /* use random number generator in every frame to make it less predictable */
11722 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11726 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11728 int min_x = x, min_y = y, max_x = x, max_y = y;
11731 for (i = 0; i < MAX_PLAYERS; i++)
11733 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11735 if (!stored_player[i].active || &stored_player[i] == player)
11738 min_x = MIN(min_x, jx);
11739 min_y = MIN(min_y, jy);
11740 max_x = MAX(max_x, jx);
11741 max_y = MAX(max_y, jy);
11744 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11747 static boolean AllPlayersInVisibleScreen()
11751 for (i = 0; i < MAX_PLAYERS; i++)
11753 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11755 if (!stored_player[i].active)
11758 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11765 void ScrollLevel(int dx, int dy)
11768 static Bitmap *bitmap_db_field2 = NULL;
11769 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11776 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11777 /* only horizontal XOR vertical scroll direction allowed */
11778 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11783 if (bitmap_db_field2 == NULL)
11784 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11786 /* needed when blitting directly to same bitmap -- should not be needed with
11787 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11788 BlitBitmap(drawto_field, bitmap_db_field2,
11789 FX + TILEX * (dx == -1) - softscroll_offset,
11790 FY + TILEY * (dy == -1) - softscroll_offset,
11791 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11792 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11793 FX + TILEX * (dx == 1) - softscroll_offset,
11794 FY + TILEY * (dy == 1) - softscroll_offset);
11795 BlitBitmap(bitmap_db_field2, drawto_field,
11796 FX + TILEX * (dx == 1) - softscroll_offset,
11797 FY + TILEY * (dy == 1) - softscroll_offset,
11798 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11799 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11800 FX + TILEX * (dx == 1) - softscroll_offset,
11801 FY + TILEY * (dy == 1) - softscroll_offset);
11806 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11807 int xsize = (BX2 - BX1 + 1);
11808 int ysize = (BY2 - BY1 + 1);
11809 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11810 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11811 int step = (start < end ? +1 : -1);
11813 for (i = start; i != end; i += step)
11815 BlitBitmap(drawto_field, drawto_field,
11816 FX + TILEX * (dx != 0 ? i + step : 0),
11817 FY + TILEY * (dy != 0 ? i + step : 0),
11818 TILEX * (dx != 0 ? 1 : xsize),
11819 TILEY * (dy != 0 ? 1 : ysize),
11820 FX + TILEX * (dx != 0 ? i : 0),
11821 FY + TILEY * (dy != 0 ? i : 0));
11826 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11828 BlitBitmap(drawto_field, drawto_field,
11829 FX + TILEX * (dx == -1) - softscroll_offset,
11830 FY + TILEY * (dy == -1) - softscroll_offset,
11831 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11832 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11833 FX + TILEX * (dx == 1) - softscroll_offset,
11834 FY + TILEY * (dy == 1) - softscroll_offset);
11840 x = (dx == 1 ? BX1 : BX2);
11841 for (y = BY1; y <= BY2; y++)
11842 DrawScreenField(x, y);
11847 y = (dy == 1 ? BY1 : BY2);
11848 for (x = BX1; x <= BX2; x++)
11849 DrawScreenField(x, y);
11852 redraw_mask |= REDRAW_FIELD;
11855 static boolean canFallDown(struct PlayerInfo *player)
11857 int jx = player->jx, jy = player->jy;
11859 return (IN_LEV_FIELD(jx, jy + 1) &&
11860 (IS_FREE(jx, jy + 1) ||
11861 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11862 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11863 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11866 static boolean canPassField(int x, int y, int move_dir)
11868 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11869 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11870 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11871 int nextx = x + dx;
11872 int nexty = y + dy;
11873 int element = Feld[x][y];
11875 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11876 !CAN_MOVE(element) &&
11877 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11878 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11879 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11882 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11884 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11885 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11886 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11890 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11891 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11892 (IS_DIGGABLE(Feld[newx][newy]) ||
11893 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11894 canPassField(newx, newy, move_dir)));
11897 static void CheckGravityMovement(struct PlayerInfo *player)
11899 #if USE_PLAYER_GRAVITY
11900 if (player->gravity && !player->programmed_action)
11902 if (game.gravity && !player->programmed_action)
11905 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11906 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11907 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11908 int jx = player->jx, jy = player->jy;
11909 boolean player_is_moving_to_valid_field =
11910 (!player_is_snapping &&
11911 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11912 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11913 boolean player_can_fall_down = canFallDown(player);
11915 if (player_can_fall_down &&
11916 !player_is_moving_to_valid_field)
11917 player->programmed_action = MV_DOWN;
11921 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11923 return CheckGravityMovement(player);
11925 #if USE_PLAYER_GRAVITY
11926 if (player->gravity && !player->programmed_action)
11928 if (game.gravity && !player->programmed_action)
11931 int jx = player->jx, jy = player->jy;
11932 boolean field_under_player_is_free =
11933 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11934 boolean player_is_standing_on_valid_field =
11935 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11936 (IS_WALKABLE(Feld[jx][jy]) &&
11937 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11939 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11940 player->programmed_action = MV_DOWN;
11945 MovePlayerOneStep()
11946 -----------------------------------------------------------------------------
11947 dx, dy: direction (non-diagonal) to try to move the player to
11948 real_dx, real_dy: direction as read from input device (can be diagonal)
11951 boolean MovePlayerOneStep(struct PlayerInfo *player,
11952 int dx, int dy, int real_dx, int real_dy)
11954 int jx = player->jx, jy = player->jy;
11955 int new_jx = jx + dx, new_jy = jy + dy;
11956 #if !USE_FIXED_DONT_RUN_INTO
11960 boolean player_can_move = !player->cannot_move;
11962 if (!player->active || (!dx && !dy))
11963 return MP_NO_ACTION;
11965 player->MovDir = (dx < 0 ? MV_LEFT :
11966 dx > 0 ? MV_RIGHT :
11968 dy > 0 ? MV_DOWN : MV_NONE);
11970 if (!IN_LEV_FIELD(new_jx, new_jy))
11971 return MP_NO_ACTION;
11973 if (!player_can_move)
11975 if (player->MovPos == 0)
11977 player->is_moving = FALSE;
11978 player->is_digging = FALSE;
11979 player->is_collecting = FALSE;
11980 player->is_snapping = FALSE;
11981 player->is_pushing = FALSE;
11986 if (!options.network && game.centered_player_nr == -1 &&
11987 !AllPlayersInSight(player, new_jx, new_jy))
11988 return MP_NO_ACTION;
11990 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11991 return MP_NO_ACTION;
11994 #if !USE_FIXED_DONT_RUN_INTO
11995 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11997 /* (moved to DigField()) */
11998 if (player_can_move && DONT_RUN_INTO(element))
12000 if (element == EL_ACID && dx == 0 && dy == 1)
12002 SplashAcid(new_jx, new_jy);
12003 Feld[jx][jy] = EL_PLAYER_1;
12004 InitMovingField(jx, jy, MV_DOWN);
12005 Store[jx][jy] = EL_ACID;
12006 ContinueMoving(jx, jy);
12007 BuryPlayer(player);
12010 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12016 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12017 if (can_move != MP_MOVING)
12020 /* check if DigField() has caused relocation of the player */
12021 if (player->jx != jx || player->jy != jy)
12022 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12024 StorePlayer[jx][jy] = 0;
12025 player->last_jx = jx;
12026 player->last_jy = jy;
12027 player->jx = new_jx;
12028 player->jy = new_jy;
12029 StorePlayer[new_jx][new_jy] = player->element_nr;
12031 if (player->move_delay_value_next != -1)
12033 player->move_delay_value = player->move_delay_value_next;
12034 player->move_delay_value_next = -1;
12038 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12040 player->step_counter++;
12042 PlayerVisit[jx][jy] = FrameCounter;
12044 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12045 player->is_moving = TRUE;
12049 /* should better be called in MovePlayer(), but this breaks some tapes */
12050 ScrollPlayer(player, SCROLL_INIT);
12056 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12058 int jx = player->jx, jy = player->jy;
12059 int old_jx = jx, old_jy = jy;
12060 int moved = MP_NO_ACTION;
12062 if (!player->active)
12067 if (player->MovPos == 0)
12069 player->is_moving = FALSE;
12070 player->is_digging = FALSE;
12071 player->is_collecting = FALSE;
12072 player->is_snapping = FALSE;
12073 player->is_pushing = FALSE;
12079 if (player->move_delay > 0)
12082 player->move_delay = -1; /* set to "uninitialized" value */
12084 /* store if player is automatically moved to next field */
12085 player->is_auto_moving = (player->programmed_action != MV_NONE);
12087 /* remove the last programmed player action */
12088 player->programmed_action = 0;
12090 if (player->MovPos)
12092 /* should only happen if pre-1.2 tape recordings are played */
12093 /* this is only for backward compatibility */
12095 int original_move_delay_value = player->move_delay_value;
12098 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12102 /* scroll remaining steps with finest movement resolution */
12103 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12105 while (player->MovPos)
12107 ScrollPlayer(player, SCROLL_GO_ON);
12108 ScrollScreen(NULL, SCROLL_GO_ON);
12110 AdvanceFrameAndPlayerCounters(player->index_nr);
12116 player->move_delay_value = original_move_delay_value;
12119 player->is_active = FALSE;
12121 if (player->last_move_dir & MV_HORIZONTAL)
12123 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12124 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12128 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12129 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12132 #if USE_FIXED_BORDER_RUNNING_GFX
12133 if (!moved && !player->is_active)
12135 player->is_moving = FALSE;
12136 player->is_digging = FALSE;
12137 player->is_collecting = FALSE;
12138 player->is_snapping = FALSE;
12139 player->is_pushing = FALSE;
12147 if (moved & MP_MOVING && !ScreenMovPos &&
12148 (player->index_nr == game.centered_player_nr ||
12149 game.centered_player_nr == -1))
12151 if (moved & MP_MOVING && !ScreenMovPos &&
12152 (player == local_player || !options.network))
12155 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12156 int offset = game.scroll_delay_value;
12158 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12160 /* actual player has left the screen -- scroll in that direction */
12161 if (jx != old_jx) /* player has moved horizontally */
12162 scroll_x += (jx - old_jx);
12163 else /* player has moved vertically */
12164 scroll_y += (jy - old_jy);
12168 if (jx != old_jx) /* player has moved horizontally */
12170 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12171 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12172 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12174 /* don't scroll over playfield boundaries */
12175 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12176 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12178 /* don't scroll more than one field at a time */
12179 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12181 /* don't scroll against the player's moving direction */
12182 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12183 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12184 scroll_x = old_scroll_x;
12186 else /* player has moved vertically */
12188 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12189 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12190 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12192 /* don't scroll over playfield boundaries */
12193 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12194 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12196 /* don't scroll more than one field at a time */
12197 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12199 /* don't scroll against the player's moving direction */
12200 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12201 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12202 scroll_y = old_scroll_y;
12206 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12209 if (!options.network && game.centered_player_nr == -1 &&
12210 !AllPlayersInVisibleScreen())
12212 scroll_x = old_scroll_x;
12213 scroll_y = old_scroll_y;
12217 if (!options.network && !AllPlayersInVisibleScreen())
12219 scroll_x = old_scroll_x;
12220 scroll_y = old_scroll_y;
12225 ScrollScreen(player, SCROLL_INIT);
12226 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12231 player->StepFrame = 0;
12233 if (moved & MP_MOVING)
12235 if (old_jx != jx && old_jy == jy)
12236 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12237 else if (old_jx == jx && old_jy != jy)
12238 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12240 DrawLevelField(jx, jy); /* for "crumbled sand" */
12242 player->last_move_dir = player->MovDir;
12243 player->is_moving = TRUE;
12244 player->is_snapping = FALSE;
12245 player->is_switching = FALSE;
12246 player->is_dropping = FALSE;
12247 player->is_dropping_pressed = FALSE;
12248 player->drop_pressed_delay = 0;
12251 /* should better be called here than above, but this breaks some tapes */
12252 ScrollPlayer(player, SCROLL_INIT);
12257 CheckGravityMovementWhenNotMoving(player);
12259 player->is_moving = FALSE;
12261 /* at this point, the player is allowed to move, but cannot move right now
12262 (e.g. because of something blocking the way) -- ensure that the player
12263 is also allowed to move in the next frame (in old versions before 3.1.1,
12264 the player was forced to wait again for eight frames before next try) */
12266 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12267 player->move_delay = 0; /* allow direct movement in the next frame */
12270 if (player->move_delay == -1) /* not yet initialized by DigField() */
12271 player->move_delay = player->move_delay_value;
12273 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12275 TestIfPlayerTouchesBadThing(jx, jy);
12276 TestIfPlayerTouchesCustomElement(jx, jy);
12279 if (!player->active)
12280 RemovePlayer(player);
12285 void ScrollPlayer(struct PlayerInfo *player, int mode)
12287 int jx = player->jx, jy = player->jy;
12288 int last_jx = player->last_jx, last_jy = player->last_jy;
12289 int move_stepsize = TILEX / player->move_delay_value;
12291 #if USE_NEW_PLAYER_SPEED
12292 if (!player->active)
12295 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12298 if (!player->active || player->MovPos == 0)
12302 if (mode == SCROLL_INIT)
12304 player->actual_frame_counter = FrameCounter;
12305 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12307 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12308 Feld[last_jx][last_jy] == EL_EMPTY)
12310 int last_field_block_delay = 0; /* start with no blocking at all */
12311 int block_delay_adjustment = player->block_delay_adjustment;
12313 /* if player blocks last field, add delay for exactly one move */
12314 if (player->block_last_field)
12316 last_field_block_delay += player->move_delay_value;
12318 /* when blocking enabled, prevent moving up despite gravity */
12319 #if USE_PLAYER_GRAVITY
12320 if (player->gravity && player->MovDir == MV_UP)
12321 block_delay_adjustment = -1;
12323 if (game.gravity && player->MovDir == MV_UP)
12324 block_delay_adjustment = -1;
12328 /* add block delay adjustment (also possible when not blocking) */
12329 last_field_block_delay += block_delay_adjustment;
12331 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12332 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12335 #if USE_NEW_PLAYER_SPEED
12336 if (player->MovPos != 0) /* player has not yet reached destination */
12342 else if (!FrameReached(&player->actual_frame_counter, 1))
12345 #if USE_NEW_PLAYER_SPEED
12346 if (player->MovPos != 0)
12348 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12349 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12351 /* before DrawPlayer() to draw correct player graphic for this case */
12352 if (player->MovPos == 0)
12353 CheckGravityMovement(player);
12356 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12357 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12359 /* before DrawPlayer() to draw correct player graphic for this case */
12360 if (player->MovPos == 0)
12361 CheckGravityMovement(player);
12364 if (player->MovPos == 0) /* player reached destination field */
12366 if (player->move_delay_reset_counter > 0)
12368 player->move_delay_reset_counter--;
12370 if (player->move_delay_reset_counter == 0)
12372 /* continue with normal speed after quickly moving through gate */
12373 HALVE_PLAYER_SPEED(player);
12375 /* be able to make the next move without delay */
12376 player->move_delay = 0;
12380 player->last_jx = jx;
12381 player->last_jy = jy;
12383 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12384 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12385 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12386 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12387 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12388 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12390 DrawPlayer(player); /* needed here only to cleanup last field */
12391 RemovePlayer(player);
12393 if (local_player->friends_still_needed == 0 ||
12394 IS_SP_ELEMENT(Feld[jx][jy]))
12395 PlayerWins(player);
12398 /* this breaks one level: "machine", level 000 */
12400 int move_direction = player->MovDir;
12401 int enter_side = MV_DIR_OPPOSITE(move_direction);
12402 int leave_side = move_direction;
12403 int old_jx = last_jx;
12404 int old_jy = last_jy;
12405 int old_element = Feld[old_jx][old_jy];
12406 int new_element = Feld[jx][jy];
12408 if (IS_CUSTOM_ELEMENT(old_element))
12409 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12411 player->index_bit, leave_side);
12413 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12414 CE_PLAYER_LEAVES_X,
12415 player->index_bit, leave_side);
12417 if (IS_CUSTOM_ELEMENT(new_element))
12418 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12419 player->index_bit, enter_side);
12421 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12422 CE_PLAYER_ENTERS_X,
12423 player->index_bit, enter_side);
12425 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12426 CE_MOVE_OF_X, move_direction);
12429 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12431 TestIfPlayerTouchesBadThing(jx, jy);
12432 TestIfPlayerTouchesCustomElement(jx, jy);
12434 /* needed because pushed element has not yet reached its destination,
12435 so it would trigger a change event at its previous field location */
12436 if (!player->is_pushing)
12437 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12439 if (!player->active)
12440 RemovePlayer(player);
12443 if (!local_player->LevelSolved && level.use_step_counter)
12453 if (TimeLeft <= 10 && setup.time_limit)
12454 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12457 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12459 DisplayGameControlValues();
12461 DrawGameValue_Time(TimeLeft);
12464 if (!TimeLeft && setup.time_limit)
12465 for (i = 0; i < MAX_PLAYERS; i++)
12466 KillPlayer(&stored_player[i]);
12469 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12471 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12473 DisplayGameControlValues();
12476 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12477 DrawGameValue_Time(TimePlayed);
12481 if (tape.single_step && tape.recording && !tape.pausing &&
12482 !player->programmed_action)
12483 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12487 void ScrollScreen(struct PlayerInfo *player, int mode)
12489 static unsigned long screen_frame_counter = 0;
12491 if (mode == SCROLL_INIT)
12493 /* set scrolling step size according to actual player's moving speed */
12494 ScrollStepSize = TILEX / player->move_delay_value;
12496 screen_frame_counter = FrameCounter;
12497 ScreenMovDir = player->MovDir;
12498 ScreenMovPos = player->MovPos;
12499 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12502 else if (!FrameReached(&screen_frame_counter, 1))
12507 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12508 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12509 redraw_mask |= REDRAW_FIELD;
12512 ScreenMovDir = MV_NONE;
12515 void TestIfPlayerTouchesCustomElement(int x, int y)
12517 static int xy[4][2] =
12524 static int trigger_sides[4][2] =
12526 /* center side border side */
12527 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12528 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12529 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12530 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12532 static int touch_dir[4] =
12534 MV_LEFT | MV_RIGHT,
12539 int center_element = Feld[x][y]; /* should always be non-moving! */
12542 for (i = 0; i < NUM_DIRECTIONS; i++)
12544 int xx = x + xy[i][0];
12545 int yy = y + xy[i][1];
12546 int center_side = trigger_sides[i][0];
12547 int border_side = trigger_sides[i][1];
12548 int border_element;
12550 if (!IN_LEV_FIELD(xx, yy))
12553 if (IS_PLAYER(x, y))
12555 struct PlayerInfo *player = PLAYERINFO(x, y);
12557 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12558 border_element = Feld[xx][yy]; /* may be moving! */
12559 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12560 border_element = Feld[xx][yy];
12561 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12562 border_element = MovingOrBlocked2Element(xx, yy);
12564 continue; /* center and border element do not touch */
12566 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12567 player->index_bit, border_side);
12568 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12569 CE_PLAYER_TOUCHES_X,
12570 player->index_bit, border_side);
12572 else if (IS_PLAYER(xx, yy))
12574 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12576 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12578 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12579 continue; /* center and border element do not touch */
12582 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12583 player->index_bit, center_side);
12584 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12585 CE_PLAYER_TOUCHES_X,
12586 player->index_bit, center_side);
12592 #if USE_ELEMENT_TOUCHING_BUGFIX
12594 void TestIfElementTouchesCustomElement(int x, int y)
12596 static int xy[4][2] =
12603 static int trigger_sides[4][2] =
12605 /* center side border side */
12606 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12607 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12608 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12609 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12611 static int touch_dir[4] =
12613 MV_LEFT | MV_RIGHT,
12618 boolean change_center_element = FALSE;
12619 int center_element = Feld[x][y]; /* should always be non-moving! */
12620 int border_element_old[NUM_DIRECTIONS];
12623 for (i = 0; i < NUM_DIRECTIONS; i++)
12625 int xx = x + xy[i][0];
12626 int yy = y + xy[i][1];
12627 int border_element;
12629 border_element_old[i] = -1;
12631 if (!IN_LEV_FIELD(xx, yy))
12634 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12635 border_element = Feld[xx][yy]; /* may be moving! */
12636 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12637 border_element = Feld[xx][yy];
12638 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12639 border_element = MovingOrBlocked2Element(xx, yy);
12641 continue; /* center and border element do not touch */
12643 border_element_old[i] = border_element;
12646 for (i = 0; i < NUM_DIRECTIONS; i++)
12648 int xx = x + xy[i][0];
12649 int yy = y + xy[i][1];
12650 int center_side = trigger_sides[i][0];
12651 int border_element = border_element_old[i];
12653 if (border_element == -1)
12656 /* check for change of border element */
12657 CheckElementChangeBySide(xx, yy, border_element, center_element,
12658 CE_TOUCHING_X, center_side);
12661 for (i = 0; i < NUM_DIRECTIONS; i++)
12663 int border_side = trigger_sides[i][1];
12664 int border_element = border_element_old[i];
12666 if (border_element == -1)
12669 /* check for change of center element (but change it only once) */
12670 if (!change_center_element)
12671 change_center_element =
12672 CheckElementChangeBySide(x, y, center_element, border_element,
12673 CE_TOUCHING_X, border_side);
12679 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12681 static int xy[4][2] =
12688 static int trigger_sides[4][2] =
12690 /* center side border side */
12691 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12692 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12693 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12694 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12696 static int touch_dir[4] =
12698 MV_LEFT | MV_RIGHT,
12703 boolean change_center_element = FALSE;
12704 int center_element = Feld[x][y]; /* should always be non-moving! */
12707 for (i = 0; i < NUM_DIRECTIONS; i++)
12709 int xx = x + xy[i][0];
12710 int yy = y + xy[i][1];
12711 int center_side = trigger_sides[i][0];
12712 int border_side = trigger_sides[i][1];
12713 int border_element;
12715 if (!IN_LEV_FIELD(xx, yy))
12718 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12719 border_element = Feld[xx][yy]; /* may be moving! */
12720 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12721 border_element = Feld[xx][yy];
12722 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12723 border_element = MovingOrBlocked2Element(xx, yy);
12725 continue; /* center and border element do not touch */
12727 /* check for change of center element (but change it only once) */
12728 if (!change_center_element)
12729 change_center_element =
12730 CheckElementChangeBySide(x, y, center_element, border_element,
12731 CE_TOUCHING_X, border_side);
12733 /* check for change of border element */
12734 CheckElementChangeBySide(xx, yy, border_element, center_element,
12735 CE_TOUCHING_X, center_side);
12741 void TestIfElementHitsCustomElement(int x, int y, int direction)
12743 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12744 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12745 int hitx = x + dx, hity = y + dy;
12746 int hitting_element = Feld[x][y];
12747 int touched_element;
12749 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12752 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12753 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12755 if (IN_LEV_FIELD(hitx, hity))
12757 int opposite_direction = MV_DIR_OPPOSITE(direction);
12758 int hitting_side = direction;
12759 int touched_side = opposite_direction;
12760 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12761 MovDir[hitx][hity] != direction ||
12762 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12768 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12769 CE_HITTING_X, touched_side);
12771 CheckElementChangeBySide(hitx, hity, touched_element,
12772 hitting_element, CE_HIT_BY_X, hitting_side);
12774 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12775 CE_HIT_BY_SOMETHING, opposite_direction);
12779 /* "hitting something" is also true when hitting the playfield border */
12780 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12781 CE_HITTING_SOMETHING, direction);
12785 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12787 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12788 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12789 int hitx = x + dx, hity = y + dy;
12790 int hitting_element = Feld[x][y];
12791 int touched_element;
12793 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12794 !IS_FREE(hitx, hity) &&
12795 (!IS_MOVING(hitx, hity) ||
12796 MovDir[hitx][hity] != direction ||
12797 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12800 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12804 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12808 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12809 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12811 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12812 EP_CAN_SMASH_EVERYTHING, direction);
12814 if (IN_LEV_FIELD(hitx, hity))
12816 int opposite_direction = MV_DIR_OPPOSITE(direction);
12817 int hitting_side = direction;
12818 int touched_side = opposite_direction;
12820 int touched_element = MovingOrBlocked2Element(hitx, hity);
12823 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12824 MovDir[hitx][hity] != direction ||
12825 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12834 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12835 CE_SMASHED_BY_SOMETHING, opposite_direction);
12837 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12838 CE_OTHER_IS_SMASHING, touched_side);
12840 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12841 CE_OTHER_GETS_SMASHED, hitting_side);
12847 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12849 int i, kill_x = -1, kill_y = -1;
12851 int bad_element = -1;
12852 static int test_xy[4][2] =
12859 static int test_dir[4] =
12867 for (i = 0; i < NUM_DIRECTIONS; i++)
12869 int test_x, test_y, test_move_dir, test_element;
12871 test_x = good_x + test_xy[i][0];
12872 test_y = good_y + test_xy[i][1];
12874 if (!IN_LEV_FIELD(test_x, test_y))
12878 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12880 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12882 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12883 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12885 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12886 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12890 bad_element = test_element;
12896 if (kill_x != -1 || kill_y != -1)
12898 if (IS_PLAYER(good_x, good_y))
12900 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12902 if (player->shield_deadly_time_left > 0 &&
12903 !IS_INDESTRUCTIBLE(bad_element))
12904 Bang(kill_x, kill_y);
12905 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12906 KillPlayer(player);
12909 Bang(good_x, good_y);
12913 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12915 int i, kill_x = -1, kill_y = -1;
12916 int bad_element = Feld[bad_x][bad_y];
12917 static int test_xy[4][2] =
12924 static int touch_dir[4] =
12926 MV_LEFT | MV_RIGHT,
12931 static int test_dir[4] =
12939 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12942 for (i = 0; i < NUM_DIRECTIONS; i++)
12944 int test_x, test_y, test_move_dir, test_element;
12946 test_x = bad_x + test_xy[i][0];
12947 test_y = bad_y + test_xy[i][1];
12948 if (!IN_LEV_FIELD(test_x, test_y))
12952 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12954 test_element = Feld[test_x][test_y];
12956 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12957 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12959 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12960 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12962 /* good thing is player or penguin that does not move away */
12963 if (IS_PLAYER(test_x, test_y))
12965 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12967 if (bad_element == EL_ROBOT && player->is_moving)
12968 continue; /* robot does not kill player if he is moving */
12970 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12972 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12973 continue; /* center and border element do not touch */
12980 else if (test_element == EL_PENGUIN)
12989 if (kill_x != -1 || kill_y != -1)
12991 if (IS_PLAYER(kill_x, kill_y))
12993 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12995 if (player->shield_deadly_time_left > 0 &&
12996 !IS_INDESTRUCTIBLE(bad_element))
12997 Bang(bad_x, bad_y);
12998 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12999 KillPlayer(player);
13002 Bang(kill_x, kill_y);
13006 void TestIfPlayerTouchesBadThing(int x, int y)
13008 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13011 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13013 TestIfGoodThingHitsBadThing(x, y, move_dir);
13016 void TestIfBadThingTouchesPlayer(int x, int y)
13018 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13021 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13023 TestIfBadThingHitsGoodThing(x, y, move_dir);
13026 void TestIfFriendTouchesBadThing(int x, int y)
13028 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13031 void TestIfBadThingTouchesFriend(int x, int y)
13033 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13036 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13038 int i, kill_x = bad_x, kill_y = bad_y;
13039 static int xy[4][2] =
13047 for (i = 0; i < NUM_DIRECTIONS; i++)
13051 x = bad_x + xy[i][0];
13052 y = bad_y + xy[i][1];
13053 if (!IN_LEV_FIELD(x, y))
13056 element = Feld[x][y];
13057 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13058 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13066 if (kill_x != bad_x || kill_y != bad_y)
13067 Bang(bad_x, bad_y);
13070 void KillPlayer(struct PlayerInfo *player)
13072 int jx = player->jx, jy = player->jy;
13074 if (!player->active)
13077 /* the following code was introduced to prevent an infinite loop when calling
13079 -> CheckTriggeredElementChangeExt()
13080 -> ExecuteCustomElementAction()
13082 -> (infinitely repeating the above sequence of function calls)
13083 which occurs when killing the player while having a CE with the setting
13084 "kill player X when explosion of <player X>"; the solution using a new
13085 field "player->killed" was chosen for backwards compatibility, although
13086 clever use of the fields "player->active" etc. would probably also work */
13088 if (player->killed)
13092 player->killed = TRUE;
13094 /* remove accessible field at the player's position */
13095 Feld[jx][jy] = EL_EMPTY;
13097 /* deactivate shield (else Bang()/Explode() would not work right) */
13098 player->shield_normal_time_left = 0;
13099 player->shield_deadly_time_left = 0;
13102 BuryPlayer(player);
13105 static void KillPlayerUnlessEnemyProtected(int x, int y)
13107 if (!PLAYER_ENEMY_PROTECTED(x, y))
13108 KillPlayer(PLAYERINFO(x, y));
13111 static void KillPlayerUnlessExplosionProtected(int x, int y)
13113 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13114 KillPlayer(PLAYERINFO(x, y));
13117 void BuryPlayer(struct PlayerInfo *player)
13119 int jx = player->jx, jy = player->jy;
13121 if (!player->active)
13124 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13125 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13127 player->GameOver = TRUE;
13128 RemovePlayer(player);
13131 void RemovePlayer(struct PlayerInfo *player)
13133 int jx = player->jx, jy = player->jy;
13134 int i, found = FALSE;
13136 player->present = FALSE;
13137 player->active = FALSE;
13139 if (!ExplodeField[jx][jy])
13140 StorePlayer[jx][jy] = 0;
13142 if (player->is_moving)
13143 DrawLevelField(player->last_jx, player->last_jy);
13145 for (i = 0; i < MAX_PLAYERS; i++)
13146 if (stored_player[i].active)
13150 AllPlayersGone = TRUE;
13156 #if USE_NEW_SNAP_DELAY
13157 static void setFieldForSnapping(int x, int y, int element, int direction)
13159 struct ElementInfo *ei = &element_info[element];
13160 int direction_bit = MV_DIR_TO_BIT(direction);
13161 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13162 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13163 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13165 Feld[x][y] = EL_ELEMENT_SNAPPING;
13166 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13168 ResetGfxAnimation(x, y);
13170 GfxElement[x][y] = element;
13171 GfxAction[x][y] = action;
13172 GfxDir[x][y] = direction;
13173 GfxFrame[x][y] = -1;
13178 =============================================================================
13179 checkDiagonalPushing()
13180 -----------------------------------------------------------------------------
13181 check if diagonal input device direction results in pushing of object
13182 (by checking if the alternative direction is walkable, diggable, ...)
13183 =============================================================================
13186 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13187 int x, int y, int real_dx, int real_dy)
13189 int jx, jy, dx, dy, xx, yy;
13191 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13194 /* diagonal direction: check alternative direction */
13199 xx = jx + (dx == 0 ? real_dx : 0);
13200 yy = jy + (dy == 0 ? real_dy : 0);
13202 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13206 =============================================================================
13208 -----------------------------------------------------------------------------
13209 x, y: field next to player (non-diagonal) to try to dig to
13210 real_dx, real_dy: direction as read from input device (can be diagonal)
13211 =============================================================================
13214 int DigField(struct PlayerInfo *player,
13215 int oldx, int oldy, int x, int y,
13216 int real_dx, int real_dy, int mode)
13218 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13219 boolean player_was_pushing = player->is_pushing;
13220 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13221 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13222 int jx = oldx, jy = oldy;
13223 int dx = x - jx, dy = y - jy;
13224 int nextx = x + dx, nexty = y + dy;
13225 int move_direction = (dx == -1 ? MV_LEFT :
13226 dx == +1 ? MV_RIGHT :
13228 dy == +1 ? MV_DOWN : MV_NONE);
13229 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13230 int dig_side = MV_DIR_OPPOSITE(move_direction);
13231 int old_element = Feld[jx][jy];
13232 #if USE_FIXED_DONT_RUN_INTO
13233 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13239 if (is_player) /* function can also be called by EL_PENGUIN */
13241 if (player->MovPos == 0)
13243 player->is_digging = FALSE;
13244 player->is_collecting = FALSE;
13247 if (player->MovPos == 0) /* last pushing move finished */
13248 player->is_pushing = FALSE;
13250 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13252 player->is_switching = FALSE;
13253 player->push_delay = -1;
13255 return MP_NO_ACTION;
13259 #if !USE_FIXED_DONT_RUN_INTO
13260 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13261 return MP_NO_ACTION;
13264 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13265 old_element = Back[jx][jy];
13267 /* in case of element dropped at player position, check background */
13268 else if (Back[jx][jy] != EL_EMPTY &&
13269 game.engine_version >= VERSION_IDENT(2,2,0,0))
13270 old_element = Back[jx][jy];
13272 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13273 return MP_NO_ACTION; /* field has no opening in this direction */
13275 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13276 return MP_NO_ACTION; /* field has no opening in this direction */
13278 #if USE_FIXED_DONT_RUN_INTO
13279 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13283 Feld[jx][jy] = player->artwork_element;
13284 InitMovingField(jx, jy, MV_DOWN);
13285 Store[jx][jy] = EL_ACID;
13286 ContinueMoving(jx, jy);
13287 BuryPlayer(player);
13289 return MP_DONT_RUN_INTO;
13293 #if USE_FIXED_DONT_RUN_INTO
13294 if (player_can_move && DONT_RUN_INTO(element))
13296 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13298 return MP_DONT_RUN_INTO;
13302 #if USE_FIXED_DONT_RUN_INTO
13303 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13304 return MP_NO_ACTION;
13307 #if !USE_FIXED_DONT_RUN_INTO
13308 element = Feld[x][y];
13311 collect_count = element_info[element].collect_count_initial;
13313 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13314 return MP_NO_ACTION;
13316 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13317 player_can_move = player_can_move_or_snap;
13319 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13320 game.engine_version >= VERSION_IDENT(2,2,0,0))
13322 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13323 player->index_bit, dig_side);
13324 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13325 player->index_bit, dig_side);
13327 if (element == EL_DC_LANDMINE)
13330 if (Feld[x][y] != element) /* field changed by snapping */
13333 return MP_NO_ACTION;
13336 #if USE_PLAYER_GRAVITY
13337 if (player->gravity && is_player && !player->is_auto_moving &&
13338 canFallDown(player) && move_direction != MV_DOWN &&
13339 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13340 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13342 if (game.gravity && is_player && !player->is_auto_moving &&
13343 canFallDown(player) && move_direction != MV_DOWN &&
13344 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13345 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13348 if (player_can_move &&
13349 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13351 int sound_element = SND_ELEMENT(element);
13352 int sound_action = ACTION_WALKING;
13354 if (IS_RND_GATE(element))
13356 if (!player->key[RND_GATE_NR(element)])
13357 return MP_NO_ACTION;
13359 else if (IS_RND_GATE_GRAY(element))
13361 if (!player->key[RND_GATE_GRAY_NR(element)])
13362 return MP_NO_ACTION;
13364 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13366 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13367 return MP_NO_ACTION;
13369 else if (element == EL_EXIT_OPEN ||
13370 element == EL_EM_EXIT_OPEN ||
13371 element == EL_STEEL_EXIT_OPEN ||
13372 element == EL_EM_STEEL_EXIT_OPEN ||
13373 element == EL_SP_EXIT_OPEN ||
13374 element == EL_SP_EXIT_OPENING)
13376 sound_action = ACTION_PASSING; /* player is passing exit */
13378 else if (element == EL_EMPTY)
13380 sound_action = ACTION_MOVING; /* nothing to walk on */
13383 /* play sound from background or player, whatever is available */
13384 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13385 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13387 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13389 else if (player_can_move &&
13390 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13392 if (!ACCESS_FROM(element, opposite_direction))
13393 return MP_NO_ACTION; /* field not accessible from this direction */
13395 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13396 return MP_NO_ACTION;
13398 if (IS_EM_GATE(element))
13400 if (!player->key[EM_GATE_NR(element)])
13401 return MP_NO_ACTION;
13403 else if (IS_EM_GATE_GRAY(element))
13405 if (!player->key[EM_GATE_GRAY_NR(element)])
13406 return MP_NO_ACTION;
13408 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13410 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13411 return MP_NO_ACTION;
13413 else if (IS_EMC_GATE(element))
13415 if (!player->key[EMC_GATE_NR(element)])
13416 return MP_NO_ACTION;
13418 else if (IS_EMC_GATE_GRAY(element))
13420 if (!player->key[EMC_GATE_GRAY_NR(element)])
13421 return MP_NO_ACTION;
13423 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13425 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13426 return MP_NO_ACTION;
13428 else if (element == EL_DC_GATE_WHITE ||
13429 element == EL_DC_GATE_WHITE_GRAY ||
13430 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13432 if (player->num_white_keys == 0)
13433 return MP_NO_ACTION;
13435 player->num_white_keys--;
13437 else if (IS_SP_PORT(element))
13439 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13440 element == EL_SP_GRAVITY_PORT_RIGHT ||
13441 element == EL_SP_GRAVITY_PORT_UP ||
13442 element == EL_SP_GRAVITY_PORT_DOWN)
13443 #if USE_PLAYER_GRAVITY
13444 player->gravity = !player->gravity;
13446 game.gravity = !game.gravity;
13448 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13449 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13450 element == EL_SP_GRAVITY_ON_PORT_UP ||
13451 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13452 #if USE_PLAYER_GRAVITY
13453 player->gravity = TRUE;
13455 game.gravity = TRUE;
13457 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13458 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13459 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13460 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13461 #if USE_PLAYER_GRAVITY
13462 player->gravity = FALSE;
13464 game.gravity = FALSE;
13468 /* automatically move to the next field with double speed */
13469 player->programmed_action = move_direction;
13471 if (player->move_delay_reset_counter == 0)
13473 player->move_delay_reset_counter = 2; /* two double speed steps */
13475 DOUBLE_PLAYER_SPEED(player);
13478 PlayLevelSoundAction(x, y, ACTION_PASSING);
13480 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13484 if (mode != DF_SNAP)
13486 GfxElement[x][y] = GFX_ELEMENT(element);
13487 player->is_digging = TRUE;
13490 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13492 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13493 player->index_bit, dig_side);
13495 if (mode == DF_SNAP)
13497 #if USE_NEW_SNAP_DELAY
13498 if (level.block_snap_field)
13499 setFieldForSnapping(x, y, element, move_direction);
13501 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13503 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13506 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13507 player->index_bit, dig_side);
13510 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13514 if (is_player && mode != DF_SNAP)
13516 GfxElement[x][y] = element;
13517 player->is_collecting = TRUE;
13520 if (element == EL_SPEED_PILL)
13522 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13524 else if (element == EL_EXTRA_TIME && level.time > 0)
13526 TimeLeft += level.extra_time;
13529 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13531 DisplayGameControlValues();
13533 DrawGameValue_Time(TimeLeft);
13536 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13538 player->shield_normal_time_left += level.shield_normal_time;
13539 if (element == EL_SHIELD_DEADLY)
13540 player->shield_deadly_time_left += level.shield_deadly_time;
13542 else if (element == EL_DYNAMITE ||
13543 element == EL_EM_DYNAMITE ||
13544 element == EL_SP_DISK_RED)
13546 if (player->inventory_size < MAX_INVENTORY_SIZE)
13547 player->inventory_element[player->inventory_size++] = element;
13549 DrawGameDoorValues();
13551 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13553 player->dynabomb_count++;
13554 player->dynabombs_left++;
13556 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13558 player->dynabomb_size++;
13560 else if (element == EL_DYNABOMB_INCREASE_POWER)
13562 player->dynabomb_xl = TRUE;
13564 else if (IS_KEY(element))
13566 player->key[KEY_NR(element)] = TRUE;
13568 DrawGameDoorValues();
13570 else if (element == EL_DC_KEY_WHITE)
13572 player->num_white_keys++;
13574 /* display white keys? */
13575 /* DrawGameDoorValues(); */
13577 else if (IS_ENVELOPE(element))
13579 player->show_envelope = element;
13581 else if (element == EL_EMC_LENSES)
13583 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13585 RedrawAllInvisibleElementsForLenses();
13587 else if (element == EL_EMC_MAGNIFIER)
13589 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13591 RedrawAllInvisibleElementsForMagnifier();
13593 else if (IS_DROPPABLE(element) ||
13594 IS_THROWABLE(element)) /* can be collected and dropped */
13598 if (collect_count == 0)
13599 player->inventory_infinite_element = element;
13601 for (i = 0; i < collect_count; i++)
13602 if (player->inventory_size < MAX_INVENTORY_SIZE)
13603 player->inventory_element[player->inventory_size++] = element;
13605 DrawGameDoorValues();
13607 else if (collect_count > 0)
13609 local_player->gems_still_needed -= collect_count;
13610 if (local_player->gems_still_needed < 0)
13611 local_player->gems_still_needed = 0;
13614 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13616 DisplayGameControlValues();
13618 DrawGameValue_Emeralds(local_player->gems_still_needed);
13622 RaiseScoreElement(element);
13623 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13626 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13627 player->index_bit, dig_side);
13629 if (mode == DF_SNAP)
13631 #if USE_NEW_SNAP_DELAY
13632 if (level.block_snap_field)
13633 setFieldForSnapping(x, y, element, move_direction);
13635 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13637 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13640 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13641 player->index_bit, dig_side);
13644 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13646 if (mode == DF_SNAP && element != EL_BD_ROCK)
13647 return MP_NO_ACTION;
13649 if (CAN_FALL(element) && dy)
13650 return MP_NO_ACTION;
13652 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13653 !(element == EL_SPRING && level.use_spring_bug))
13654 return MP_NO_ACTION;
13656 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13657 ((move_direction & MV_VERTICAL &&
13658 ((element_info[element].move_pattern & MV_LEFT &&
13659 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13660 (element_info[element].move_pattern & MV_RIGHT &&
13661 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13662 (move_direction & MV_HORIZONTAL &&
13663 ((element_info[element].move_pattern & MV_UP &&
13664 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13665 (element_info[element].move_pattern & MV_DOWN &&
13666 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13667 return MP_NO_ACTION;
13669 /* do not push elements already moving away faster than player */
13670 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13671 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13672 return MP_NO_ACTION;
13674 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13676 if (player->push_delay_value == -1 || !player_was_pushing)
13677 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13679 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13681 if (player->push_delay_value == -1)
13682 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13684 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13686 if (!player->is_pushing)
13687 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13690 player->is_pushing = TRUE;
13691 player->is_active = TRUE;
13693 if (!(IN_LEV_FIELD(nextx, nexty) &&
13694 (IS_FREE(nextx, nexty) ||
13695 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13696 IS_SB_ELEMENT(element)))))
13697 return MP_NO_ACTION;
13699 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13700 return MP_NO_ACTION;
13702 if (player->push_delay == -1) /* new pushing; restart delay */
13703 player->push_delay = 0;
13705 if (player->push_delay < player->push_delay_value &&
13706 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13707 element != EL_SPRING && element != EL_BALLOON)
13709 /* make sure that there is no move delay before next try to push */
13710 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13711 player->move_delay = 0;
13713 return MP_NO_ACTION;
13716 if (IS_SB_ELEMENT(element))
13718 if (element == EL_SOKOBAN_FIELD_FULL)
13720 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13721 local_player->sokobanfields_still_needed++;
13724 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13726 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13727 local_player->sokobanfields_still_needed--;
13730 Feld[x][y] = EL_SOKOBAN_OBJECT;
13732 if (Back[x][y] == Back[nextx][nexty])
13733 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13734 else if (Back[x][y] != 0)
13735 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13738 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13741 if (local_player->sokobanfields_still_needed == 0 &&
13742 game.emulation == EMU_SOKOBAN)
13744 PlayerWins(player);
13746 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13750 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13752 InitMovingField(x, y, move_direction);
13753 GfxAction[x][y] = ACTION_PUSHING;
13755 if (mode == DF_SNAP)
13756 ContinueMoving(x, y);
13758 MovPos[x][y] = (dx != 0 ? dx : dy);
13760 Pushed[x][y] = TRUE;
13761 Pushed[nextx][nexty] = TRUE;
13763 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13764 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13766 player->push_delay_value = -1; /* get new value later */
13768 /* check for element change _after_ element has been pushed */
13769 if (game.use_change_when_pushing_bug)
13771 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13772 player->index_bit, dig_side);
13773 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13774 player->index_bit, dig_side);
13777 else if (IS_SWITCHABLE(element))
13779 if (PLAYER_SWITCHING(player, x, y))
13781 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13782 player->index_bit, dig_side);
13787 player->is_switching = TRUE;
13788 player->switch_x = x;
13789 player->switch_y = y;
13791 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13793 if (element == EL_ROBOT_WHEEL)
13795 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13799 DrawLevelField(x, y);
13801 else if (element == EL_SP_TERMINAL)
13805 SCAN_PLAYFIELD(xx, yy)
13807 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13809 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13810 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13813 else if (IS_BELT_SWITCH(element))
13815 ToggleBeltSwitch(x, y);
13817 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13818 element == EL_SWITCHGATE_SWITCH_DOWN ||
13819 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13820 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13822 ToggleSwitchgateSwitch(x, y);
13824 else if (element == EL_LIGHT_SWITCH ||
13825 element == EL_LIGHT_SWITCH_ACTIVE)
13827 ToggleLightSwitch(x, y);
13829 else if (element == EL_TIMEGATE_SWITCH ||
13830 element == EL_DC_TIMEGATE_SWITCH)
13832 ActivateTimegateSwitch(x, y);
13834 else if (element == EL_BALLOON_SWITCH_LEFT ||
13835 element == EL_BALLOON_SWITCH_RIGHT ||
13836 element == EL_BALLOON_SWITCH_UP ||
13837 element == EL_BALLOON_SWITCH_DOWN ||
13838 element == EL_BALLOON_SWITCH_NONE ||
13839 element == EL_BALLOON_SWITCH_ANY)
13841 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13842 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13843 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13844 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13845 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13848 else if (element == EL_LAMP)
13850 Feld[x][y] = EL_LAMP_ACTIVE;
13851 local_player->lights_still_needed--;
13853 ResetGfxAnimation(x, y);
13854 DrawLevelField(x, y);
13856 else if (element == EL_TIME_ORB_FULL)
13858 Feld[x][y] = EL_TIME_ORB_EMPTY;
13860 if (level.time > 0 || level.use_time_orb_bug)
13862 TimeLeft += level.time_orb_time;
13865 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13867 DisplayGameControlValues();
13869 DrawGameValue_Time(TimeLeft);
13873 ResetGfxAnimation(x, y);
13874 DrawLevelField(x, y);
13876 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13877 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13881 game.ball_state = !game.ball_state;
13883 SCAN_PLAYFIELD(xx, yy)
13885 int e = Feld[xx][yy];
13887 if (game.ball_state)
13889 if (e == EL_EMC_MAGIC_BALL)
13890 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13891 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13892 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13896 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13897 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13898 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13899 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13904 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13905 player->index_bit, dig_side);
13907 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13908 player->index_bit, dig_side);
13910 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13911 player->index_bit, dig_side);
13917 if (!PLAYER_SWITCHING(player, x, y))
13919 player->is_switching = TRUE;
13920 player->switch_x = x;
13921 player->switch_y = y;
13923 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13924 player->index_bit, dig_side);
13925 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13926 player->index_bit, dig_side);
13928 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13929 player->index_bit, dig_side);
13930 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13931 player->index_bit, dig_side);
13934 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13935 player->index_bit, dig_side);
13936 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13937 player->index_bit, dig_side);
13939 return MP_NO_ACTION;
13942 player->push_delay = -1;
13944 if (is_player) /* function can also be called by EL_PENGUIN */
13946 if (Feld[x][y] != element) /* really digged/collected something */
13948 player->is_collecting = !player->is_digging;
13949 player->is_active = TRUE;
13956 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13958 int jx = player->jx, jy = player->jy;
13959 int x = jx + dx, y = jy + dy;
13960 int snap_direction = (dx == -1 ? MV_LEFT :
13961 dx == +1 ? MV_RIGHT :
13963 dy == +1 ? MV_DOWN : MV_NONE);
13964 boolean can_continue_snapping = (level.continuous_snapping &&
13965 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13967 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13970 if (!player->active || !IN_LEV_FIELD(x, y))
13978 if (player->MovPos == 0)
13979 player->is_pushing = FALSE;
13981 player->is_snapping = FALSE;
13983 if (player->MovPos == 0)
13985 player->is_moving = FALSE;
13986 player->is_digging = FALSE;
13987 player->is_collecting = FALSE;
13993 #if USE_NEW_CONTINUOUS_SNAPPING
13994 /* prevent snapping with already pressed snap key when not allowed */
13995 if (player->is_snapping && !can_continue_snapping)
13998 if (player->is_snapping)
14002 player->MovDir = snap_direction;
14004 if (player->MovPos == 0)
14006 player->is_moving = FALSE;
14007 player->is_digging = FALSE;
14008 player->is_collecting = FALSE;
14011 player->is_dropping = FALSE;
14012 player->is_dropping_pressed = FALSE;
14013 player->drop_pressed_delay = 0;
14015 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14018 player->is_snapping = TRUE;
14019 player->is_active = TRUE;
14021 if (player->MovPos == 0)
14023 player->is_moving = FALSE;
14024 player->is_digging = FALSE;
14025 player->is_collecting = FALSE;
14028 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14029 DrawLevelField(player->last_jx, player->last_jy);
14031 DrawLevelField(x, y);
14036 boolean DropElement(struct PlayerInfo *player)
14038 int old_element, new_element;
14039 int dropx = player->jx, dropy = player->jy;
14040 int drop_direction = player->MovDir;
14041 int drop_side = drop_direction;
14042 int drop_element = (player->inventory_size > 0 ?
14043 player->inventory_element[player->inventory_size - 1] :
14044 player->inventory_infinite_element != EL_UNDEFINED ?
14045 player->inventory_infinite_element :
14046 player->dynabombs_left > 0 ?
14047 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14050 player->is_dropping_pressed = TRUE;
14052 /* do not drop an element on top of another element; when holding drop key
14053 pressed without moving, dropped element must move away before the next
14054 element can be dropped (this is especially important if the next element
14055 is dynamite, which can be placed on background for historical reasons) */
14056 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14059 if (IS_THROWABLE(drop_element))
14061 dropx += GET_DX_FROM_DIR(drop_direction);
14062 dropy += GET_DY_FROM_DIR(drop_direction);
14064 if (!IN_LEV_FIELD(dropx, dropy))
14068 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14069 new_element = drop_element; /* default: no change when dropping */
14071 /* check if player is active, not moving and ready to drop */
14072 if (!player->active || player->MovPos || player->drop_delay > 0)
14075 /* check if player has anything that can be dropped */
14076 if (new_element == EL_UNDEFINED)
14079 /* check if drop key was pressed long enough for EM style dynamite */
14080 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14083 /* check if anything can be dropped at the current position */
14084 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14087 /* collected custom elements can only be dropped on empty fields */
14088 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14091 if (old_element != EL_EMPTY)
14092 Back[dropx][dropy] = old_element; /* store old element on this field */
14094 ResetGfxAnimation(dropx, dropy);
14095 ResetRandomAnimationValue(dropx, dropy);
14097 if (player->inventory_size > 0 ||
14098 player->inventory_infinite_element != EL_UNDEFINED)
14100 if (player->inventory_size > 0)
14102 player->inventory_size--;
14104 DrawGameDoorValues();
14106 if (new_element == EL_DYNAMITE)
14107 new_element = EL_DYNAMITE_ACTIVE;
14108 else if (new_element == EL_EM_DYNAMITE)
14109 new_element = EL_EM_DYNAMITE_ACTIVE;
14110 else if (new_element == EL_SP_DISK_RED)
14111 new_element = EL_SP_DISK_RED_ACTIVE;
14114 Feld[dropx][dropy] = new_element;
14116 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14117 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14118 el2img(Feld[dropx][dropy]), 0);
14120 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14122 /* needed if previous element just changed to "empty" in the last frame */
14123 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14125 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14126 player->index_bit, drop_side);
14127 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14129 player->index_bit, drop_side);
14131 TestIfElementTouchesCustomElement(dropx, dropy);
14133 else /* player is dropping a dyna bomb */
14135 player->dynabombs_left--;
14137 Feld[dropx][dropy] = new_element;
14139 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14140 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14141 el2img(Feld[dropx][dropy]), 0);
14143 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14146 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14147 InitField_WithBug1(dropx, dropy, FALSE);
14149 new_element = Feld[dropx][dropy]; /* element might have changed */
14151 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14152 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14154 int move_direction, nextx, nexty;
14156 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14157 MovDir[dropx][dropy] = drop_direction;
14159 move_direction = MovDir[dropx][dropy];
14160 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14161 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14163 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14165 #if USE_FIX_IMPACT_COLLISION
14166 /* do not cause impact style collision by dropping elements that can fall */
14167 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14169 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14173 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14174 player->is_dropping = TRUE;
14176 player->drop_pressed_delay = 0;
14177 player->is_dropping_pressed = FALSE;
14179 player->drop_x = dropx;
14180 player->drop_y = dropy;
14185 /* ------------------------------------------------------------------------- */
14186 /* game sound playing functions */
14187 /* ------------------------------------------------------------------------- */
14189 static int *loop_sound_frame = NULL;
14190 static int *loop_sound_volume = NULL;
14192 void InitPlayLevelSound()
14194 int num_sounds = getSoundListSize();
14196 checked_free(loop_sound_frame);
14197 checked_free(loop_sound_volume);
14199 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14200 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14203 static void PlayLevelSound(int x, int y, int nr)
14205 int sx = SCREENX(x), sy = SCREENY(y);
14206 int volume, stereo_position;
14207 int max_distance = 8;
14208 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14210 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14211 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14214 if (!IN_LEV_FIELD(x, y) ||
14215 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14216 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14219 volume = SOUND_MAX_VOLUME;
14221 if (!IN_SCR_FIELD(sx, sy))
14223 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14224 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14226 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14229 stereo_position = (SOUND_MAX_LEFT +
14230 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14231 (SCR_FIELDX + 2 * max_distance));
14233 if (IS_LOOP_SOUND(nr))
14235 /* This assures that quieter loop sounds do not overwrite louder ones,
14236 while restarting sound volume comparison with each new game frame. */
14238 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14241 loop_sound_volume[nr] = volume;
14242 loop_sound_frame[nr] = FrameCounter;
14245 PlaySoundExt(nr, volume, stereo_position, type);
14248 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14250 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14251 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14252 y < LEVELY(BY1) ? LEVELY(BY1) :
14253 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14257 static void PlayLevelSoundAction(int x, int y, int action)
14259 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14262 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14264 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14266 if (sound_effect != SND_UNDEFINED)
14267 PlayLevelSound(x, y, sound_effect);
14270 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14273 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14275 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14276 PlayLevelSound(x, y, sound_effect);
14279 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14281 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14283 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14284 PlayLevelSound(x, y, sound_effect);
14287 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14289 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14291 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14292 StopSound(sound_effect);
14295 static void PlayLevelMusic()
14297 if (levelset.music[level_nr] != MUS_UNDEFINED)
14298 PlayMusic(levelset.music[level_nr]); /* from config file */
14300 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14303 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14305 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14306 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14307 int x = xx - 1 - offset;
14308 int y = yy - 1 - offset;
14313 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14317 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14321 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14325 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14329 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14333 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14337 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14340 case SAMPLE_android_clone:
14341 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14344 case SAMPLE_android_move:
14345 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14348 case SAMPLE_spring:
14349 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14353 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14357 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14360 case SAMPLE_eater_eat:
14361 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14365 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14368 case SAMPLE_collect:
14369 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14372 case SAMPLE_diamond:
14373 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14376 case SAMPLE_squash:
14377 /* !!! CHECK THIS !!! */
14379 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14381 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14385 case SAMPLE_wonderfall:
14386 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14390 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14394 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14398 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14402 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14406 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14410 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14413 case SAMPLE_wonder:
14414 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14418 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14421 case SAMPLE_exit_open:
14422 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14425 case SAMPLE_exit_leave:
14426 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14429 case SAMPLE_dynamite:
14430 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14434 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14438 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14442 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14446 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14450 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14454 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14458 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14464 void ChangeTime(int value)
14466 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14470 /* EMC game engine uses value from time counter of RND game engine */
14471 level.native_em_level->lev->time = *time;
14473 DrawGameValue_Time(*time);
14476 void RaiseScore(int value)
14478 /* EMC game engine and RND game engine have separate score counters */
14479 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14480 &level.native_em_level->lev->score : &local_player->score);
14484 DrawGameValue_Score(*score);
14488 void RaiseScore(int value)
14490 local_player->score += value;
14493 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14495 DisplayGameControlValues();
14497 DrawGameValue_Score(local_player->score);
14501 void RaiseScoreElement(int element)
14506 case EL_BD_DIAMOND:
14507 case EL_EMERALD_YELLOW:
14508 case EL_EMERALD_RED:
14509 case EL_EMERALD_PURPLE:
14510 case EL_SP_INFOTRON:
14511 RaiseScore(level.score[SC_EMERALD]);
14514 RaiseScore(level.score[SC_DIAMOND]);
14517 RaiseScore(level.score[SC_CRYSTAL]);
14520 RaiseScore(level.score[SC_PEARL]);
14523 case EL_BD_BUTTERFLY:
14524 case EL_SP_ELECTRON:
14525 RaiseScore(level.score[SC_BUG]);
14528 case EL_BD_FIREFLY:
14529 case EL_SP_SNIKSNAK:
14530 RaiseScore(level.score[SC_SPACESHIP]);
14533 case EL_DARK_YAMYAM:
14534 RaiseScore(level.score[SC_YAMYAM]);
14537 RaiseScore(level.score[SC_ROBOT]);
14540 RaiseScore(level.score[SC_PACMAN]);
14543 RaiseScore(level.score[SC_NUT]);
14546 case EL_EM_DYNAMITE:
14547 case EL_SP_DISK_RED:
14548 case EL_DYNABOMB_INCREASE_NUMBER:
14549 case EL_DYNABOMB_INCREASE_SIZE:
14550 case EL_DYNABOMB_INCREASE_POWER:
14551 RaiseScore(level.score[SC_DYNAMITE]);
14553 case EL_SHIELD_NORMAL:
14554 case EL_SHIELD_DEADLY:
14555 RaiseScore(level.score[SC_SHIELD]);
14557 case EL_EXTRA_TIME:
14558 RaiseScore(level.extra_time_score);
14572 case EL_DC_KEY_WHITE:
14573 RaiseScore(level.score[SC_KEY]);
14576 RaiseScore(element_info[element].collect_score);
14581 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14583 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14585 #if defined(NETWORK_AVALIABLE)
14586 if (options.network)
14587 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14596 FadeSkipNextFadeIn();
14598 fading = fading_none;
14602 OpenDoor(DOOR_CLOSE_1);
14605 game_status = GAME_MODE_MAIN;
14608 DrawAndFadeInMainMenu(REDRAW_FIELD);
14616 FadeOut(REDRAW_FIELD);
14619 game_status = GAME_MODE_MAIN;
14621 DrawAndFadeInMainMenu(REDRAW_FIELD);
14625 else /* continue playing the game */
14627 if (tape.playing && tape.deactivate_display)
14628 TapeDeactivateDisplayOff(TRUE);
14630 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14632 if (tape.playing && tape.deactivate_display)
14633 TapeDeactivateDisplayOn();
14637 void RequestQuitGame(boolean ask_if_really_quit)
14639 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14640 boolean skip_request = AllPlayersGone || quick_quit;
14642 RequestQuitGameExt(skip_request, quick_quit,
14643 "Do you really want to quit the game ?");
14647 /* ------------------------------------------------------------------------- */
14648 /* random generator functions */
14649 /* ------------------------------------------------------------------------- */
14651 unsigned int InitEngineRandom_RND(long seed)
14653 game.num_random_calls = 0;
14656 unsigned int rnd_seed = InitEngineRandom(seed);
14658 printf("::: START RND: %d\n", rnd_seed);
14663 return InitEngineRandom(seed);
14669 unsigned int RND(int max)
14673 game.num_random_calls++;
14675 return GetEngineRandom(max);
14682 /* ------------------------------------------------------------------------- */
14683 /* game engine snapshot handling functions */
14684 /* ------------------------------------------------------------------------- */
14686 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14688 struct EngineSnapshotInfo
14690 /* runtime values for custom element collect score */
14691 int collect_score[NUM_CUSTOM_ELEMENTS];
14693 /* runtime values for group element choice position */
14694 int choice_pos[NUM_GROUP_ELEMENTS];
14696 /* runtime values for belt position animations */
14697 int belt_graphic[4 * NUM_BELT_PARTS];
14698 int belt_anim_mode[4 * NUM_BELT_PARTS];
14701 struct EngineSnapshotNodeInfo
14708 static struct EngineSnapshotInfo engine_snapshot_rnd;
14709 static ListNode *engine_snapshot_list = NULL;
14710 static char *snapshot_level_identifier = NULL;
14711 static int snapshot_level_nr = -1;
14713 void FreeEngineSnapshot()
14715 while (engine_snapshot_list != NULL)
14716 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14719 setString(&snapshot_level_identifier, NULL);
14720 snapshot_level_nr = -1;
14723 static void SaveEngineSnapshotValues_RND()
14725 static int belt_base_active_element[4] =
14727 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14728 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14729 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14730 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14734 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14736 int element = EL_CUSTOM_START + i;
14738 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14741 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14743 int element = EL_GROUP_START + i;
14745 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14748 for (i = 0; i < 4; i++)
14750 for (j = 0; j < NUM_BELT_PARTS; j++)
14752 int element = belt_base_active_element[i] + j;
14753 int graphic = el2img(element);
14754 int anim_mode = graphic_info[graphic].anim_mode;
14756 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14757 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14762 static void LoadEngineSnapshotValues_RND()
14764 unsigned long num_random_calls = game.num_random_calls;
14767 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14769 int element = EL_CUSTOM_START + i;
14771 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14774 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14776 int element = EL_GROUP_START + i;
14778 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14781 for (i = 0; i < 4; i++)
14783 for (j = 0; j < NUM_BELT_PARTS; j++)
14785 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14786 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14788 graphic_info[graphic].anim_mode = anim_mode;
14792 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14794 InitRND(tape.random_seed);
14795 for (i = 0; i < num_random_calls; i++)
14799 if (game.num_random_calls != num_random_calls)
14801 Error(ERR_INFO, "number of random calls out of sync");
14802 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14803 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14804 Error(ERR_EXIT, "this should not happen -- please debug");
14808 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14810 struct EngineSnapshotNodeInfo *bi =
14811 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14813 bi->buffer_orig = buffer;
14814 bi->buffer_copy = checked_malloc(size);
14817 memcpy(bi->buffer_copy, buffer, size);
14819 addNodeToList(&engine_snapshot_list, NULL, bi);
14822 void SaveEngineSnapshot()
14824 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14826 if (level_editor_test_game) /* do not save snapshots from editor */
14829 /* copy some special values to a structure better suited for the snapshot */
14831 SaveEngineSnapshotValues_RND();
14832 SaveEngineSnapshotValues_EM();
14834 /* save values stored in special snapshot structure */
14836 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14837 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14839 /* save further RND engine values */
14841 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14842 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14843 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14845 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14846 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14847 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14848 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14850 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14851 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14852 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14853 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14854 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14856 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14857 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14858 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14860 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14862 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14864 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14865 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14867 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14868 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14869 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14870 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14871 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14872 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14873 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14874 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14875 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14876 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14877 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14878 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14879 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14880 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14881 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14882 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14883 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14884 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14886 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14887 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14889 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14890 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14891 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14893 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14894 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14896 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14897 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14898 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14899 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14900 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14902 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14903 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14905 /* save level identification information */
14907 setString(&snapshot_level_identifier, leveldir_current->identifier);
14908 snapshot_level_nr = level_nr;
14911 ListNode *node = engine_snapshot_list;
14914 while (node != NULL)
14916 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14921 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14925 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14927 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14930 void LoadEngineSnapshot()
14932 ListNode *node = engine_snapshot_list;
14934 if (engine_snapshot_list == NULL)
14937 while (node != NULL)
14939 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14944 /* restore special values from snapshot structure */
14946 LoadEngineSnapshotValues_RND();
14947 LoadEngineSnapshotValues_EM();
14950 boolean CheckEngineSnapshot()
14952 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14953 snapshot_level_nr == level_nr);
14957 /* ---------- new game button stuff ---------------------------------------- */
14959 /* graphic position values for game buttons */
14960 #define GAME_BUTTON_XSIZE 30
14961 #define GAME_BUTTON_YSIZE 30
14962 #define GAME_BUTTON_XPOS 5
14963 #define GAME_BUTTON_YPOS 215
14964 #define SOUND_BUTTON_XPOS 5
14965 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14967 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14968 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14969 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14970 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14971 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14972 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14980 } gamebutton_info[NUM_GAME_BUTTONS] =
14984 &game.button.stop.x, &game.button.stop.y,
14985 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14990 &game.button.pause.x, &game.button.pause.y,
14991 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14992 GAME_CTRL_ID_PAUSE,
14996 &game.button.play.x, &game.button.play.y,
14997 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15002 &game.button.sound_music.x, &game.button.sound_music.y,
15003 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15004 SOUND_CTRL_ID_MUSIC,
15005 "background music on/off"
15008 &game.button.sound_loops.x, &game.button.sound_loops.y,
15009 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15010 SOUND_CTRL_ID_LOOPS,
15011 "sound loops on/off"
15014 &game.button.sound_simple.x,&game.button.sound_simple.y,
15015 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15016 SOUND_CTRL_ID_SIMPLE,
15017 "normal sounds on/off"
15021 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15026 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15027 GAME_CTRL_ID_PAUSE,
15031 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15036 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15037 SOUND_CTRL_ID_MUSIC,
15038 "background music on/off"
15041 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15042 SOUND_CTRL_ID_LOOPS,
15043 "sound loops on/off"
15046 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15047 SOUND_CTRL_ID_SIMPLE,
15048 "normal sounds on/off"
15053 void CreateGameButtons()
15057 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15059 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15060 struct GadgetInfo *gi;
15063 unsigned long event_mask;
15065 int gd_xoffset, gd_yoffset;
15066 int gd_x1, gd_x2, gd_y1, gd_y2;
15069 x = DX + *gamebutton_info[i].x;
15070 y = DY + *gamebutton_info[i].y;
15071 gd_xoffset = gamebutton_info[i].gd_x;
15072 gd_yoffset = gamebutton_info[i].gd_y;
15073 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15074 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15076 if (id == GAME_CTRL_ID_STOP ||
15077 id == GAME_CTRL_ID_PAUSE ||
15078 id == GAME_CTRL_ID_PLAY)
15080 button_type = GD_TYPE_NORMAL_BUTTON;
15082 event_mask = GD_EVENT_RELEASED;
15083 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15084 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15088 button_type = GD_TYPE_CHECK_BUTTON;
15090 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15091 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15092 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15093 event_mask = GD_EVENT_PRESSED;
15094 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15095 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15098 gi = CreateGadget(GDI_CUSTOM_ID, id,
15099 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15104 GDI_X, DX + gd_xoffset,
15105 GDI_Y, DY + gd_yoffset,
15107 GDI_WIDTH, GAME_BUTTON_XSIZE,
15108 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15109 GDI_TYPE, button_type,
15110 GDI_STATE, GD_BUTTON_UNPRESSED,
15111 GDI_CHECKED, checked,
15112 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15113 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15114 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15115 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15116 GDI_EVENT_MASK, event_mask,
15117 GDI_CALLBACK_ACTION, HandleGameButtons,
15121 Error(ERR_EXIT, "cannot create gadget");
15123 game_gadget[id] = gi;
15127 void FreeGameButtons()
15131 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15132 FreeGadget(game_gadget[i]);
15135 static void MapGameButtons()
15139 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15140 MapGadget(game_gadget[i]);
15143 void UnmapGameButtons()
15147 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15148 UnmapGadget(game_gadget[i]);
15151 static void HandleGameButtons(struct GadgetInfo *gi)
15153 int id = gi->custom_id;
15155 if (game_status != GAME_MODE_PLAYING)
15160 case GAME_CTRL_ID_STOP:
15164 RequestQuitGame(TRUE);
15167 case GAME_CTRL_ID_PAUSE:
15168 if (options.network)
15170 #if defined(NETWORK_AVALIABLE)
15172 SendToServer_ContinuePlaying();
15174 SendToServer_PausePlaying();
15178 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15181 case GAME_CTRL_ID_PLAY:
15184 #if defined(NETWORK_AVALIABLE)
15185 if (options.network)
15186 SendToServer_ContinuePlaying();
15190 tape.pausing = FALSE;
15191 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15196 case SOUND_CTRL_ID_MUSIC:
15197 if (setup.sound_music)
15199 setup.sound_music = FALSE;
15202 else if (audio.music_available)
15204 setup.sound = setup.sound_music = TRUE;
15206 SetAudioMode(setup.sound);
15212 case SOUND_CTRL_ID_LOOPS:
15213 if (setup.sound_loops)
15214 setup.sound_loops = FALSE;
15215 else if (audio.loops_available)
15217 setup.sound = setup.sound_loops = TRUE;
15218 SetAudioMode(setup.sound);
15222 case SOUND_CTRL_ID_SIMPLE:
15223 if (setup.sound_simple)
15224 setup.sound_simple = FALSE;
15225 else if (audio.sound_available)
15227 setup.sound = setup.sound_simple = TRUE;
15228 SetAudioMode(setup.sound);