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_EMC_MAGIC_BALL_TIME 37
172 #define GAME_CONTROL_LIGHT_SWITCH 38
173 #define GAME_CONTROL_LIGHT_SWITCH_TIME 39
174 #define GAME_CONTROL_TIMEGATE_SWITCH 40
175 #define GAME_CONTROL_TIMEGATE_SWITCH_TIME 41
176 #define GAME_CONTROL_SWITCHGATE_SWITCH 42
177 #define GAME_CONTROL_EMC_LENSES 43
178 #define GAME_CONTROL_EMC_LENSES_TIME 44
179 #define GAME_CONTROL_EMC_MAGNIFIER 45
180 #define GAME_CONTROL_EMC_MAGNIFIER_TIME 46
181 #define GAME_CONTROL_BALLOON_SWITCH 47
182 #define GAME_CONTROL_DYNABOMB_NUMBER 48
183 #define GAME_CONTROL_DYNABOMB_SIZE 49
184 #define GAME_CONTROL_DYNABOMB_POWER 50
185 #define GAME_CONTROL_PENGUINS 51
186 #define GAME_CONTROL_SOKOBAN_OBJECTS 52
187 #define GAME_CONTROL_SOKOBAN_FIELDS 53
188 #define GAME_CONTROL_ROBOT_WHEEL 54
189 #define GAME_CONTROL_CONVEYOR_BELT_1 55
190 #define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 56
191 #define GAME_CONTROL_CONVEYOR_BELT_2 57
192 #define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 58
193 #define GAME_CONTROL_CONVEYOR_BELT_3 59
194 #define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 60
195 #define GAME_CONTROL_CONVEYOR_BELT_4 61
196 #define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 62
197 #define GAME_CONTROL_MAGIC_WALL 63
198 #define GAME_CONTROL_MAGIC_WALL_TIME 64
199 #define GAME_CONTROL_BD_MAGIC_WALL 65
200 #define GAME_CONTROL_DC_MAGIC_WALL 66
201 #define GAME_CONTROL_PLAYER_NAME 67
202 #define GAME_CONTROL_LEVEL_NAME 68
203 #define GAME_CONTROL_LEVEL_AUTHOR 69
205 #define NUM_GAME_CONTROLS 70
207 int game_control_value[NUM_GAME_CONTROLS];
208 int last_game_control_value[NUM_GAME_CONTROLS];
210 struct GameControlInfo
214 struct TextPosInfo *pos;
218 static struct GameControlInfo game_controls[] =
221 GAME_CONTROL_LEVEL_NUMBER,
222 &game.panel.level_number,
231 GAME_CONTROL_INVENTORY,
232 &game.panel.inventory,
276 GAME_CONTROL_KEY_WHITE,
277 &game.panel.key_white,
281 GAME_CONTROL_KEY_WHITE_COUNT,
282 &game.panel.key_white_count,
296 GAME_CONTROL_TIME_HH,
301 GAME_CONTROL_TIME_MM,
306 GAME_CONTROL_TIME_SS,
311 GAME_CONTROL_DROP_NEXT_1,
312 &game.panel.drop_next_1,
316 GAME_CONTROL_DROP_NEXT_2,
317 &game.panel.drop_next_2,
321 GAME_CONTROL_DROP_NEXT_3,
322 &game.panel.drop_next_3,
326 GAME_CONTROL_DROP_NEXT_4,
327 &game.panel.drop_next_4,
331 GAME_CONTROL_DROP_NEXT_5,
332 &game.panel.drop_next_5,
336 GAME_CONTROL_DROP_NEXT_6,
337 &game.panel.drop_next_6,
341 GAME_CONTROL_DROP_NEXT_7,
342 &game.panel.drop_next_7,
346 GAME_CONTROL_DROP_NEXT_8,
347 &game.panel.drop_next_8,
351 GAME_CONTROL_SHIELD_NORMAL,
352 &game.panel.shield_normal,
356 GAME_CONTROL_SHIELD_NORMAL_TIME,
357 &game.panel.shield_normal_time,
361 GAME_CONTROL_SHIELD_DEADLY,
362 &game.panel.shield_deadly,
366 GAME_CONTROL_SHIELD_DEADLY_TIME,
367 &game.panel.shield_deadly_time,
376 GAME_CONTROL_EM_EXIT,
381 GAME_CONTROL_SP_EXIT,
386 GAME_CONTROL_STEEL_EXIT,
387 &game.panel.steel_exit,
391 GAME_CONTROL_EM_STEEL_EXIT,
392 &game.panel.em_steel_exit,
396 GAME_CONTROL_EMC_MAGIC_BALL,
397 &game.panel.emc_magic_ball,
401 GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
402 &game.panel.emc_magic_ball_switch,
406 GAME_CONTROL_EMC_MAGIC_BALL_TIME,
407 &game.panel.emc_magic_ball_time,
411 GAME_CONTROL_LIGHT_SWITCH,
412 &game.panel.light_switch,
416 GAME_CONTROL_LIGHT_SWITCH_TIME,
417 &game.panel.light_switch_time,
421 GAME_CONTROL_TIMEGATE_SWITCH,
422 &game.panel.timegate_switch,
426 GAME_CONTROL_TIMEGATE_SWITCH_TIME,
427 &game.panel.timegate_switch_time,
431 GAME_CONTROL_SWITCHGATE_SWITCH,
432 &game.panel.switchgate_switch,
436 GAME_CONTROL_EMC_LENSES,
437 &game.panel.emc_lenses,
441 GAME_CONTROL_EMC_LENSES_TIME,
442 &game.panel.emc_lenses_time,
446 GAME_CONTROL_EMC_MAGNIFIER,
447 &game.panel.emc_magnifier,
451 GAME_CONTROL_EMC_MAGNIFIER_TIME,
452 &game.panel.emc_magnifier_time,
456 GAME_CONTROL_BALLOON_SWITCH,
457 &game.panel.balloon_switch,
461 GAME_CONTROL_DYNABOMB_NUMBER,
462 &game.panel.dynabomb_number,
466 GAME_CONTROL_DYNABOMB_SIZE,
467 &game.panel.dynabomb_size,
471 GAME_CONTROL_DYNABOMB_POWER,
472 &game.panel.dynabomb_power,
476 GAME_CONTROL_PENGUINS,
477 &game.panel.penguins,
481 GAME_CONTROL_SOKOBAN_OBJECTS,
482 &game.panel.sokoban_objects,
486 GAME_CONTROL_SOKOBAN_FIELDS,
487 &game.panel.sokoban_fields,
491 GAME_CONTROL_ROBOT_WHEEL,
492 &game.panel.robot_wheel,
496 GAME_CONTROL_CONVEYOR_BELT_1,
497 &game.panel.conveyor_belt_1,
501 GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
502 &game.panel.conveyor_belt_1_switch,
506 GAME_CONTROL_CONVEYOR_BELT_2,
507 &game.panel.conveyor_belt_2,
511 GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
512 &game.panel.conveyor_belt_2_switch,
516 GAME_CONTROL_CONVEYOR_BELT_3,
517 &game.panel.conveyor_belt_3,
521 GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
522 &game.panel.conveyor_belt_3_switch,
526 GAME_CONTROL_CONVEYOR_BELT_4,
527 &game.panel.conveyor_belt_4,
531 GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
532 &game.panel.conveyor_belt_4_switch,
536 GAME_CONTROL_MAGIC_WALL,
537 &game.panel.magic_wall,
541 GAME_CONTROL_MAGIC_WALL_TIME,
542 &game.panel.magic_wall_time,
546 GAME_CONTROL_BD_MAGIC_WALL,
547 &game.panel.bd_magic_wall,
551 GAME_CONTROL_DC_MAGIC_WALL,
552 &game.panel.dc_magic_wall,
556 GAME_CONTROL_PLAYER_NAME,
557 &game.panel.player_name,
561 GAME_CONTROL_LEVEL_NAME,
562 &game.panel.level_name,
566 GAME_CONTROL_LEVEL_AUTHOR,
567 &game.panel.level_author,
580 /* values for delayed check of falling and moving elements and for collision */
581 #define CHECK_DELAY_MOVING 3
582 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
583 #define CHECK_DELAY_COLLISION 2
584 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
586 /* values for initial player move delay (initial delay counter value) */
587 #define INITIAL_MOVE_DELAY_OFF -1
588 #define INITIAL_MOVE_DELAY_ON 0
590 /* values for player movement speed (which is in fact a delay value) */
591 #define MOVE_DELAY_MIN_SPEED 32
592 #define MOVE_DELAY_NORMAL_SPEED 8
593 #define MOVE_DELAY_HIGH_SPEED 4
594 #define MOVE_DELAY_MAX_SPEED 1
596 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
597 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
599 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
600 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
602 /* values for other actions */
603 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
604 #define MOVE_STEPSIZE_MIN (1)
605 #define MOVE_STEPSIZE_MAX (TILEX)
607 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
608 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
610 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
612 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
613 RND(element_info[e].push_delay_random))
614 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
615 RND(element_info[e].drop_delay_random))
616 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
617 RND(element_info[e].move_delay_random))
618 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
619 (element_info[e].move_delay_random))
620 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
621 RND(element_info[e].ce_value_random_initial))
622 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
623 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
624 RND((c)->delay_random * (c)->delay_frames))
625 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
626 RND((c)->delay_random))
629 #define GET_VALID_RUNTIME_ELEMENT(e) \
630 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
632 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
633 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
634 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
635 (be) + (e) - EL_SELF)
637 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
638 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
639 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
640 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
641 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
642 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
643 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
644 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
645 RESOLVED_REFERENCE_ELEMENT(be, e) : \
648 #define CAN_GROW_INTO(e) \
649 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
651 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
652 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
655 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
656 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
657 (CAN_MOVE_INTO_ACID(e) && \
658 Feld[x][y] == EL_ACID) || \
661 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
662 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
663 (CAN_MOVE_INTO_ACID(e) && \
664 Feld[x][y] == EL_ACID) || \
667 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
668 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
670 (CAN_MOVE_INTO_ACID(e) && \
671 Feld[x][y] == EL_ACID) || \
672 (DONT_COLLIDE_WITH(e) && \
674 !PLAYER_ENEMY_PROTECTED(x, y))))
676 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
677 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
679 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
680 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
682 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
683 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
685 #define ANDROID_CAN_CLONE_FIELD(x, y) \
686 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
687 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
689 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
690 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
692 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
693 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
695 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
696 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
698 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
699 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
701 #define PIG_CAN_ENTER_FIELD(e, x, y) \
702 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
704 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
705 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
706 Feld[x][y] == EL_EM_EXIT_OPEN || \
707 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
708 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
709 IS_FOOD_PENGUIN(Feld[x][y])))
710 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
711 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
713 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
714 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
716 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
717 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
719 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
720 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
721 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
723 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
725 #define CE_ENTER_FIELD_COND(e, x, y) \
726 (!IS_PLAYER(x, y) && \
727 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
729 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
730 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
732 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
733 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
735 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
736 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
737 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
738 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
740 /* game button identifiers */
741 #define GAME_CTRL_ID_STOP 0
742 #define GAME_CTRL_ID_PAUSE 1
743 #define GAME_CTRL_ID_PLAY 2
744 #define SOUND_CTRL_ID_MUSIC 3
745 #define SOUND_CTRL_ID_LOOPS 4
746 #define SOUND_CTRL_ID_SIMPLE 5
748 #define NUM_GAME_BUTTONS 6
751 /* forward declaration for internal use */
753 static void CreateField(int, int, int);
755 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
756 static void AdvanceFrameAndPlayerCounters(int);
758 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
759 static boolean MovePlayer(struct PlayerInfo *, int, int);
760 static void ScrollPlayer(struct PlayerInfo *, int);
761 static void ScrollScreen(struct PlayerInfo *, int);
763 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
765 static void InitBeltMovement(void);
766 static void CloseAllOpenTimegates(void);
767 static void CheckGravityMovement(struct PlayerInfo *);
768 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
769 static void KillPlayerUnlessEnemyProtected(int, int);
770 static void KillPlayerUnlessExplosionProtected(int, int);
772 static void TestIfPlayerTouchesCustomElement(int, int);
773 static void TestIfElementTouchesCustomElement(int, int);
774 static void TestIfElementHitsCustomElement(int, int, int);
776 static void TestIfElementSmashesCustomElement(int, int, int);
779 static void HandleElementChange(int, int, int);
780 static void ExecuteCustomElementAction(int, int, int, int);
781 static boolean ChangeElement(int, int, int, int);
783 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
784 #define CheckTriggeredElementChange(x, y, e, ev) \
785 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
786 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
787 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
788 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
789 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
790 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
791 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
793 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
794 #define CheckElementChange(x, y, e, te, ev) \
795 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
796 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
797 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
798 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
799 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
801 static void PlayLevelSound(int, int, int);
802 static void PlayLevelSoundNearest(int, int, int);
803 static void PlayLevelSoundAction(int, int, int);
804 static void PlayLevelSoundElementAction(int, int, int, int);
805 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
806 static void PlayLevelSoundActionIfLoop(int, int, int);
807 static void StopLevelSoundActionIfLoop(int, int, int);
808 static void PlayLevelMusic();
810 static void MapGameButtons();
811 static void HandleGameButtons(struct GadgetInfo *);
813 int AmoebeNachbarNr(int, int);
814 void AmoebeUmwandeln(int, int);
815 void ContinueMoving(int, int);
817 void InitMovDir(int, int);
818 void InitAmoebaNr(int, int);
819 int NewHiScore(void);
821 void TestIfGoodThingHitsBadThing(int, int, int);
822 void TestIfBadThingHitsGoodThing(int, int, int);
823 void TestIfPlayerTouchesBadThing(int, int);
824 void TestIfPlayerRunsIntoBadThing(int, int, int);
825 void TestIfBadThingTouchesPlayer(int, int);
826 void TestIfBadThingRunsIntoPlayer(int, int, int);
827 void TestIfFriendTouchesBadThing(int, int);
828 void TestIfBadThingTouchesFriend(int, int);
829 void TestIfBadThingTouchesOtherBadThing(int, int);
831 void KillPlayer(struct PlayerInfo *);
832 void BuryPlayer(struct PlayerInfo *);
833 void RemovePlayer(struct PlayerInfo *);
835 boolean SnapField(struct PlayerInfo *, int, int);
836 boolean DropElement(struct PlayerInfo *);
838 static int getInvisibleActiveFromInvisibleElement(int);
839 static int getInvisibleFromInvisibleActiveElement(int);
841 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
843 /* for detection of endless loops, caused by custom element programming */
844 /* (using maximal playfield width x 10 is just a rough approximation) */
845 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
847 #define RECURSION_LOOP_DETECTION_START(e, rc) \
849 if (recursion_loop_detected) \
852 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
854 recursion_loop_detected = TRUE; \
855 recursion_loop_element = (e); \
858 recursion_loop_depth++; \
861 #define RECURSION_LOOP_DETECTION_END() \
863 recursion_loop_depth--; \
866 static int recursion_loop_depth;
867 static boolean recursion_loop_detected;
868 static boolean recursion_loop_element;
871 /* ------------------------------------------------------------------------- */
872 /* definition of elements that automatically change to other elements after */
873 /* a specified time, eventually calling a function when changing */
874 /* ------------------------------------------------------------------------- */
876 /* forward declaration for changer functions */
877 static void InitBuggyBase(int, int);
878 static void WarnBuggyBase(int, int);
880 static void InitTrap(int, int);
881 static void ActivateTrap(int, int);
882 static void ChangeActiveTrap(int, int);
884 static void InitRobotWheel(int, int);
885 static void RunRobotWheel(int, int);
886 static void StopRobotWheel(int, int);
888 static void InitTimegateWheel(int, int);
889 static void RunTimegateWheel(int, int);
891 static void InitMagicBallDelay(int, int);
892 static void ActivateMagicBall(int, int);
894 struct ChangingElementInfo
899 void (*pre_change_function)(int x, int y);
900 void (*change_function)(int x, int y);
901 void (*post_change_function)(int x, int y);
904 static struct ChangingElementInfo change_delay_list[] =
939 EL_STEEL_EXIT_OPENING,
947 EL_STEEL_EXIT_CLOSING,
948 EL_STEEL_EXIT_CLOSED,
975 EL_EM_STEEL_EXIT_OPENING,
976 EL_EM_STEEL_EXIT_OPEN,
983 EL_EM_STEEL_EXIT_CLOSING,
987 EL_EM_STEEL_EXIT_CLOSED,
1011 EL_SWITCHGATE_OPENING,
1019 EL_SWITCHGATE_CLOSING,
1020 EL_SWITCHGATE_CLOSED,
1027 EL_TIMEGATE_OPENING,
1035 EL_TIMEGATE_CLOSING,
1044 EL_ACID_SPLASH_LEFT,
1052 EL_ACID_SPLASH_RIGHT,
1061 EL_SP_BUGGY_BASE_ACTIVATING,
1068 EL_SP_BUGGY_BASE_ACTIVATING,
1069 EL_SP_BUGGY_BASE_ACTIVE,
1076 EL_SP_BUGGY_BASE_ACTIVE,
1100 EL_ROBOT_WHEEL_ACTIVE,
1108 EL_TIMEGATE_SWITCH_ACTIVE,
1116 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1117 EL_DC_TIMEGATE_SWITCH,
1124 EL_EMC_MAGIC_BALL_ACTIVE,
1125 EL_EMC_MAGIC_BALL_ACTIVE,
1132 EL_EMC_SPRING_BUMPER_ACTIVE,
1133 EL_EMC_SPRING_BUMPER,
1140 EL_DIAGONAL_SHRINKING,
1148 EL_DIAGONAL_GROWING,
1169 int push_delay_fixed, push_delay_random;
1173 { EL_SPRING, 0, 0 },
1174 { EL_BALLOON, 0, 0 },
1176 { EL_SOKOBAN_OBJECT, 2, 0 },
1177 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1178 { EL_SATELLITE, 2, 0 },
1179 { EL_SP_DISK_YELLOW, 2, 0 },
1181 { EL_UNDEFINED, 0, 0 },
1189 move_stepsize_list[] =
1191 { EL_AMOEBA_DROP, 2 },
1192 { EL_AMOEBA_DROPPING, 2 },
1193 { EL_QUICKSAND_FILLING, 1 },
1194 { EL_QUICKSAND_EMPTYING, 1 },
1195 { EL_QUICKSAND_FAST_FILLING, 2 },
1196 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1197 { EL_MAGIC_WALL_FILLING, 2 },
1198 { EL_MAGIC_WALL_EMPTYING, 2 },
1199 { EL_BD_MAGIC_WALL_FILLING, 2 },
1200 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1201 { EL_DC_MAGIC_WALL_FILLING, 2 },
1202 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1204 { EL_UNDEFINED, 0 },
1212 collect_count_list[] =
1215 { EL_BD_DIAMOND, 1 },
1216 { EL_EMERALD_YELLOW, 1 },
1217 { EL_EMERALD_RED, 1 },
1218 { EL_EMERALD_PURPLE, 1 },
1220 { EL_SP_INFOTRON, 1 },
1224 { EL_UNDEFINED, 0 },
1232 access_direction_list[] =
1234 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1235 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1236 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1237 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1238 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1239 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1240 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1241 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1242 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1243 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1244 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1246 { EL_SP_PORT_LEFT, MV_RIGHT },
1247 { EL_SP_PORT_RIGHT, MV_LEFT },
1248 { EL_SP_PORT_UP, MV_DOWN },
1249 { EL_SP_PORT_DOWN, MV_UP },
1250 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1251 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1252 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1253 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1254 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1255 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1256 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1257 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1258 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1259 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1260 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1261 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1262 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1263 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1264 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1266 { EL_UNDEFINED, MV_NONE }
1269 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1271 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1272 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1273 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1274 IS_JUST_CHANGING(x, y))
1276 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1278 /* static variables for playfield scan mode (scanning forward or backward) */
1279 static int playfield_scan_start_x = 0;
1280 static int playfield_scan_start_y = 0;
1281 static int playfield_scan_delta_x = 1;
1282 static int playfield_scan_delta_y = 1;
1284 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1285 (y) >= 0 && (y) <= lev_fieldy - 1; \
1286 (y) += playfield_scan_delta_y) \
1287 for ((x) = playfield_scan_start_x; \
1288 (x) >= 0 && (x) <= lev_fieldx - 1; \
1289 (x) += playfield_scan_delta_x)
1292 void DEBUG_SetMaximumDynamite()
1296 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1297 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1298 local_player->inventory_element[local_player->inventory_size++] =
1303 static void InitPlayfieldScanModeVars()
1305 if (game.use_reverse_scan_direction)
1307 playfield_scan_start_x = lev_fieldx - 1;
1308 playfield_scan_start_y = lev_fieldy - 1;
1310 playfield_scan_delta_x = -1;
1311 playfield_scan_delta_y = -1;
1315 playfield_scan_start_x = 0;
1316 playfield_scan_start_y = 0;
1318 playfield_scan_delta_x = 1;
1319 playfield_scan_delta_y = 1;
1323 static void InitPlayfieldScanMode(int mode)
1325 game.use_reverse_scan_direction =
1326 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1328 InitPlayfieldScanModeVars();
1331 static int get_move_delay_from_stepsize(int move_stepsize)
1334 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1336 /* make sure that stepsize value is always a power of 2 */
1337 move_stepsize = (1 << log_2(move_stepsize));
1339 return TILEX / move_stepsize;
1342 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1345 int player_nr = player->index_nr;
1346 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1347 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1349 /* do no immediately change move delay -- the player might just be moving */
1350 player->move_delay_value_next = move_delay;
1352 /* information if player can move must be set separately */
1353 player->cannot_move = cannot_move;
1357 player->move_delay = game.initial_move_delay[player_nr];
1358 player->move_delay_value = game.initial_move_delay_value[player_nr];
1360 player->move_delay_value_next = -1;
1362 player->move_delay_reset_counter = 0;
1366 void GetPlayerConfig()
1368 GameFrameDelay = setup.game_frame_delay;
1370 if (!audio.sound_available)
1371 setup.sound_simple = FALSE;
1373 if (!audio.loops_available)
1374 setup.sound_loops = FALSE;
1376 if (!audio.music_available)
1377 setup.sound_music = FALSE;
1379 if (!video.fullscreen_available)
1380 setup.fullscreen = FALSE;
1382 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1384 SetAudioMode(setup.sound);
1388 int GetElementFromGroupElement(int element)
1390 if (IS_GROUP_ELEMENT(element))
1392 struct ElementGroupInfo *group = element_info[element].group;
1393 int last_anim_random_frame = gfx.anim_random_frame;
1396 if (group->choice_mode == ANIM_RANDOM)
1397 gfx.anim_random_frame = RND(group->num_elements_resolved);
1399 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1400 group->choice_mode, 0,
1403 if (group->choice_mode == ANIM_RANDOM)
1404 gfx.anim_random_frame = last_anim_random_frame;
1406 group->choice_pos++;
1408 element = group->element_resolved[element_pos];
1414 static void InitPlayerField(int x, int y, int element, boolean init_game)
1416 if (element == EL_SP_MURPHY)
1420 if (stored_player[0].present)
1422 Feld[x][y] = EL_SP_MURPHY_CLONE;
1428 stored_player[0].use_murphy = TRUE;
1430 if (!level.use_artwork_element[0])
1431 stored_player[0].artwork_element = EL_SP_MURPHY;
1434 Feld[x][y] = EL_PLAYER_1;
1440 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1441 int jx = player->jx, jy = player->jy;
1443 player->present = TRUE;
1445 player->block_last_field = (element == EL_SP_MURPHY ?
1446 level.sp_block_last_field :
1447 level.block_last_field);
1449 /* ---------- initialize player's last field block delay --------------- */
1451 /* always start with reliable default value (no adjustment needed) */
1452 player->block_delay_adjustment = 0;
1454 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1455 if (player->block_last_field && element == EL_SP_MURPHY)
1456 player->block_delay_adjustment = 1;
1458 /* special case 2: in game engines before 3.1.1, blocking was different */
1459 if (game.use_block_last_field_bug)
1460 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1462 if (!options.network || player->connected)
1464 player->active = TRUE;
1466 /* remove potentially duplicate players */
1467 if (StorePlayer[jx][jy] == Feld[x][y])
1468 StorePlayer[jx][jy] = 0;
1470 StorePlayer[x][y] = Feld[x][y];
1474 printf("Player %d activated.\n", player->element_nr);
1475 printf("[Local player is %d and currently %s.]\n",
1476 local_player->element_nr,
1477 local_player->active ? "active" : "not active");
1481 Feld[x][y] = EL_EMPTY;
1483 player->jx = player->last_jx = x;
1484 player->jy = player->last_jy = y;
1488 static void InitField(int x, int y, boolean init_game)
1490 int element = Feld[x][y];
1499 InitPlayerField(x, y, element, init_game);
1502 case EL_SOKOBAN_FIELD_PLAYER:
1503 element = Feld[x][y] = EL_PLAYER_1;
1504 InitField(x, y, init_game);
1506 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1507 InitField(x, y, init_game);
1510 case EL_SOKOBAN_FIELD_EMPTY:
1511 local_player->sokobanfields_still_needed++;
1515 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1516 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1517 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1518 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1519 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1520 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1521 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1522 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1523 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1524 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1533 case EL_SPACESHIP_RIGHT:
1534 case EL_SPACESHIP_UP:
1535 case EL_SPACESHIP_LEFT:
1536 case EL_SPACESHIP_DOWN:
1537 case EL_BD_BUTTERFLY:
1538 case EL_BD_BUTTERFLY_RIGHT:
1539 case EL_BD_BUTTERFLY_UP:
1540 case EL_BD_BUTTERFLY_LEFT:
1541 case EL_BD_BUTTERFLY_DOWN:
1543 case EL_BD_FIREFLY_RIGHT:
1544 case EL_BD_FIREFLY_UP:
1545 case EL_BD_FIREFLY_LEFT:
1546 case EL_BD_FIREFLY_DOWN:
1547 case EL_PACMAN_RIGHT:
1549 case EL_PACMAN_LEFT:
1550 case EL_PACMAN_DOWN:
1552 case EL_YAMYAM_LEFT:
1553 case EL_YAMYAM_RIGHT:
1555 case EL_YAMYAM_DOWN:
1556 case EL_DARK_YAMYAM:
1559 case EL_SP_SNIKSNAK:
1560 case EL_SP_ELECTRON:
1569 case EL_AMOEBA_FULL:
1574 case EL_AMOEBA_DROP:
1575 if (y == lev_fieldy - 1)
1577 Feld[x][y] = EL_AMOEBA_GROWING;
1578 Store[x][y] = EL_AMOEBA_WET;
1582 case EL_DYNAMITE_ACTIVE:
1583 case EL_SP_DISK_RED_ACTIVE:
1584 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1585 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1586 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1587 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1588 MovDelay[x][y] = 96;
1591 case EL_EM_DYNAMITE_ACTIVE:
1592 MovDelay[x][y] = 32;
1596 local_player->lights_still_needed++;
1600 local_player->friends_still_needed++;
1605 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1608 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1609 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1610 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1611 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1612 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1613 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1614 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1615 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1616 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1617 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1618 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1619 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1622 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1623 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1624 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1626 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1628 game.belt_dir[belt_nr] = belt_dir;
1629 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1631 else /* more than one switch -- set it like the first switch */
1633 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1638 #if !USE_BOTH_SWITCHGATE_SWITCHES
1639 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1641 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1644 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1646 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1650 case EL_LIGHT_SWITCH_ACTIVE:
1652 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1655 case EL_INVISIBLE_STEELWALL:
1656 case EL_INVISIBLE_WALL:
1657 case EL_INVISIBLE_SAND:
1658 if (game.light_time_left > 0 ||
1659 game.lenses_time_left > 0)
1660 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1663 case EL_EMC_MAGIC_BALL:
1664 if (game.ball_state)
1665 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1668 case EL_EMC_MAGIC_BALL_SWITCH:
1669 if (game.ball_state)
1670 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1674 if (IS_CUSTOM_ELEMENT(element))
1676 if (CAN_MOVE(element))
1679 #if USE_NEW_CUSTOM_VALUE
1680 if (!element_info[element].use_last_ce_value || init_game)
1681 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1684 else if (IS_GROUP_ELEMENT(element))
1686 Feld[x][y] = GetElementFromGroupElement(element);
1688 InitField(x, y, init_game);
1695 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1698 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1700 InitField(x, y, init_game);
1702 /* not needed to call InitMovDir() -- already done by InitField()! */
1703 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1704 CAN_MOVE(Feld[x][y]))
1708 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1710 int old_element = Feld[x][y];
1712 InitField(x, y, init_game);
1714 /* not needed to call InitMovDir() -- already done by InitField()! */
1715 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1716 CAN_MOVE(old_element) &&
1717 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1720 /* this case is in fact a combination of not less than three bugs:
1721 first, it calls InitMovDir() for elements that can move, although this is
1722 already done by InitField(); then, it checks the element that was at this
1723 field _before_ the call to InitField() (which can change it); lastly, it
1724 was not called for "mole with direction" elements, which were treated as
1725 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1731 void InitGameControlValues()
1735 for (i = 0; i < NUM_GAME_CONTROLS; i++)
1736 game_control_value[i] = last_game_control_value[i] = -1;
1738 for (i = 0; game_controls[i].nr != -1; i++)
1740 int nr = game_controls[i].nr;
1741 int type = game_controls[i].type;
1742 struct TextPosInfo *pos = game_controls[i].pos;
1744 game_control_value[nr] = last_game_control_value[nr] = -1;
1746 /* determine panel value width for later calculation of alignment */
1747 if (type == TYPE_INTEGER || type == TYPE_STRING)
1748 pos->width = pos->chars * getFontWidth(pos->font);
1752 void UpdateGameControlValues()
1756 game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
1757 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
1759 game_control_value[GAME_CONTROL_INVENTORY] = 0;
1760 for (i = 0; i < MAX_NUM_KEYS; i++)
1761 game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
1762 game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
1763 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
1765 if (game.centered_player_nr == -1)
1767 for (i = 0; i < MAX_PLAYERS; i++)
1769 for (j = 0; j < MAX_NUM_KEYS; j++)
1770 if (stored_player[i].key[j])
1771 game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
1773 game_control_value[GAME_CONTROL_INVENTORY] +=
1774 stored_player[i].inventory_size;
1776 if (stored_player[i].num_white_keys > 0)
1777 game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1779 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1780 stored_player[i].num_white_keys;
1785 int player_nr = game.centered_player_nr;
1787 for (i = 0; i < MAX_NUM_KEYS; i++)
1788 if (stored_player[player_nr].key[i])
1789 game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
1791 game_control_value[GAME_CONTROL_INVENTORY] +=
1792 stored_player[player_nr].inventory_size;
1794 if (stored_player[player_nr].num_white_keys > 0)
1795 game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
1797 game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
1798 stored_player[player_nr].num_white_keys;
1801 game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
1802 local_player->score_final :
1803 local_player->score);
1805 game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
1809 game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
1810 game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
1811 game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
1813 for (i = 0; i < 8; i++)
1814 game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = 0;
1816 game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
1817 (local_player->shield_normal_time_left > 0 ? 1 : 0);
1818 game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
1819 local_player->shield_normal_time_left;
1820 game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
1821 (local_player->shield_deadly_time_left > 0 ? 1 : 0);
1822 game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
1823 local_player->shield_deadly_time_left;
1825 game_control_value[GAME_CONTROL_EXIT] = 0;
1826 game_control_value[GAME_CONTROL_EM_EXIT] = 0;
1827 game_control_value[GAME_CONTROL_SP_EXIT] = 0;
1828 game_control_value[GAME_CONTROL_STEEL_EXIT] = 0;
1829 game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = 0;
1831 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = 0;
1832 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = 0;
1833 game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_TIME] = 0;
1835 game_control_value[GAME_CONTROL_LIGHT_SWITCH] = 0;
1836 game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
1838 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] = 0;
1839 game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
1840 game.timegate_time_left;
1842 game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = 0;
1844 game_control_value[GAME_CONTROL_EMC_LENSES] = 0;
1845 game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
1847 game_control_value[GAME_CONTROL_EMC_MAGNIFIER] = 0;
1848 game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
1850 game_control_value[GAME_CONTROL_BALLOON_SWITCH] = 0;
1852 game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
1853 local_player->dynabomb_count;
1854 game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
1855 local_player->dynabomb_size;
1856 game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
1857 local_player->dynabomb_xl;
1859 game_control_value[GAME_CONTROL_PENGUINS] =
1860 local_player->friends_still_needed;
1862 game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
1863 local_player->sokobanfields_still_needed;
1864 game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
1865 local_player->sokobanfields_still_needed;
1867 game_control_value[GAME_CONTROL_ROBOT_WHEEL] = 0;
1869 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = 0;
1870 game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = 0;
1871 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = 0;
1872 game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = 0;
1873 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = 0;
1874 game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = 0;
1875 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = 0;
1876 game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = 0;
1878 game_control_value[GAME_CONTROL_MAGIC_WALL] = 0;
1879 game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
1880 game.magic_wall_time_left;
1881 game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = 0;
1882 game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = 0;
1884 game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
1885 game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
1886 game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
1889 void DisplayGameControlValues()
1893 for (i = 0; game_controls[i].nr != -1; i++)
1895 int nr = game_controls[i].nr;
1896 int type = game_controls[i].type;
1897 struct TextPosInfo *pos = game_controls[i].pos;
1898 int value = game_control_value[nr];
1899 int last_value = last_game_control_value[nr];
1900 int chars = pos->chars;
1901 int font = pos->font;
1903 if (value == last_value)
1906 last_game_control_value[nr] = value;
1909 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
1912 if (PANEL_DEACTIVATED(pos))
1915 if (type == TYPE_INTEGER)
1917 if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
1919 boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
1921 if (use_dynamic_chars) /* use dynamic number of chars */
1923 int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
1924 int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
1925 int chars2 = chars1 + 1;
1926 int font1 = pos->font;
1927 int font2 = pos->font_alt;
1929 chars = (value < value_change ? chars1 : chars2);
1930 font = (value < value_change ? font1 : font2);
1932 /* clear background if value just changed its size (dynamic chars) */
1933 if ((last_value < value_change) != (value < value_change))
1935 int width1 = chars1 * getFontWidth(font1);
1936 int width2 = chars2 * getFontWidth(font2);
1937 int max_width = MAX(width1, width2);
1938 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
1940 pos->width = max_width;
1942 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1943 max_width, max_height);
1947 pos->width = chars * getFontWidth(font);
1950 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
1952 else if (type == TYPE_ELEMENT)
1954 if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
1956 int key_nr = nr - GAME_CONTROL_KEY_1;
1957 int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
1958 int src_y = DOOR_GFX_PAGEY1 + 123;
1959 int dst_x = PANEL_XPOS(pos);
1960 int dst_y = PANEL_YPOS(pos);
1961 int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1962 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1963 EL_EM_KEY_1 : EL_KEY_1) + key_nr;
1964 int graphic = el2edimg(element);
1967 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
1969 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1970 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1973 else if (type == TYPE_STRING)
1975 char *s = (nr == GAME_CONTROL_PLAYER_NAME ? setup.player_name :
1976 nr == GAME_CONTROL_LEVEL_NAME ? level.name :
1977 nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
1981 char *s_cut = getStringCopyN(s, pos->chars);
1983 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
1991 void DrawGameValue_Emeralds(int value)
1993 struct TextPosInfo *pos = &game.panel.gems;
1995 int font_nr = pos->font;
1997 int font_nr = FONT_TEXT_2;
1999 int font_width = getFontWidth(font_nr);
2000 int chars = pos->chars;
2003 return; /* !!! USE NEW STUFF !!! */
2006 if (PANEL_DEACTIVATED(pos))
2009 pos->width = chars * font_width;
2011 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2014 void DrawGameValue_Dynamite(int value)
2016 struct TextPosInfo *pos = &game.panel.inventory;
2018 int font_nr = pos->font;
2020 int font_nr = FONT_TEXT_2;
2022 int font_width = getFontWidth(font_nr);
2023 int chars = pos->chars;
2026 return; /* !!! USE NEW STUFF !!! */
2029 if (PANEL_DEACTIVATED(pos))
2032 pos->width = chars * font_width;
2034 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2037 void DrawGameValue_Score(int value)
2039 struct TextPosInfo *pos = &game.panel.score;
2041 int font_nr = pos->font;
2043 int font_nr = FONT_TEXT_2;
2045 int font_width = getFontWidth(font_nr);
2046 int chars = pos->chars;
2049 return; /* !!! USE NEW STUFF !!! */
2052 if (PANEL_DEACTIVATED(pos))
2055 pos->width = chars * font_width;
2057 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2060 void DrawGameValue_Time(int value)
2062 struct TextPosInfo *pos = &game.panel.time;
2063 static int last_value = -1;
2066 int chars = pos->chars;
2068 int font1_nr = pos->font;
2069 int font2_nr = pos->font_alt;
2071 int font1_nr = FONT_TEXT_2;
2072 int font2_nr = FONT_TEXT_1;
2074 int font_nr = font1_nr;
2075 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2078 return; /* !!! USE NEW STUFF !!! */
2081 if (PANEL_DEACTIVATED(pos))
2084 if (use_dynamic_chars) /* use dynamic number of chars */
2086 chars = (value < 1000 ? chars1 : chars2);
2087 font_nr = (value < 1000 ? font1_nr : font2_nr);
2090 /* clear background if value just changed its size (dynamic chars only) */
2091 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2093 int width1 = chars1 * getFontWidth(font1_nr);
2094 int width2 = chars2 * getFontWidth(font2_nr);
2095 int max_width = MAX(width1, width2);
2096 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2098 pos->width = max_width;
2100 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2101 max_width, max_height);
2104 pos->width = chars * getFontWidth(font_nr);
2106 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2111 void DrawGameValue_Level(int value)
2113 struct TextPosInfo *pos = &game.panel.level_number;
2116 int chars = pos->chars;
2118 int font1_nr = pos->font;
2119 int font2_nr = pos->font_alt;
2121 int font1_nr = FONT_TEXT_2;
2122 int font2_nr = FONT_TEXT_1;
2124 int font_nr = font1_nr;
2125 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2128 return; /* !!! USE NEW STUFF !!! */
2131 if (PANEL_DEACTIVATED(pos))
2134 if (use_dynamic_chars) /* use dynamic number of chars */
2136 chars = (level_nr < 100 ? chars1 : chars2);
2137 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2140 pos->width = chars * getFontWidth(font_nr);
2142 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2145 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2148 struct TextPosInfo *pos = &game.panel.keys;
2151 int base_key_graphic = EL_KEY_1;
2156 return; /* !!! USE NEW STUFF !!! */
2160 if (PANEL_DEACTIVATED(pos))
2165 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2166 base_key_graphic = EL_EM_KEY_1;
2170 pos->width = 4 * MINI_TILEX;
2174 for (i = 0; i < MAX_NUM_KEYS; i++)
2176 /* currently only 4 of 8 possible keys are displayed */
2177 for (i = 0; i < STD_NUM_KEYS; i++)
2181 struct TextPosInfo *pos = &game.panel.key[i];
2183 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2184 int src_y = DOOR_GFX_PAGEY1 + 123;
2186 int dst_x = PANEL_XPOS(pos);
2187 int dst_y = PANEL_YPOS(pos);
2189 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2190 int dst_y = PANEL_YPOS(pos);
2194 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2195 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2197 int graphic = el2edimg(element);
2201 if (PANEL_DEACTIVATED(pos))
2206 /* masked blit with tiles from half-size scaled bitmap does not work yet
2207 (no mask bitmap created for these sizes after loading and scaling) --
2208 solution: load without creating mask, scale, then create final mask */
2210 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2211 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2216 int graphic = el2edimg(base_key_graphic + i);
2221 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2223 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2224 dst_x - src_x, dst_y - src_y);
2225 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2231 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2233 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2234 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2237 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2239 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2240 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2248 void DrawGameValue_Emeralds(int value)
2250 int font_nr = FONT_TEXT_2;
2251 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2253 if (PANEL_DEACTIVATED(game.panel.gems))
2256 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2259 void DrawGameValue_Dynamite(int value)
2261 int font_nr = FONT_TEXT_2;
2262 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2264 if (PANEL_DEACTIVATED(game.panel.inventory))
2267 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2270 void DrawGameValue_Score(int value)
2272 int font_nr = FONT_TEXT_2;
2273 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2275 if (PANEL_DEACTIVATED(game.panel.score))
2278 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2281 void DrawGameValue_Time(int value)
2283 int font1_nr = FONT_TEXT_2;
2285 int font2_nr = FONT_TEXT_1;
2287 int font2_nr = FONT_LEVEL_NUMBER;
2289 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2290 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2292 if (PANEL_DEACTIVATED(game.panel.time))
2295 /* clear background if value just changed its size */
2296 if (value == 999 || value == 1000)
2297 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2300 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2302 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2305 void DrawGameValue_Level(int value)
2307 int font1_nr = FONT_TEXT_2;
2309 int font2_nr = FONT_TEXT_1;
2311 int font2_nr = FONT_LEVEL_NUMBER;
2314 if (PANEL_DEACTIVATED(game.panel.level))
2318 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2320 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2323 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2325 int base_key_graphic = EL_KEY_1;
2328 if (PANEL_DEACTIVATED(game.panel.keys))
2331 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2332 base_key_graphic = EL_EM_KEY_1;
2334 /* currently only 4 of 8 possible keys are displayed */
2335 for (i = 0; i < STD_NUM_KEYS; i++)
2337 int x = XX_KEYS + i * MINI_TILEX;
2341 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2343 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2344 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2350 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2353 int key[MAX_NUM_KEYS];
2356 /* prevent EM engine from updating time/score values parallel to GameWon() */
2357 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2358 local_player->LevelSolved)
2361 for (i = 0; i < MAX_NUM_KEYS; i++)
2362 key[i] = key_bits & (1 << i);
2364 DrawGameValue_Level(level_nr);
2366 DrawGameValue_Emeralds(emeralds);
2367 DrawGameValue_Dynamite(dynamite);
2368 DrawGameValue_Score(score);
2369 DrawGameValue_Time(time);
2371 DrawGameValue_Keys(key);
2374 void DrawGameDoorValues()
2376 UpdateGameControlValues();
2377 DisplayGameControlValues();
2380 void DrawGameDoorValues_OLD()
2382 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2383 int dynamite_value = 0;
2384 int score_value = (local_player->LevelSolved ? local_player->score_final :
2385 local_player->score);
2386 int gems_value = local_player->gems_still_needed;
2390 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2392 DrawGameDoorValues_EM();
2397 if (game.centered_player_nr == -1)
2399 for (i = 0; i < MAX_PLAYERS; i++)
2401 for (j = 0; j < MAX_NUM_KEYS; j++)
2402 if (stored_player[i].key[j])
2403 key_bits |= (1 << j);
2405 dynamite_value += stored_player[i].inventory_size;
2410 int player_nr = game.centered_player_nr;
2412 for (i = 0; i < MAX_NUM_KEYS; i++)
2413 if (stored_player[player_nr].key[i])
2414 key_bits |= (1 << i);
2416 dynamite_value = stored_player[player_nr].inventory_size;
2419 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2425 =============================================================================
2427 -----------------------------------------------------------------------------
2428 initialize game engine due to level / tape version number
2429 =============================================================================
2432 static void InitGameEngine()
2434 int i, j, k, l, x, y;
2436 /* set game engine from tape file when re-playing, else from level file */
2437 game.engine_version = (tape.playing ? tape.engine_version :
2438 level.game_version);
2440 /* ---------------------------------------------------------------------- */
2441 /* set flags for bugs and changes according to active game engine version */
2442 /* ---------------------------------------------------------------------- */
2445 Summary of bugfix/change:
2446 Fixed handling for custom elements that change when pushed by the player.
2448 Fixed/changed in version:
2452 Before 3.1.0, custom elements that "change when pushing" changed directly
2453 after the player started pushing them (until then handled in "DigField()").
2454 Since 3.1.0, these custom elements are not changed until the "pushing"
2455 move of the element is finished (now handled in "ContinueMoving()").
2457 Affected levels/tapes:
2458 The first condition is generally needed for all levels/tapes before version
2459 3.1.0, which might use the old behaviour before it was changed; known tapes
2460 that are affected are some tapes from the level set "Walpurgis Gardens" by
2462 The second condition is an exception from the above case and is needed for
2463 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2464 above (including some development versions of 3.1.0), but before it was
2465 known that this change would break tapes like the above and was fixed in
2466 3.1.1, so that the changed behaviour was active although the engine version
2467 while recording maybe was before 3.1.0. There is at least one tape that is
2468 affected by this exception, which is the tape for the one-level set "Bug
2469 Machine" by Juergen Bonhagen.
2472 game.use_change_when_pushing_bug =
2473 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2475 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2476 tape.game_version < VERSION_IDENT(3,1,1,0)));
2479 Summary of bugfix/change:
2480 Fixed handling for blocking the field the player leaves when moving.
2482 Fixed/changed in version:
2486 Before 3.1.1, when "block last field when moving" was enabled, the field
2487 the player is leaving when moving was blocked for the time of the move,
2488 and was directly unblocked afterwards. This resulted in the last field
2489 being blocked for exactly one less than the number of frames of one player
2490 move. Additionally, even when blocking was disabled, the last field was
2491 blocked for exactly one frame.
2492 Since 3.1.1, due to changes in player movement handling, the last field
2493 is not blocked at all when blocking is disabled. When blocking is enabled,
2494 the last field is blocked for exactly the number of frames of one player
2495 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2496 last field is blocked for exactly one more than the number of frames of
2499 Affected levels/tapes:
2500 (!!! yet to be determined -- probably many !!!)
2503 game.use_block_last_field_bug =
2504 (game.engine_version < VERSION_IDENT(3,1,1,0));
2507 Summary of bugfix/change:
2508 Changed behaviour of CE changes with multiple changes per single frame.
2510 Fixed/changed in version:
2514 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2515 This resulted in race conditions where CEs seem to behave strange in some
2516 situations (where triggered CE changes were just skipped because there was
2517 already a CE change on that tile in the playfield in that engine frame).
2518 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2519 (The number of changes per frame must be limited in any case, because else
2520 it is easily possible to define CE changes that would result in an infinite
2521 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2522 should be set large enough so that it would only be reached in cases where
2523 the corresponding CE change conditions run into a loop. Therefore, it seems
2524 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2525 maximal number of change pages for custom elements.)
2527 Affected levels/tapes:
2531 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2532 game.max_num_changes_per_frame = 1;
2534 game.max_num_changes_per_frame =
2535 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2538 /* ---------------------------------------------------------------------- */
2540 /* default scan direction: scan playfield from top/left to bottom/right */
2541 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2543 /* dynamically adjust element properties according to game engine version */
2544 InitElementPropertiesEngine(game.engine_version);
2547 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2548 printf(" tape version == %06d [%s] [file: %06d]\n",
2549 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2551 printf(" => game.engine_version == %06d\n", game.engine_version);
2554 /* ---------- initialize player's initial move delay --------------------- */
2556 /* dynamically adjust player properties according to level information */
2557 for (i = 0; i < MAX_PLAYERS; i++)
2558 game.initial_move_delay_value[i] =
2559 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2561 /* dynamically adjust player properties according to game engine version */
2562 for (i = 0; i < MAX_PLAYERS; i++)
2563 game.initial_move_delay[i] =
2564 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2565 game.initial_move_delay_value[i] : 0);
2567 /* ---------- initialize player's initial push delay --------------------- */
2569 /* dynamically adjust player properties according to game engine version */
2570 game.initial_push_delay_value =
2571 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2573 /* ---------- initialize changing elements ------------------------------- */
2575 /* initialize changing elements information */
2576 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2578 struct ElementInfo *ei = &element_info[i];
2580 /* this pointer might have been changed in the level editor */
2581 ei->change = &ei->change_page[0];
2583 if (!IS_CUSTOM_ELEMENT(i))
2585 ei->change->target_element = EL_EMPTY_SPACE;
2586 ei->change->delay_fixed = 0;
2587 ei->change->delay_random = 0;
2588 ei->change->delay_frames = 1;
2591 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2593 ei->has_change_event[j] = FALSE;
2595 ei->event_page_nr[j] = 0;
2596 ei->event_page[j] = &ei->change_page[0];
2600 /* add changing elements from pre-defined list */
2601 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2603 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2604 struct ElementInfo *ei = &element_info[ch_delay->element];
2606 ei->change->target_element = ch_delay->target_element;
2607 ei->change->delay_fixed = ch_delay->change_delay;
2609 ei->change->pre_change_function = ch_delay->pre_change_function;
2610 ei->change->change_function = ch_delay->change_function;
2611 ei->change->post_change_function = ch_delay->post_change_function;
2613 ei->change->can_change = TRUE;
2614 ei->change->can_change_or_has_action = TRUE;
2616 ei->has_change_event[CE_DELAY] = TRUE;
2618 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2619 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2622 /* ---------- initialize internal run-time variables ------------- */
2624 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2626 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2628 for (j = 0; j < ei->num_change_pages; j++)
2630 ei->change_page[j].can_change_or_has_action =
2631 (ei->change_page[j].can_change |
2632 ei->change_page[j].has_action);
2636 /* add change events from custom element configuration */
2637 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2639 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2641 for (j = 0; j < ei->num_change_pages; j++)
2643 if (!ei->change_page[j].can_change_or_has_action)
2646 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2648 /* only add event page for the first page found with this event */
2649 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2651 ei->has_change_event[k] = TRUE;
2653 ei->event_page_nr[k] = j;
2654 ei->event_page[k] = &ei->change_page[j];
2660 /* ---------- initialize run-time trigger player and element ------------- */
2662 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2664 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2666 for (j = 0; j < ei->num_change_pages; j++)
2668 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2669 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2670 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2671 ei->change_page[j].actual_trigger_ce_value = 0;
2672 ei->change_page[j].actual_trigger_ce_score = 0;
2676 /* ---------- initialize trigger events ---------------------------------- */
2678 /* initialize trigger events information */
2679 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2680 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2681 trigger_events[i][j] = FALSE;
2683 /* add trigger events from element change event properties */
2684 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2686 struct ElementInfo *ei = &element_info[i];
2688 for (j = 0; j < ei->num_change_pages; j++)
2690 if (!ei->change_page[j].can_change_or_has_action)
2693 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2695 int trigger_element = ei->change_page[j].trigger_element;
2697 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2699 if (ei->change_page[j].has_event[k])
2701 if (IS_GROUP_ELEMENT(trigger_element))
2703 struct ElementGroupInfo *group =
2704 element_info[trigger_element].group;
2706 for (l = 0; l < group->num_elements_resolved; l++)
2707 trigger_events[group->element_resolved[l]][k] = TRUE;
2709 else if (trigger_element == EL_ANY_ELEMENT)
2710 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2711 trigger_events[l][k] = TRUE;
2713 trigger_events[trigger_element][k] = TRUE;
2720 /* ---------- initialize push delay -------------------------------------- */
2722 /* initialize push delay values to default */
2723 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2725 if (!IS_CUSTOM_ELEMENT(i))
2727 /* set default push delay values (corrected since version 3.0.7-1) */
2728 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2730 element_info[i].push_delay_fixed = 2;
2731 element_info[i].push_delay_random = 8;
2735 element_info[i].push_delay_fixed = 8;
2736 element_info[i].push_delay_random = 8;
2741 /* set push delay value for certain elements from pre-defined list */
2742 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2744 int e = push_delay_list[i].element;
2746 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2747 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2750 /* set push delay value for Supaplex elements for newer engine versions */
2751 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2753 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2755 if (IS_SP_ELEMENT(i))
2757 /* set SP push delay to just enough to push under a falling zonk */
2758 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2760 element_info[i].push_delay_fixed = delay;
2761 element_info[i].push_delay_random = 0;
2766 /* ---------- initialize move stepsize ----------------------------------- */
2768 /* initialize move stepsize values to default */
2769 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2770 if (!IS_CUSTOM_ELEMENT(i))
2771 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2773 /* set move stepsize value for certain elements from pre-defined list */
2774 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2776 int e = move_stepsize_list[i].element;
2778 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2781 /* ---------- initialize collect score ----------------------------------- */
2783 /* initialize collect score values for custom elements from initial value */
2784 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2785 if (IS_CUSTOM_ELEMENT(i))
2786 element_info[i].collect_score = element_info[i].collect_score_initial;
2788 /* ---------- initialize collect count ----------------------------------- */
2790 /* initialize collect count values for non-custom elements */
2791 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2792 if (!IS_CUSTOM_ELEMENT(i))
2793 element_info[i].collect_count_initial = 0;
2795 /* add collect count values for all elements from pre-defined list */
2796 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2797 element_info[collect_count_list[i].element].collect_count_initial =
2798 collect_count_list[i].count;
2800 /* ---------- initialize access direction -------------------------------- */
2802 /* initialize access direction values to default (access from every side) */
2803 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2804 if (!IS_CUSTOM_ELEMENT(i))
2805 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2807 /* set access direction value for certain elements from pre-defined list */
2808 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2809 element_info[access_direction_list[i].element].access_direction =
2810 access_direction_list[i].direction;
2812 /* ---------- initialize explosion content ------------------------------- */
2813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2815 if (IS_CUSTOM_ELEMENT(i))
2818 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2820 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2822 element_info[i].content.e[x][y] =
2823 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2824 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2825 i == EL_PLAYER_3 ? EL_EMERALD :
2826 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2827 i == EL_MOLE ? EL_EMERALD_RED :
2828 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2829 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2830 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2831 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2832 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2833 i == EL_WALL_EMERALD ? EL_EMERALD :
2834 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2835 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2836 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2837 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2838 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2839 i == EL_WALL_PEARL ? EL_PEARL :
2840 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2845 /* ---------- initialize recursion detection ------------------------------ */
2846 recursion_loop_depth = 0;
2847 recursion_loop_detected = FALSE;
2848 recursion_loop_element = EL_UNDEFINED;
2851 int get_num_special_action(int element, int action_first, int action_last)
2853 int num_special_action = 0;
2856 for (i = action_first; i <= action_last; i++)
2858 boolean found = FALSE;
2860 for (j = 0; j < NUM_DIRECTIONS; j++)
2861 if (el_act_dir2img(element, i, j) !=
2862 el_act_dir2img(element, ACTION_DEFAULT, j))
2866 num_special_action++;
2871 return num_special_action;
2876 =============================================================================
2878 -----------------------------------------------------------------------------
2879 initialize and start new game
2880 =============================================================================
2885 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2886 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2887 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2889 boolean do_fading = (game_status == GAME_MODE_MAIN);
2893 game_status = GAME_MODE_PLAYING;
2896 InitGameControlValues();
2898 /* don't play tapes over network */
2899 network_playing = (options.network && !tape.playing);
2901 for (i = 0; i < MAX_PLAYERS; i++)
2903 struct PlayerInfo *player = &stored_player[i];
2905 player->index_nr = i;
2906 player->index_bit = (1 << i);
2907 player->element_nr = EL_PLAYER_1 + i;
2909 player->present = FALSE;
2910 player->active = FALSE;
2911 player->killed = FALSE;
2914 player->effective_action = 0;
2915 player->programmed_action = 0;
2918 player->score_final = 0;
2920 player->gems_still_needed = level.gems_needed;
2921 player->sokobanfields_still_needed = 0;
2922 player->lights_still_needed = 0;
2923 player->friends_still_needed = 0;
2925 for (j = 0; j < MAX_NUM_KEYS; j++)
2926 player->key[j] = FALSE;
2928 player->num_white_keys = 0;
2930 player->dynabomb_count = 0;
2931 player->dynabomb_size = 1;
2932 player->dynabombs_left = 0;
2933 player->dynabomb_xl = FALSE;
2935 player->MovDir = MV_NONE;
2938 player->GfxDir = MV_NONE;
2939 player->GfxAction = ACTION_DEFAULT;
2941 player->StepFrame = 0;
2943 player->use_murphy = FALSE;
2944 player->artwork_element =
2945 (level.use_artwork_element[i] ? level.artwork_element[i] :
2946 player->element_nr);
2948 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2949 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2951 player->gravity = level.initial_player_gravity[i];
2953 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2955 player->actual_frame_counter = 0;
2957 player->step_counter = 0;
2959 player->last_move_dir = MV_NONE;
2961 player->is_active = FALSE;
2963 player->is_waiting = FALSE;
2964 player->is_moving = FALSE;
2965 player->is_auto_moving = FALSE;
2966 player->is_digging = FALSE;
2967 player->is_snapping = FALSE;
2968 player->is_collecting = FALSE;
2969 player->is_pushing = FALSE;
2970 player->is_switching = FALSE;
2971 player->is_dropping = FALSE;
2972 player->is_dropping_pressed = FALSE;
2974 player->is_bored = FALSE;
2975 player->is_sleeping = FALSE;
2977 player->frame_counter_bored = -1;
2978 player->frame_counter_sleeping = -1;
2980 player->anim_delay_counter = 0;
2981 player->post_delay_counter = 0;
2983 player->dir_waiting = MV_NONE;
2984 player->action_waiting = ACTION_DEFAULT;
2985 player->last_action_waiting = ACTION_DEFAULT;
2986 player->special_action_bored = ACTION_DEFAULT;
2987 player->special_action_sleeping = ACTION_DEFAULT;
2989 player->switch_x = -1;
2990 player->switch_y = -1;
2992 player->drop_x = -1;
2993 player->drop_y = -1;
2995 player->show_envelope = 0;
2997 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2999 player->push_delay = -1; /* initialized when pushing starts */
3000 player->push_delay_value = game.initial_push_delay_value;
3002 player->drop_delay = 0;
3003 player->drop_pressed_delay = 0;
3005 player->last_jx = -1;
3006 player->last_jy = -1;
3010 player->shield_normal_time_left = 0;
3011 player->shield_deadly_time_left = 0;
3013 player->inventory_infinite_element = EL_UNDEFINED;
3014 player->inventory_size = 0;
3016 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3017 SnapField(player, 0, 0);
3019 player->LevelSolved = FALSE;
3020 player->GameOver = FALSE;
3022 player->LevelSolved_GameWon = FALSE;
3023 player->LevelSolved_GameEnd = FALSE;
3024 player->LevelSolved_PanelOff = FALSE;
3025 player->LevelSolved_SaveTape = FALSE;
3026 player->LevelSolved_SaveScore = FALSE;
3029 network_player_action_received = FALSE;
3031 #if defined(NETWORK_AVALIABLE)
3032 /* initial null action */
3033 if (network_playing)
3034 SendToServer_MovePlayer(MV_NONE);
3043 TimeLeft = level.time;
3046 ScreenMovDir = MV_NONE;
3050 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3052 AllPlayersGone = FALSE;
3054 game.yamyam_content_nr = 0;
3055 game.magic_wall_active = FALSE;
3056 game.magic_wall_time_left = 0;
3057 game.light_time_left = 0;
3058 game.timegate_time_left = 0;
3059 game.switchgate_pos = 0;
3060 game.wind_direction = level.wind_direction_initial;
3062 #if !USE_PLAYER_GRAVITY
3063 game.gravity = FALSE;
3064 game.explosions_delayed = TRUE;
3067 game.lenses_time_left = 0;
3068 game.magnify_time_left = 0;
3070 game.ball_state = level.ball_state_initial;
3071 game.ball_content_nr = 0;
3073 game.envelope_active = FALSE;
3075 /* set focus to local player for network games, else to all players */
3076 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3077 game.centered_player_nr_next = game.centered_player_nr;
3078 game.set_centered_player = FALSE;
3080 if (network_playing && tape.recording)
3082 /* store client dependent player focus when recording network games */
3083 tape.centered_player_nr_next = game.centered_player_nr_next;
3084 tape.set_centered_player = TRUE;
3087 for (i = 0; i < NUM_BELTS; i++)
3089 game.belt_dir[i] = MV_NONE;
3090 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3093 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3094 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3096 SCAN_PLAYFIELD(x, y)
3098 Feld[x][y] = level.field[x][y];
3099 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3100 ChangeDelay[x][y] = 0;
3101 ChangePage[x][y] = -1;
3102 #if USE_NEW_CUSTOM_VALUE
3103 CustomValue[x][y] = 0; /* initialized in InitField() */
3105 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3107 WasJustMoving[x][y] = 0;
3108 WasJustFalling[x][y] = 0;
3109 CheckCollision[x][y] = 0;
3110 CheckImpact[x][y] = 0;
3112 Pushed[x][y] = FALSE;
3114 ChangeCount[x][y] = 0;
3115 ChangeEvent[x][y] = -1;
3117 ExplodePhase[x][y] = 0;
3118 ExplodeDelay[x][y] = 0;
3119 ExplodeField[x][y] = EX_TYPE_NONE;
3121 RunnerVisit[x][y] = 0;
3122 PlayerVisit[x][y] = 0;
3125 GfxRandom[x][y] = INIT_GFX_RANDOM();
3126 GfxElement[x][y] = EL_UNDEFINED;
3127 GfxAction[x][y] = ACTION_DEFAULT;
3128 GfxDir[x][y] = MV_NONE;
3131 SCAN_PLAYFIELD(x, y)
3133 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3135 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3137 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3140 InitField(x, y, TRUE);
3145 for (i = 0; i < MAX_PLAYERS; i++)
3147 struct PlayerInfo *player = &stored_player[i];
3149 /* set number of special actions for bored and sleeping animation */
3150 player->num_special_action_bored =
3151 get_num_special_action(player->artwork_element,
3152 ACTION_BORING_1, ACTION_BORING_LAST);
3153 player->num_special_action_sleeping =
3154 get_num_special_action(player->artwork_element,
3155 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3158 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3159 emulate_sb ? EMU_SOKOBAN :
3160 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3162 #if USE_NEW_ALL_SLIPPERY
3163 /* initialize type of slippery elements */
3164 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3166 if (!IS_CUSTOM_ELEMENT(i))
3168 /* default: elements slip down either to the left or right randomly */
3169 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3171 /* SP style elements prefer to slip down on the left side */
3172 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3173 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3175 /* BD style elements prefer to slip down on the left side */
3176 if (game.emulation == EMU_BOULDERDASH)
3177 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3182 /* initialize explosion and ignition delay */
3183 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3185 if (!IS_CUSTOM_ELEMENT(i))
3188 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3189 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3190 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3191 int last_phase = (num_phase + 1) * delay;
3192 int half_phase = (num_phase / 2) * delay;
3194 element_info[i].explosion_delay = last_phase - 1;
3195 element_info[i].ignition_delay = half_phase;
3197 if (i == EL_BLACK_ORB)
3198 element_info[i].ignition_delay = 1;
3202 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3203 element_info[i].explosion_delay = 1;
3205 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3206 element_info[i].ignition_delay = 1;
3210 /* correct non-moving belts to start moving left */
3211 for (i = 0; i < NUM_BELTS; i++)
3212 if (game.belt_dir[i] == MV_NONE)
3213 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3215 /* check if any connected player was not found in playfield */
3216 for (i = 0; i < MAX_PLAYERS; i++)
3218 struct PlayerInfo *player = &stored_player[i];
3220 if (player->connected && !player->present)
3222 for (j = 0; j < MAX_PLAYERS; j++)
3224 struct PlayerInfo *some_player = &stored_player[j];
3225 int jx = some_player->jx, jy = some_player->jy;
3227 /* assign first free player found that is present in the playfield */
3228 if (some_player->present && !some_player->connected)
3230 player->present = TRUE;
3231 player->active = TRUE;
3233 some_player->present = FALSE;
3234 some_player->active = FALSE;
3236 player->artwork_element = some_player->artwork_element;
3238 player->block_last_field = some_player->block_last_field;
3239 player->block_delay_adjustment = some_player->block_delay_adjustment;
3241 StorePlayer[jx][jy] = player->element_nr;
3242 player->jx = player->last_jx = jx;
3243 player->jy = player->last_jy = jy;
3253 /* when playing a tape, eliminate all players who do not participate */
3255 for (i = 0; i < MAX_PLAYERS; i++)
3257 if (stored_player[i].active && !tape.player_participates[i])
3259 struct PlayerInfo *player = &stored_player[i];
3260 int jx = player->jx, jy = player->jy;
3262 player->active = FALSE;
3263 StorePlayer[jx][jy] = 0;
3264 Feld[jx][jy] = EL_EMPTY;
3268 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3270 /* when in single player mode, eliminate all but the first active player */
3272 for (i = 0; i < MAX_PLAYERS; i++)
3274 if (stored_player[i].active)
3276 for (j = i + 1; j < MAX_PLAYERS; j++)
3278 if (stored_player[j].active)
3280 struct PlayerInfo *player = &stored_player[j];
3281 int jx = player->jx, jy = player->jy;
3283 player->active = FALSE;
3284 player->present = FALSE;
3286 StorePlayer[jx][jy] = 0;
3287 Feld[jx][jy] = EL_EMPTY;
3294 /* when recording the game, store which players take part in the game */
3297 for (i = 0; i < MAX_PLAYERS; i++)
3298 if (stored_player[i].active)
3299 tape.player_participates[i] = TRUE;
3304 for (i = 0; i < MAX_PLAYERS; i++)
3306 struct PlayerInfo *player = &stored_player[i];
3308 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3313 if (local_player == player)
3314 printf("Player %d is local player.\n", i+1);
3318 if (BorderElement == EL_EMPTY)
3321 SBX_Right = lev_fieldx - SCR_FIELDX;
3323 SBY_Lower = lev_fieldy - SCR_FIELDY;
3328 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3330 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3333 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3334 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3336 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3337 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3339 /* if local player not found, look for custom element that might create
3340 the player (make some assumptions about the right custom element) */
3341 if (!local_player->present)
3343 int start_x = 0, start_y = 0;
3344 int found_rating = 0;
3345 int found_element = EL_UNDEFINED;
3346 int player_nr = local_player->index_nr;
3348 SCAN_PLAYFIELD(x, y)
3350 int element = Feld[x][y];
3355 if (level.use_start_element[player_nr] &&
3356 level.start_element[player_nr] == element &&
3363 found_element = element;
3366 if (!IS_CUSTOM_ELEMENT(element))
3369 if (CAN_CHANGE(element))
3371 for (i = 0; i < element_info[element].num_change_pages; i++)
3373 /* check for player created from custom element as single target */
3374 content = element_info[element].change_page[i].target_element;
3375 is_player = ELEM_IS_PLAYER(content);
3377 if (is_player && (found_rating < 3 || element < found_element))
3383 found_element = element;
3388 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3390 /* check for player created from custom element as explosion content */
3391 content = element_info[element].content.e[xx][yy];
3392 is_player = ELEM_IS_PLAYER(content);
3394 if (is_player && (found_rating < 2 || element < found_element))
3396 start_x = x + xx - 1;
3397 start_y = y + yy - 1;
3400 found_element = element;
3403 if (!CAN_CHANGE(element))
3406 for (i = 0; i < element_info[element].num_change_pages; i++)
3408 /* check for player created from custom element as extended target */
3410 element_info[element].change_page[i].target_content.e[xx][yy];
3412 is_player = ELEM_IS_PLAYER(content);
3414 if (is_player && (found_rating < 1 || element < found_element))
3416 start_x = x + xx - 1;
3417 start_y = y + yy - 1;
3420 found_element = element;
3426 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3427 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3430 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3431 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3436 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3437 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3438 local_player->jx - MIDPOSX);
3440 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3441 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3442 local_player->jy - MIDPOSY);
3447 if (!game.restart_level)
3448 CloseDoor(DOOR_CLOSE_1);
3451 if (level_editor_test_game)
3452 FadeSkipNextFadeIn();
3456 if (level_editor_test_game)
3457 fading = fading_none;
3459 fading = menu.destination;
3463 FadeOut(REDRAW_FIELD);
3466 FadeOut(REDRAW_FIELD);
3469 /* !!! FIX THIS (START) !!! */
3470 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3472 InitGameEngine_EM();
3474 /* blit playfield from scroll buffer to normal back buffer for fading in */
3475 BlitScreenToBitmap_EM(backbuffer);
3482 /* after drawing the level, correct some elements */
3483 if (game.timegate_time_left == 0)
3484 CloseAllOpenTimegates();
3486 /* blit playfield from scroll buffer to normal back buffer for fading in */
3487 if (setup.soft_scrolling)
3488 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3490 redraw_mask |= REDRAW_FROM_BACKBUFFER;
3492 /* !!! FIX THIS (END) !!! */
3495 FadeIn(REDRAW_FIELD);
3498 FadeIn(REDRAW_FIELD);
3503 if (!game.restart_level)
3505 /* copy default game door content to main double buffer */
3506 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3507 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3510 SetPanelBackground();
3511 SetDrawBackgroundMask(REDRAW_DOOR_1);
3513 DrawGameDoorValues();
3515 if (!game.restart_level)
3519 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3520 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3521 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3525 /* copy actual game door content to door double buffer for OpenDoor() */
3526 BlitBitmap(drawto, bitmap_db_door,
3527 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3529 OpenDoor(DOOR_OPEN_ALL);
3531 PlaySound(SND_GAME_STARTING);
3533 if (setup.sound_music)
3536 KeyboardAutoRepeatOffUnlessAutoplay();
3540 for (i = 0; i < MAX_PLAYERS; i++)
3541 printf("Player %d %sactive.\n",
3542 i + 1, (stored_player[i].active ? "" : "not "));
3553 game.restart_level = FALSE;
3556 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3558 /* this is used for non-R'n'D game engines to update certain engine values */
3560 /* needed to determine if sounds are played within the visible screen area */
3561 scroll_x = actual_scroll_x;
3562 scroll_y = actual_scroll_y;
3565 void InitMovDir(int x, int y)
3567 int i, element = Feld[x][y];
3568 static int xy[4][2] =
3575 static int direction[3][4] =
3577 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
3578 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
3579 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
3588 Feld[x][y] = EL_BUG;
3589 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3592 case EL_SPACESHIP_RIGHT:
3593 case EL_SPACESHIP_UP:
3594 case EL_SPACESHIP_LEFT:
3595 case EL_SPACESHIP_DOWN:
3596 Feld[x][y] = EL_SPACESHIP;
3597 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3600 case EL_BD_BUTTERFLY_RIGHT:
3601 case EL_BD_BUTTERFLY_UP:
3602 case EL_BD_BUTTERFLY_LEFT:
3603 case EL_BD_BUTTERFLY_DOWN:
3604 Feld[x][y] = EL_BD_BUTTERFLY;
3605 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3608 case EL_BD_FIREFLY_RIGHT:
3609 case EL_BD_FIREFLY_UP:
3610 case EL_BD_FIREFLY_LEFT:
3611 case EL_BD_FIREFLY_DOWN:
3612 Feld[x][y] = EL_BD_FIREFLY;
3613 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3616 case EL_PACMAN_RIGHT:
3618 case EL_PACMAN_LEFT:
3619 case EL_PACMAN_DOWN:
3620 Feld[x][y] = EL_PACMAN;
3621 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3624 case EL_YAMYAM_LEFT:
3625 case EL_YAMYAM_RIGHT:
3627 case EL_YAMYAM_DOWN:
3628 Feld[x][y] = EL_YAMYAM;
3629 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3632 case EL_SP_SNIKSNAK:
3633 MovDir[x][y] = MV_UP;
3636 case EL_SP_ELECTRON:
3637 MovDir[x][y] = MV_LEFT;
3644 Feld[x][y] = EL_MOLE;
3645 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3649 if (IS_CUSTOM_ELEMENT(element))
3651 struct ElementInfo *ei = &element_info[element];
3652 int move_direction_initial = ei->move_direction_initial;
3653 int move_pattern = ei->move_pattern;
3655 if (move_direction_initial == MV_START_PREVIOUS)
3657 if (MovDir[x][y] != MV_NONE)
3660 move_direction_initial = MV_START_AUTOMATIC;
3663 if (move_direction_initial == MV_START_RANDOM)
3664 MovDir[x][y] = 1 << RND(4);
3665 else if (move_direction_initial & MV_ANY_DIRECTION)
3666 MovDir[x][y] = move_direction_initial;
3667 else if (move_pattern == MV_ALL_DIRECTIONS ||
3668 move_pattern == MV_TURNING_LEFT ||
3669 move_pattern == MV_TURNING_RIGHT ||
3670 move_pattern == MV_TURNING_LEFT_RIGHT ||
3671 move_pattern == MV_TURNING_RIGHT_LEFT ||
3672 move_pattern == MV_TURNING_RANDOM)
3673 MovDir[x][y] = 1 << RND(4);
3674 else if (move_pattern == MV_HORIZONTAL)
3675 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3676 else if (move_pattern == MV_VERTICAL)
3677 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3678 else if (move_pattern & MV_ANY_DIRECTION)
3679 MovDir[x][y] = element_info[element].move_pattern;
3680 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3681 move_pattern == MV_ALONG_RIGHT_SIDE)
3683 /* use random direction as default start direction */
3684 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3685 MovDir[x][y] = 1 << RND(4);
3687 for (i = 0; i < NUM_DIRECTIONS; i++)
3689 int x1 = x + xy[i][0];
3690 int y1 = y + xy[i][1];
3692 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3694 if (move_pattern == MV_ALONG_RIGHT_SIDE)
3695 MovDir[x][y] = direction[0][i];
3697 MovDir[x][y] = direction[1][i];
3706 MovDir[x][y] = 1 << RND(4);
3708 if (element != EL_BUG &&
3709 element != EL_SPACESHIP &&
3710 element != EL_BD_BUTTERFLY &&
3711 element != EL_BD_FIREFLY)
3714 for (i = 0; i < NUM_DIRECTIONS; i++)
3716 int x1 = x + xy[i][0];
3717 int y1 = y + xy[i][1];
3719 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3721 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3723 MovDir[x][y] = direction[0][i];
3726 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3727 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3729 MovDir[x][y] = direction[1][i];
3738 GfxDir[x][y] = MovDir[x][y];
3741 void InitAmoebaNr(int x, int y)
3744 int group_nr = AmoebeNachbarNr(x, y);
3748 for (i = 1; i < MAX_NUM_AMOEBA; i++)
3750 if (AmoebaCnt[i] == 0)
3758 AmoebaNr[x][y] = group_nr;
3759 AmoebaCnt[group_nr]++;
3760 AmoebaCnt2[group_nr]++;
3763 static void PlayerWins(struct PlayerInfo *player)
3765 player->LevelSolved = TRUE;
3766 player->GameOver = TRUE;
3768 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3769 level.native_em_level->lev->score : player->score);
3774 static int time, time_final;
3775 static int score, score_final;
3776 static int game_over_delay_1 = 0;
3777 static int game_over_delay_2 = 0;
3778 int game_over_delay_value_1 = 50;
3779 int game_over_delay_value_2 = 50;
3781 if (!local_player->LevelSolved_GameWon)
3785 /* do not start end game actions before the player stops moving (to exit) */
3786 if (local_player->MovPos)
3789 local_player->LevelSolved_GameWon = TRUE;
3790 local_player->LevelSolved_SaveTape = tape.recording;
3791 local_player->LevelSolved_SaveScore = !tape.playing;
3793 if (tape.auto_play) /* tape might already be stopped here */
3794 tape.auto_play_level_solved = TRUE;
3800 game_over_delay_1 = game_over_delay_value_1;
3801 game_over_delay_2 = game_over_delay_value_2;
3803 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3804 score = score_final = local_player->score_final;
3809 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3811 else if (level.time == 0 && TimePlayed < 999)
3814 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3817 local_player->score_final = score_final;
3819 if (level_editor_test_game)
3822 score = score_final;
3825 game_control_value[GAME_CONTROL_TIME] = time;
3826 game_control_value[GAME_CONTROL_SCORE] = score;
3828 DisplayGameControlValues();
3830 DrawGameValue_Time(time);
3831 DrawGameValue_Score(score);
3835 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3837 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3839 /* close exit door after last player */
3840 if ((AllPlayersGone &&
3841 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3842 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3843 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3844 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3845 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3847 int element = Feld[ExitX][ExitY];
3850 if (element == EL_EM_EXIT_OPEN ||
3851 element == EL_EM_STEEL_EXIT_OPEN)
3858 Feld[ExitX][ExitY] =
3859 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3860 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3861 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3862 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3863 EL_EM_STEEL_EXIT_CLOSING);
3865 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3869 /* player disappears */
3870 DrawLevelField(ExitX, ExitY);
3873 for (i = 0; i < MAX_PLAYERS; i++)
3875 struct PlayerInfo *player = &stored_player[i];
3877 if (player->present)
3879 RemovePlayer(player);
3881 /* player disappears */
3882 DrawLevelField(player->jx, player->jy);
3887 PlaySound(SND_GAME_WINNING);
3890 if (game_over_delay_1 > 0)
3892 game_over_delay_1--;
3897 if (time != time_final)
3899 int time_to_go = ABS(time_final - time);
3900 int time_count_dir = (time < time_final ? +1 : -1);
3901 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3903 time += time_count_steps * time_count_dir;
3904 score += time_count_steps * level.score[SC_TIME_BONUS];
3907 game_control_value[GAME_CONTROL_TIME] = time;
3908 game_control_value[GAME_CONTROL_SCORE] = score;
3910 DisplayGameControlValues();
3912 DrawGameValue_Time(time);
3913 DrawGameValue_Score(score);
3916 if (time == time_final)
3917 StopSound(SND_GAME_LEVELTIME_BONUS);
3918 else if (setup.sound_loops)
3919 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3921 PlaySound(SND_GAME_LEVELTIME_BONUS);
3926 local_player->LevelSolved_PanelOff = TRUE;
3928 if (game_over_delay_2 > 0)
3930 game_over_delay_2--;
3943 boolean raise_level = FALSE;
3945 local_player->LevelSolved_GameEnd = TRUE;
3947 CloseDoor(DOOR_CLOSE_1);
3949 if (local_player->LevelSolved_SaveTape)
3956 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3958 SaveTape(tape.level_nr); /* ask to save tape */
3962 if (level_editor_test_game)
3964 game_status = GAME_MODE_MAIN;
3967 DrawAndFadeInMainMenu(REDRAW_FIELD);
3975 if (!local_player->LevelSolved_SaveScore)
3978 FadeOut(REDRAW_FIELD);
3981 game_status = GAME_MODE_MAIN;
3983 DrawAndFadeInMainMenu(REDRAW_FIELD);
3988 if (level_nr == leveldir_current->handicap_level)
3990 leveldir_current->handicap_level++;
3991 SaveLevelSetup_SeriesInfo();
3994 if (level_nr < leveldir_current->last_level)
3995 raise_level = TRUE; /* advance to next level */
3997 if ((hi_pos = NewHiScore()) >= 0)
3999 game_status = GAME_MODE_SCORES;
4001 DrawHallOfFame(hi_pos);
4012 FadeOut(REDRAW_FIELD);
4015 game_status = GAME_MODE_MAIN;
4023 DrawAndFadeInMainMenu(REDRAW_FIELD);
4032 LoadScore(level_nr);
4034 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4035 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4038 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4040 if (local_player->score_final > highscore[k].Score)
4042 /* player has made it to the hall of fame */
4044 if (k < MAX_SCORE_ENTRIES - 1)
4046 int m = MAX_SCORE_ENTRIES - 1;
4049 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4050 if (strEqual(setup.player_name, highscore[l].Name))
4052 if (m == k) /* player's new highscore overwrites his old one */
4056 for (l = m; l > k; l--)
4058 strcpy(highscore[l].Name, highscore[l - 1].Name);
4059 highscore[l].Score = highscore[l - 1].Score;
4066 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4067 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4068 highscore[k].Score = local_player->score_final;
4074 else if (!strncmp(setup.player_name, highscore[k].Name,
4075 MAX_PLAYER_NAME_LEN))
4076 break; /* player already there with a higher score */
4082 SaveScore(level_nr);
4087 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4089 int element = Feld[x][y];
4090 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4091 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4092 int horiz_move = (dx != 0);
4093 int sign = (horiz_move ? dx : dy);
4094 int step = sign * element_info[element].move_stepsize;
4096 /* special values for move stepsize for spring and things on conveyor belt */
4099 if (CAN_FALL(element) &&
4100 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4101 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4102 else if (element == EL_SPRING)
4103 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4109 inline static int getElementMoveStepsize(int x, int y)
4111 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4114 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4116 if (player->GfxAction != action || player->GfxDir != dir)
4119 printf("Player frame reset! (%d => %d, %d => %d)\n",
4120 player->GfxAction, action, player->GfxDir, dir);
4123 player->GfxAction = action;
4124 player->GfxDir = dir;
4126 player->StepFrame = 0;
4130 #if USE_GFX_RESET_GFX_ANIMATION
4131 static void ResetGfxFrame(int x, int y, boolean redraw)
4133 int element = Feld[x][y];
4134 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4135 int last_gfx_frame = GfxFrame[x][y];
4137 if (graphic_info[graphic].anim_global_sync)
4138 GfxFrame[x][y] = FrameCounter;
4139 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4140 GfxFrame[x][y] = CustomValue[x][y];
4141 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4142 GfxFrame[x][y] = element_info[element].collect_score;
4143 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4144 GfxFrame[x][y] = ChangeDelay[x][y];
4146 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4147 DrawLevelGraphicAnimation(x, y, graphic);
4151 static void ResetGfxAnimation(int x, int y)
4153 GfxAction[x][y] = ACTION_DEFAULT;
4154 GfxDir[x][y] = MovDir[x][y];
4157 #if USE_GFX_RESET_GFX_ANIMATION
4158 ResetGfxFrame(x, y, FALSE);
4162 static void ResetRandomAnimationValue(int x, int y)
4164 GfxRandom[x][y] = INIT_GFX_RANDOM();
4167 void InitMovingField(int x, int y, int direction)
4169 int element = Feld[x][y];
4170 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4171 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4174 boolean is_moving_before, is_moving_after;
4176 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4179 /* check if element was/is moving or being moved before/after mode change */
4182 is_moving_before = (WasJustMoving[x][y] != 0);
4184 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4185 is_moving_before = WasJustMoving[x][y];
4188 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4190 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4192 /* reset animation only for moving elements which change direction of moving
4193 or which just started or stopped moving
4194 (else CEs with property "can move" / "not moving" are reset each frame) */
4195 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4197 if (is_moving_before != is_moving_after ||
4198 direction != MovDir[x][y])
4199 ResetGfxAnimation(x, y);
4201 if ((is_moving_before || is_moving_after) && !continues_moving)
4202 ResetGfxAnimation(x, y);
4205 if (!continues_moving)
4206 ResetGfxAnimation(x, y);
4209 MovDir[x][y] = direction;
4210 GfxDir[x][y] = direction;
4212 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4213 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4214 direction == MV_DOWN && CAN_FALL(element) ?
4215 ACTION_FALLING : ACTION_MOVING);
4217 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4218 ACTION_FALLING : ACTION_MOVING);
4221 /* this is needed for CEs with property "can move" / "not moving" */
4223 if (is_moving_after)
4225 if (Feld[newx][newy] == EL_EMPTY)
4226 Feld[newx][newy] = EL_BLOCKED;
4228 MovDir[newx][newy] = MovDir[x][y];
4230 #if USE_NEW_CUSTOM_VALUE
4231 CustomValue[newx][newy] = CustomValue[x][y];
4234 GfxFrame[newx][newy] = GfxFrame[x][y];
4235 GfxRandom[newx][newy] = GfxRandom[x][y];
4236 GfxAction[newx][newy] = GfxAction[x][y];
4237 GfxDir[newx][newy] = GfxDir[x][y];
4241 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4243 int direction = MovDir[x][y];
4244 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4245 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4251 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4253 int oldx = x, oldy = y;
4254 int direction = MovDir[x][y];
4256 if (direction == MV_LEFT)
4258 else if (direction == MV_RIGHT)
4260 else if (direction == MV_UP)
4262 else if (direction == MV_DOWN)
4265 *comes_from_x = oldx;
4266 *comes_from_y = oldy;
4269 int MovingOrBlocked2Element(int x, int y)
4271 int element = Feld[x][y];
4273 if (element == EL_BLOCKED)
4277 Blocked2Moving(x, y, &oldx, &oldy);
4278 return Feld[oldx][oldy];
4284 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4286 /* like MovingOrBlocked2Element(), but if element is moving
4287 and (x,y) is the field the moving element is just leaving,
4288 return EL_BLOCKED instead of the element value */
4289 int element = Feld[x][y];
4291 if (IS_MOVING(x, y))
4293 if (element == EL_BLOCKED)
4297 Blocked2Moving(x, y, &oldx, &oldy);
4298 return Feld[oldx][oldy];
4307 static void RemoveField(int x, int y)
4309 Feld[x][y] = EL_EMPTY;
4315 #if USE_NEW_CUSTOM_VALUE
4316 CustomValue[x][y] = 0;
4320 ChangeDelay[x][y] = 0;
4321 ChangePage[x][y] = -1;
4322 Pushed[x][y] = FALSE;
4325 ExplodeField[x][y] = EX_TYPE_NONE;
4328 GfxElement[x][y] = EL_UNDEFINED;
4329 GfxAction[x][y] = ACTION_DEFAULT;
4330 GfxDir[x][y] = MV_NONE;
4333 void RemoveMovingField(int x, int y)
4335 int oldx = x, oldy = y, newx = x, newy = y;
4336 int element = Feld[x][y];
4337 int next_element = EL_UNDEFINED;
4339 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4342 if (IS_MOVING(x, y))
4344 Moving2Blocked(x, y, &newx, &newy);
4346 if (Feld[newx][newy] != EL_BLOCKED)
4348 /* element is moving, but target field is not free (blocked), but
4349 already occupied by something different (example: acid pool);
4350 in this case, only remove the moving field, but not the target */
4352 RemoveField(oldx, oldy);
4354 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4356 DrawLevelField(oldx, oldy);
4361 else if (element == EL_BLOCKED)
4363 Blocked2Moving(x, y, &oldx, &oldy);
4364 if (!IS_MOVING(oldx, oldy))
4368 if (element == EL_BLOCKED &&
4369 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4370 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4371 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4372 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4373 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4374 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4375 next_element = get_next_element(Feld[oldx][oldy]);
4377 RemoveField(oldx, oldy);
4378 RemoveField(newx, newy);
4380 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4382 if (next_element != EL_UNDEFINED)
4383 Feld[oldx][oldy] = next_element;
4385 DrawLevelField(oldx, oldy);
4386 DrawLevelField(newx, newy);
4389 void DrawDynamite(int x, int y)
4391 int sx = SCREENX(x), sy = SCREENY(y);
4392 int graphic = el2img(Feld[x][y]);
4395 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4398 if (IS_WALKABLE_INSIDE(Back[x][y]))
4402 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4403 else if (Store[x][y])
4404 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4406 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4408 if (Back[x][y] || Store[x][y])
4409 DrawGraphicThruMask(sx, sy, graphic, frame);
4411 DrawGraphic(sx, sy, graphic, frame);
4414 void CheckDynamite(int x, int y)
4416 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4420 if (MovDelay[x][y] != 0)
4423 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4429 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4434 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4436 boolean num_checked_players = 0;
4439 for (i = 0; i < MAX_PLAYERS; i++)
4441 if (stored_player[i].active)
4443 int sx = stored_player[i].jx;
4444 int sy = stored_player[i].jy;
4446 if (num_checked_players == 0)
4453 *sx1 = MIN(*sx1, sx);
4454 *sy1 = MIN(*sy1, sy);
4455 *sx2 = MAX(*sx2, sx);
4456 *sy2 = MAX(*sy2, sy);
4459 num_checked_players++;
4464 static boolean checkIfAllPlayersFitToScreen_RND()
4466 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4468 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4470 return (sx2 - sx1 < SCR_FIELDX &&
4471 sy2 - sy1 < SCR_FIELDY);
4474 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4476 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4478 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4480 *sx = (sx1 + sx2) / 2;
4481 *sy = (sy1 + sy2) / 2;
4484 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4485 boolean center_screen, boolean quick_relocation)
4487 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4488 boolean no_delay = (tape.warp_forward);
4489 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4490 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4492 if (quick_relocation)
4494 int offset = (setup.scroll_delay ? 3 : 0);
4496 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4498 if (!level.shifted_relocation || center_screen)
4500 /* quick relocation (without scrolling), with centering of screen */
4502 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4503 x > SBX_Right + MIDPOSX ? SBX_Right :
4506 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4507 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4512 /* quick relocation (without scrolling), but do not center screen */
4514 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4515 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4518 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4519 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4522 int offset_x = x + (scroll_x - center_scroll_x);
4523 int offset_y = y + (scroll_y - center_scroll_y);
4525 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4526 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4527 offset_x - MIDPOSX);
4529 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4530 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4531 offset_y - MIDPOSY);
4536 /* quick relocation (without scrolling), inside visible screen area */
4538 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
4539 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4540 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4542 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
4543 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4544 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4546 /* don't scroll over playfield boundaries */
4547 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4548 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4550 /* don't scroll over playfield boundaries */
4551 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4552 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4555 RedrawPlayfield(TRUE, 0,0,0,0);
4560 int scroll_xx, scroll_yy;
4562 if (!level.shifted_relocation || center_screen)
4564 /* visible relocation (with scrolling), with centering of screen */
4566 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4567 x > SBX_Right + MIDPOSX ? SBX_Right :
4570 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4571 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4576 /* visible relocation (with scrolling), but do not center screen */
4578 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4579 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4582 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4583 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4586 int offset_x = x + (scroll_x - center_scroll_x);
4587 int offset_y = y + (scroll_y - center_scroll_y);
4589 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4590 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4591 offset_x - MIDPOSX);
4593 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4594 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4595 offset_y - MIDPOSY);
4600 /* visible relocation (with scrolling), with centering of screen */
4602 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
4603 x > SBX_Right + MIDPOSX ? SBX_Right :
4606 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4607 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4611 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4613 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4616 int fx = FX, fy = FY;
4618 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4619 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4621 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4627 fx += dx * TILEX / 2;
4628 fy += dy * TILEY / 2;
4630 ScrollLevel(dx, dy);
4633 /* scroll in two steps of half tile size to make things smoother */
4634 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4636 Delay(wait_delay_value);
4638 /* scroll second step to align at full tile size */
4640 Delay(wait_delay_value);
4645 Delay(wait_delay_value);
4649 void RelocatePlayer(int jx, int jy, int el_player_raw)
4651 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4652 int player_nr = GET_PLAYER_NR(el_player);
4653 struct PlayerInfo *player = &stored_player[player_nr];
4654 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4655 boolean no_delay = (tape.warp_forward);
4656 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4657 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4658 int old_jx = player->jx;
4659 int old_jy = player->jy;
4660 int old_element = Feld[old_jx][old_jy];
4661 int element = Feld[jx][jy];
4662 boolean player_relocated = (old_jx != jx || old_jy != jy);
4664 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4665 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
4666 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4667 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
4668 int leave_side_horiz = move_dir_horiz;
4669 int leave_side_vert = move_dir_vert;
4670 int enter_side = enter_side_horiz | enter_side_vert;
4671 int leave_side = leave_side_horiz | leave_side_vert;
4673 if (player->GameOver) /* do not reanimate dead player */
4676 if (!player_relocated) /* no need to relocate the player */
4679 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
4681 RemoveField(jx, jy); /* temporarily remove newly placed player */
4682 DrawLevelField(jx, jy);
4685 if (player->present)
4687 while (player->MovPos)
4689 ScrollPlayer(player, SCROLL_GO_ON);
4690 ScrollScreen(NULL, SCROLL_GO_ON);
4692 AdvanceFrameAndPlayerCounters(player->index_nr);
4697 Delay(wait_delay_value);
4700 DrawPlayer(player); /* needed here only to cleanup last field */
4701 DrawLevelField(player->jx, player->jy); /* remove player graphic */
4703 player->is_moving = FALSE;
4706 if (IS_CUSTOM_ELEMENT(old_element))
4707 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4709 player->index_bit, leave_side);
4711 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4713 player->index_bit, leave_side);
4715 Feld[jx][jy] = el_player;
4716 InitPlayerField(jx, jy, el_player, TRUE);
4718 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4720 Feld[jx][jy] = element;
4721 InitField(jx, jy, FALSE);
4724 /* only visually relocate centered player */
4725 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4726 FALSE, level.instant_relocation);
4728 TestIfPlayerTouchesBadThing(jx, jy);
4729 TestIfPlayerTouchesCustomElement(jx, jy);
4731 if (IS_CUSTOM_ELEMENT(element))
4732 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4733 player->index_bit, enter_side);
4735 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4736 player->index_bit, enter_side);
4739 void Explode(int ex, int ey, int phase, int mode)
4745 /* !!! eliminate this variable !!! */
4746 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4748 if (game.explosions_delayed)
4750 ExplodeField[ex][ey] = mode;
4754 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
4756 int center_element = Feld[ex][ey];
4757 int artwork_element, explosion_element; /* set these values later */
4760 /* --- This is only really needed (and now handled) in "Impact()". --- */
4761 /* do not explode moving elements that left the explode field in time */
4762 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4763 center_element == EL_EMPTY &&
4764 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4769 /* !!! at this place, the center element may be EL_BLOCKED !!! */
4770 if (mode == EX_TYPE_NORMAL ||
4771 mode == EX_TYPE_CENTER ||
4772 mode == EX_TYPE_CROSS)
4773 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4776 /* remove things displayed in background while burning dynamite */
4777 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4780 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4782 /* put moving element to center field (and let it explode there) */
4783 center_element = MovingOrBlocked2Element(ex, ey);
4784 RemoveMovingField(ex, ey);
4785 Feld[ex][ey] = center_element;
4788 /* now "center_element" is finally determined -- set related values now */
4789 artwork_element = center_element; /* for custom player artwork */
4790 explosion_element = center_element; /* for custom player artwork */
4792 if (IS_PLAYER(ex, ey))
4794 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4796 artwork_element = stored_player[player_nr].artwork_element;
4798 if (level.use_explosion_element[player_nr])
4800 explosion_element = level.explosion_element[player_nr];
4801 artwork_element = explosion_element;
4806 if (mode == EX_TYPE_NORMAL ||
4807 mode == EX_TYPE_CENTER ||
4808 mode == EX_TYPE_CROSS)
4809 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4812 last_phase = element_info[explosion_element].explosion_delay + 1;
4814 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4816 int xx = x - ex + 1;
4817 int yy = y - ey + 1;
4820 if (!IN_LEV_FIELD(x, y) ||
4821 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4822 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
4825 element = Feld[x][y];
4827 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4829 element = MovingOrBlocked2Element(x, y);
4831 if (!IS_EXPLOSION_PROOF(element))
4832 RemoveMovingField(x, y);
4835 /* indestructible elements can only explode in center (but not flames) */
4836 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4837 mode == EX_TYPE_BORDER)) ||
4838 element == EL_FLAMES)
4841 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4842 behaviour, for example when touching a yamyam that explodes to rocks
4843 with active deadly shield, a rock is created under the player !!! */
4844 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4846 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4847 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4848 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4850 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4853 if (IS_ACTIVE_BOMB(element))
4855 /* re-activate things under the bomb like gate or penguin */
4856 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4863 /* save walkable background elements while explosion on same tile */
4864 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4865 (x != ex || y != ey || mode == EX_TYPE_BORDER))
4866 Back[x][y] = element;
4868 /* ignite explodable elements reached by other explosion */
4869 if (element == EL_EXPLOSION)
4870 element = Store2[x][y];
4872 if (AmoebaNr[x][y] &&
4873 (element == EL_AMOEBA_FULL ||
4874 element == EL_BD_AMOEBA ||
4875 element == EL_AMOEBA_GROWING))
4877 AmoebaCnt[AmoebaNr[x][y]]--;
4878 AmoebaCnt2[AmoebaNr[x][y]]--;
4883 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4885 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4887 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4889 if (PLAYERINFO(ex, ey)->use_murphy)
4890 Store[x][y] = EL_EMPTY;
4893 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4894 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4895 else if (ELEM_IS_PLAYER(center_element))
4896 Store[x][y] = EL_EMPTY;
4897 else if (center_element == EL_YAMYAM)
4898 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4899 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4900 Store[x][y] = element_info[center_element].content.e[xx][yy];
4902 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4903 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4904 otherwise) -- FIX THIS !!! */
4905 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4906 Store[x][y] = element_info[element].content.e[1][1];
4908 else if (!CAN_EXPLODE(element))
4909 Store[x][y] = element_info[element].content.e[1][1];
4912 Store[x][y] = EL_EMPTY;
4914 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4915 center_element == EL_AMOEBA_TO_DIAMOND)
4916 Store2[x][y] = element;
4918 Feld[x][y] = EL_EXPLOSION;
4919 GfxElement[x][y] = artwork_element;
4921 ExplodePhase[x][y] = 1;
4922 ExplodeDelay[x][y] = last_phase;
4927 if (center_element == EL_YAMYAM)
4928 game.yamyam_content_nr =
4929 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4941 GfxFrame[x][y] = 0; /* restart explosion animation */
4943 last_phase = ExplodeDelay[x][y];
4945 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4949 /* activate this even in non-DEBUG version until cause for crash in
4950 getGraphicAnimationFrame() (see below) is found and eliminated */
4956 /* this can happen if the player leaves an explosion just in time */
4957 if (GfxElement[x][y] == EL_UNDEFINED)
4958 GfxElement[x][y] = EL_EMPTY;
4960 if (GfxElement[x][y] == EL_UNDEFINED)
4963 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4964 printf("Explode(): This should never happen!\n");
4967 GfxElement[x][y] = EL_EMPTY;
4973 border_element = Store2[x][y];
4974 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4975 border_element = StorePlayer[x][y];
4977 if (phase == element_info[border_element].ignition_delay ||
4978 phase == last_phase)
4980 boolean border_explosion = FALSE;
4982 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4983 !PLAYER_EXPLOSION_PROTECTED(x, y))
4985 KillPlayerUnlessExplosionProtected(x, y);
4986 border_explosion = TRUE;
4988 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4990 Feld[x][y] = Store2[x][y];
4993 border_explosion = TRUE;
4995 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4997 AmoebeUmwandeln(x, y);
4999 border_explosion = TRUE;
5002 /* if an element just explodes due to another explosion (chain-reaction),
5003 do not immediately end the new explosion when it was the last frame of
5004 the explosion (as it would be done in the following "if"-statement!) */
5005 if (border_explosion && phase == last_phase)
5009 if (phase == last_phase)
5013 element = Feld[x][y] = Store[x][y];
5014 Store[x][y] = Store2[x][y] = 0;
5015 GfxElement[x][y] = EL_UNDEFINED;
5017 /* player can escape from explosions and might therefore be still alive */
5018 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5019 element <= EL_PLAYER_IS_EXPLODING_4)
5021 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5022 int explosion_element = EL_PLAYER_1 + player_nr;
5023 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5024 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5026 if (level.use_explosion_element[player_nr])
5027 explosion_element = level.explosion_element[player_nr];
5029 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5030 element_info[explosion_element].content.e[xx][yy]);
5033 /* restore probably existing indestructible background element */
5034 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5035 element = Feld[x][y] = Back[x][y];
5038 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5039 GfxDir[x][y] = MV_NONE;
5040 ChangeDelay[x][y] = 0;
5041 ChangePage[x][y] = -1;
5043 #if USE_NEW_CUSTOM_VALUE
5044 CustomValue[x][y] = 0;
5047 InitField_WithBug2(x, y, FALSE);
5049 DrawLevelField(x, y);
5051 TestIfElementTouchesCustomElement(x, y);
5053 if (GFX_CRUMBLED(element))
5054 DrawLevelFieldCrumbledSandNeighbours(x, y);
5056 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5057 StorePlayer[x][y] = 0;
5059 if (ELEM_IS_PLAYER(element))
5060 RelocatePlayer(x, y, element);
5062 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5064 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5065 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5068 DrawLevelFieldCrumbledSand(x, y);
5070 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5072 DrawLevelElement(x, y, Back[x][y]);
5073 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5075 else if (IS_WALKABLE_UNDER(Back[x][y]))
5077 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5078 DrawLevelElementThruMask(x, y, Back[x][y]);
5080 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5081 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5085 void DynaExplode(int ex, int ey)
5088 int dynabomb_element = Feld[ex][ey];
5089 int dynabomb_size = 1;
5090 boolean dynabomb_xl = FALSE;
5091 struct PlayerInfo *player;
5092 static int xy[4][2] =
5100 if (IS_ACTIVE_BOMB(dynabomb_element))
5102 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5103 dynabomb_size = player->dynabomb_size;
5104 dynabomb_xl = player->dynabomb_xl;
5105 player->dynabombs_left++;
5108 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5110 for (i = 0; i < NUM_DIRECTIONS; i++)
5112 for (j = 1; j <= dynabomb_size; j++)
5114 int x = ex + j * xy[i][0];
5115 int y = ey + j * xy[i][1];
5118 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5121 element = Feld[x][y];
5123 /* do not restart explosions of fields with active bombs */
5124 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5127 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5129 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5130 !IS_DIGGABLE(element) && !dynabomb_xl)
5136 void Bang(int x, int y)
5138 int element = MovingOrBlocked2Element(x, y);
5139 int explosion_type = EX_TYPE_NORMAL;
5141 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5143 struct PlayerInfo *player = PLAYERINFO(x, y);
5145 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5146 player->element_nr);
5148 if (level.use_explosion_element[player->index_nr])
5150 int explosion_element = level.explosion_element[player->index_nr];
5152 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5153 explosion_type = EX_TYPE_CROSS;
5154 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5155 explosion_type = EX_TYPE_CENTER;
5163 case EL_BD_BUTTERFLY:
5166 case EL_DARK_YAMYAM:
5170 RaiseScoreElement(element);
5173 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5174 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5175 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5176 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5177 case EL_DYNABOMB_INCREASE_NUMBER:
5178 case EL_DYNABOMB_INCREASE_SIZE:
5179 case EL_DYNABOMB_INCREASE_POWER:
5180 explosion_type = EX_TYPE_DYNA;
5183 case EL_DC_LANDMINE:
5185 case EL_EM_EXIT_OPEN:
5186 case EL_EM_STEEL_EXIT_OPEN:
5188 explosion_type = EX_TYPE_CENTER;
5193 case EL_LAMP_ACTIVE:
5194 case EL_AMOEBA_TO_DIAMOND:
5195 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5196 explosion_type = EX_TYPE_CENTER;
5200 if (element_info[element].explosion_type == EXPLODES_CROSS)
5201 explosion_type = EX_TYPE_CROSS;
5202 else if (element_info[element].explosion_type == EXPLODES_1X1)
5203 explosion_type = EX_TYPE_CENTER;
5207 if (explosion_type == EX_TYPE_DYNA)
5210 Explode(x, y, EX_PHASE_START, explosion_type);
5212 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5215 void SplashAcid(int x, int y)
5217 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5218 (!IN_LEV_FIELD(x - 1, y - 2) ||
5219 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5220 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5222 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5223 (!IN_LEV_FIELD(x + 1, y - 2) ||
5224 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5225 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5227 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5230 static void InitBeltMovement()
5232 static int belt_base_element[4] =
5234 EL_CONVEYOR_BELT_1_LEFT,
5235 EL_CONVEYOR_BELT_2_LEFT,
5236 EL_CONVEYOR_BELT_3_LEFT,
5237 EL_CONVEYOR_BELT_4_LEFT
5239 static int belt_base_active_element[4] =
5241 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5242 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5243 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5244 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5249 /* set frame order for belt animation graphic according to belt direction */
5250 for (i = 0; i < NUM_BELTS; i++)
5254 for (j = 0; j < NUM_BELT_PARTS; j++)
5256 int element = belt_base_active_element[belt_nr] + j;
5257 int graphic = el2img(element);
5259 if (game.belt_dir[i] == MV_LEFT)
5260 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5262 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5266 SCAN_PLAYFIELD(x, y)
5268 int element = Feld[x][y];
5270 for (i = 0; i < NUM_BELTS; i++)
5272 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5274 int e_belt_nr = getBeltNrFromBeltElement(element);
5277 if (e_belt_nr == belt_nr)
5279 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5281 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5288 static void ToggleBeltSwitch(int x, int y)
5290 static int belt_base_element[4] =
5292 EL_CONVEYOR_BELT_1_LEFT,
5293 EL_CONVEYOR_BELT_2_LEFT,
5294 EL_CONVEYOR_BELT_3_LEFT,
5295 EL_CONVEYOR_BELT_4_LEFT
5297 static int belt_base_active_element[4] =
5299 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5300 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5301 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5302 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5304 static int belt_base_switch_element[4] =
5306 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5307 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5308 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5309 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5311 static int belt_move_dir[4] =
5319 int element = Feld[x][y];
5320 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5321 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5322 int belt_dir = belt_move_dir[belt_dir_nr];
5325 if (!IS_BELT_SWITCH(element))
5328 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5329 game.belt_dir[belt_nr] = belt_dir;
5331 if (belt_dir_nr == 3)
5334 /* set frame order for belt animation graphic according to belt direction */
5335 for (i = 0; i < NUM_BELT_PARTS; i++)
5337 int element = belt_base_active_element[belt_nr] + i;
5338 int graphic = el2img(element);
5340 if (belt_dir == MV_LEFT)
5341 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5343 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
5346 SCAN_PLAYFIELD(xx, yy)
5348 int element = Feld[xx][yy];
5350 if (IS_BELT_SWITCH(element))
5352 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5354 if (e_belt_nr == belt_nr)
5356 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5357 DrawLevelField(xx, yy);
5360 else if (IS_BELT(element) && belt_dir != MV_NONE)
5362 int e_belt_nr = getBeltNrFromBeltElement(element);
5364 if (e_belt_nr == belt_nr)
5366 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5368 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5369 DrawLevelField(xx, yy);
5372 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5374 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5376 if (e_belt_nr == belt_nr)
5378 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5380 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5381 DrawLevelField(xx, yy);
5387 static void ToggleSwitchgateSwitch(int x, int y)
5391 game.switchgate_pos = !game.switchgate_pos;
5393 SCAN_PLAYFIELD(xx, yy)
5395 int element = Feld[xx][yy];
5397 #if !USE_BOTH_SWITCHGATE_SWITCHES
5398 if (element == EL_SWITCHGATE_SWITCH_UP ||
5399 element == EL_SWITCHGATE_SWITCH_DOWN)
5401 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5402 DrawLevelField(xx, yy);
5404 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5405 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5407 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5408 DrawLevelField(xx, yy);
5411 if (element == EL_SWITCHGATE_SWITCH_UP)
5413 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5414 DrawLevelField(xx, yy);
5416 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5418 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5419 DrawLevelField(xx, yy);
5421 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5423 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5424 DrawLevelField(xx, yy);
5426 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5428 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5429 DrawLevelField(xx, yy);
5432 else if (element == EL_SWITCHGATE_OPEN ||
5433 element == EL_SWITCHGATE_OPENING)
5435 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5437 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5439 else if (element == EL_SWITCHGATE_CLOSED ||
5440 element == EL_SWITCHGATE_CLOSING)
5442 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5444 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5449 static int getInvisibleActiveFromInvisibleElement(int element)
5451 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5452 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5453 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5457 static int getInvisibleFromInvisibleActiveElement(int element)
5459 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5460 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5461 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5465 static void RedrawAllLightSwitchesAndInvisibleElements()
5469 SCAN_PLAYFIELD(x, y)
5471 int element = Feld[x][y];
5473 if (element == EL_LIGHT_SWITCH &&
5474 game.light_time_left > 0)
5476 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5477 DrawLevelField(x, y);
5479 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5480 game.light_time_left == 0)
5482 Feld[x][y] = EL_LIGHT_SWITCH;
5483 DrawLevelField(x, y);
5485 else if (element == EL_EMC_DRIPPER &&
5486 game.light_time_left > 0)
5488 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5489 DrawLevelField(x, y);
5491 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5492 game.light_time_left == 0)
5494 Feld[x][y] = EL_EMC_DRIPPER;
5495 DrawLevelField(x, y);
5497 else if (element == EL_INVISIBLE_STEELWALL ||
5498 element == EL_INVISIBLE_WALL ||
5499 element == EL_INVISIBLE_SAND)
5501 if (game.light_time_left > 0)
5502 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5504 DrawLevelField(x, y);
5506 /* uncrumble neighbour fields, if needed */
5507 if (element == EL_INVISIBLE_SAND)
5508 DrawLevelFieldCrumbledSandNeighbours(x, y);
5510 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5511 element == EL_INVISIBLE_WALL_ACTIVE ||
5512 element == EL_INVISIBLE_SAND_ACTIVE)
5514 if (game.light_time_left == 0)
5515 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5517 DrawLevelField(x, y);
5519 /* re-crumble neighbour fields, if needed */
5520 if (element == EL_INVISIBLE_SAND)
5521 DrawLevelFieldCrumbledSandNeighbours(x, y);
5526 static void RedrawAllInvisibleElementsForLenses()
5530 SCAN_PLAYFIELD(x, y)
5532 int element = Feld[x][y];
5534 if (element == EL_EMC_DRIPPER &&
5535 game.lenses_time_left > 0)
5537 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5538 DrawLevelField(x, y);
5540 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5541 game.lenses_time_left == 0)
5543 Feld[x][y] = EL_EMC_DRIPPER;
5544 DrawLevelField(x, y);
5546 else if (element == EL_INVISIBLE_STEELWALL ||
5547 element == EL_INVISIBLE_WALL ||
5548 element == EL_INVISIBLE_SAND)
5550 if (game.lenses_time_left > 0)
5551 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5553 DrawLevelField(x, y);
5555 /* uncrumble neighbour fields, if needed */
5556 if (element == EL_INVISIBLE_SAND)
5557 DrawLevelFieldCrumbledSandNeighbours(x, y);
5559 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5560 element == EL_INVISIBLE_WALL_ACTIVE ||
5561 element == EL_INVISIBLE_SAND_ACTIVE)
5563 if (game.lenses_time_left == 0)
5564 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5566 DrawLevelField(x, y);
5568 /* re-crumble neighbour fields, if needed */
5569 if (element == EL_INVISIBLE_SAND)
5570 DrawLevelFieldCrumbledSandNeighbours(x, y);
5575 static void RedrawAllInvisibleElementsForMagnifier()
5579 SCAN_PLAYFIELD(x, y)
5581 int element = Feld[x][y];
5583 if (element == EL_EMC_FAKE_GRASS &&
5584 game.magnify_time_left > 0)
5586 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5587 DrawLevelField(x, y);
5589 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5590 game.magnify_time_left == 0)
5592 Feld[x][y] = EL_EMC_FAKE_GRASS;
5593 DrawLevelField(x, y);
5595 else if (IS_GATE_GRAY(element) &&
5596 game.magnify_time_left > 0)
5598 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5599 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5600 IS_EM_GATE_GRAY(element) ?
5601 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5602 IS_EMC_GATE_GRAY(element) ?
5603 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5605 DrawLevelField(x, y);
5607 else if (IS_GATE_GRAY_ACTIVE(element) &&
5608 game.magnify_time_left == 0)
5610 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5611 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5612 IS_EM_GATE_GRAY_ACTIVE(element) ?
5613 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5614 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5615 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5617 DrawLevelField(x, y);
5622 static void ToggleLightSwitch(int x, int y)
5624 int element = Feld[x][y];
5626 game.light_time_left =
5627 (element == EL_LIGHT_SWITCH ?
5628 level.time_light * FRAMES_PER_SECOND : 0);
5630 RedrawAllLightSwitchesAndInvisibleElements();
5633 static void ActivateTimegateSwitch(int x, int y)
5637 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5639 SCAN_PLAYFIELD(xx, yy)
5641 int element = Feld[xx][yy];
5643 if (element == EL_TIMEGATE_CLOSED ||
5644 element == EL_TIMEGATE_CLOSING)
5646 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5647 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5651 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5653 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5654 DrawLevelField(xx, yy);
5661 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5662 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5664 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5668 void Impact(int x, int y)
5670 boolean last_line = (y == lev_fieldy - 1);
5671 boolean object_hit = FALSE;
5672 boolean impact = (last_line || object_hit);
5673 int element = Feld[x][y];
5674 int smashed = EL_STEELWALL;
5676 if (!last_line) /* check if element below was hit */
5678 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5681 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5682 MovDir[x][y + 1] != MV_DOWN ||
5683 MovPos[x][y + 1] <= TILEY / 2));
5685 /* do not smash moving elements that left the smashed field in time */
5686 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5687 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5690 #if USE_QUICKSAND_IMPACT_BUGFIX
5691 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5693 RemoveMovingField(x, y + 1);
5694 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5695 Feld[x][y + 2] = EL_ROCK;
5696 DrawLevelField(x, y + 2);
5701 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5703 RemoveMovingField(x, y + 1);
5704 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5705 Feld[x][y + 2] = EL_ROCK;
5706 DrawLevelField(x, y + 2);
5713 smashed = MovingOrBlocked2Element(x, y + 1);
5715 impact = (last_line || object_hit);
5718 if (!last_line && smashed == EL_ACID) /* element falls into acid */
5720 SplashAcid(x, y + 1);
5724 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5725 /* only reset graphic animation if graphic really changes after impact */
5727 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5729 ResetGfxAnimation(x, y);
5730 DrawLevelField(x, y);
5733 if (impact && CAN_EXPLODE_IMPACT(element))
5738 else if (impact && element == EL_PEARL &&
5739 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5741 ResetGfxAnimation(x, y);
5743 Feld[x][y] = EL_PEARL_BREAKING;
5744 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5747 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5749 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5754 if (impact && element == EL_AMOEBA_DROP)
5756 if (object_hit && IS_PLAYER(x, y + 1))
5757 KillPlayerUnlessEnemyProtected(x, y + 1);
5758 else if (object_hit && smashed == EL_PENGUIN)
5762 Feld[x][y] = EL_AMOEBA_GROWING;
5763 Store[x][y] = EL_AMOEBA_WET;
5765 ResetRandomAnimationValue(x, y);
5770 if (object_hit) /* check which object was hit */
5772 if ((CAN_PASS_MAGIC_WALL(element) &&
5773 (smashed == EL_MAGIC_WALL ||
5774 smashed == EL_BD_MAGIC_WALL)) ||
5775 (CAN_PASS_DC_MAGIC_WALL(element) &&
5776 smashed == EL_DC_MAGIC_WALL))
5779 int activated_magic_wall =
5780 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5781 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5782 EL_DC_MAGIC_WALL_ACTIVE);
5784 /* activate magic wall / mill */
5785 SCAN_PLAYFIELD(xx, yy)
5787 if (Feld[xx][yy] == smashed)
5788 Feld[xx][yy] = activated_magic_wall;
5791 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5792 game.magic_wall_active = TRUE;
5794 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5795 SND_MAGIC_WALL_ACTIVATING :
5796 smashed == EL_BD_MAGIC_WALL ?
5797 SND_BD_MAGIC_WALL_ACTIVATING :
5798 SND_DC_MAGIC_WALL_ACTIVATING));
5801 if (IS_PLAYER(x, y + 1))
5803 if (CAN_SMASH_PLAYER(element))
5805 KillPlayerUnlessEnemyProtected(x, y + 1);
5809 else if (smashed == EL_PENGUIN)
5811 if (CAN_SMASH_PLAYER(element))
5817 else if (element == EL_BD_DIAMOND)
5819 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5825 else if (((element == EL_SP_INFOTRON ||
5826 element == EL_SP_ZONK) &&
5827 (smashed == EL_SP_SNIKSNAK ||
5828 smashed == EL_SP_ELECTRON ||
5829 smashed == EL_SP_DISK_ORANGE)) ||
5830 (element == EL_SP_INFOTRON &&
5831 smashed == EL_SP_DISK_YELLOW))
5836 else if (CAN_SMASH_EVERYTHING(element))
5838 if (IS_CLASSIC_ENEMY(smashed) ||
5839 CAN_EXPLODE_SMASHED(smashed))
5844 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5846 if (smashed == EL_LAMP ||
5847 smashed == EL_LAMP_ACTIVE)
5852 else if (smashed == EL_NUT)
5854 Feld[x][y + 1] = EL_NUT_BREAKING;
5855 PlayLevelSound(x, y, SND_NUT_BREAKING);
5856 RaiseScoreElement(EL_NUT);
5859 else if (smashed == EL_PEARL)
5861 ResetGfxAnimation(x, y);
5863 Feld[x][y + 1] = EL_PEARL_BREAKING;
5864 PlayLevelSound(x, y, SND_PEARL_BREAKING);
5867 else if (smashed == EL_DIAMOND)
5869 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5870 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5873 else if (IS_BELT_SWITCH(smashed))
5875 ToggleBeltSwitch(x, y + 1);
5877 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5878 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5879 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5880 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5882 ToggleSwitchgateSwitch(x, y + 1);
5884 else if (smashed == EL_LIGHT_SWITCH ||
5885 smashed == EL_LIGHT_SWITCH_ACTIVE)
5887 ToggleLightSwitch(x, y + 1);
5892 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5895 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5897 CheckElementChangeBySide(x, y + 1, smashed, element,
5898 CE_SWITCHED, CH_SIDE_TOP);
5899 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5905 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5910 /* play sound of magic wall / mill */
5912 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5913 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5914 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5916 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5917 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5918 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5919 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5920 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5921 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5926 /* play sound of object that hits the ground */
5927 if (last_line || object_hit)
5928 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5931 inline static void TurnRoundExt(int x, int y)
5943 { 0, 0 }, { 0, 0 }, { 0, 0 },
5948 int left, right, back;
5952 { MV_DOWN, MV_UP, MV_RIGHT },
5953 { MV_UP, MV_DOWN, MV_LEFT },
5955 { MV_LEFT, MV_RIGHT, MV_DOWN },
5959 { MV_RIGHT, MV_LEFT, MV_UP }
5962 int element = Feld[x][y];
5963 int move_pattern = element_info[element].move_pattern;
5965 int old_move_dir = MovDir[x][y];
5966 int left_dir = turn[old_move_dir].left;
5967 int right_dir = turn[old_move_dir].right;
5968 int back_dir = turn[old_move_dir].back;
5970 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5971 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5972 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5973 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5975 int left_x = x + left_dx, left_y = y + left_dy;
5976 int right_x = x + right_dx, right_y = y + right_dy;
5977 int move_x = x + move_dx, move_y = y + move_dy;
5981 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5983 TestIfBadThingTouchesOtherBadThing(x, y);
5985 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5986 MovDir[x][y] = right_dir;
5987 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5988 MovDir[x][y] = left_dir;
5990 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5992 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5995 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5997 TestIfBadThingTouchesOtherBadThing(x, y);
5999 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6000 MovDir[x][y] = left_dir;
6001 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6002 MovDir[x][y] = right_dir;
6004 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6006 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6009 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6011 TestIfBadThingTouchesOtherBadThing(x, y);
6013 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6014 MovDir[x][y] = left_dir;
6015 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6016 MovDir[x][y] = right_dir;
6018 if (MovDir[x][y] != old_move_dir)
6021 else if (element == EL_YAMYAM)
6023 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6024 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6026 if (can_turn_left && can_turn_right)
6027 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6028 else if (can_turn_left)
6029 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6030 else if (can_turn_right)
6031 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6033 MovDir[x][y] = back_dir;
6035 MovDelay[x][y] = 16 + 16 * RND(3);
6037 else if (element == EL_DARK_YAMYAM)
6039 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6041 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6044 if (can_turn_left && can_turn_right)
6045 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6046 else if (can_turn_left)
6047 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6048 else if (can_turn_right)
6049 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6051 MovDir[x][y] = back_dir;
6053 MovDelay[x][y] = 16 + 16 * RND(3);
6055 else if (element == EL_PACMAN)
6057 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6058 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6060 if (can_turn_left && can_turn_right)
6061 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6062 else if (can_turn_left)
6063 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6064 else if (can_turn_right)
6065 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6067 MovDir[x][y] = back_dir;
6069 MovDelay[x][y] = 6 + RND(40);
6071 else if (element == EL_PIG)
6073 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6074 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6075 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6076 boolean should_turn_left, should_turn_right, should_move_on;
6078 int rnd = RND(rnd_value);
6080 should_turn_left = (can_turn_left &&
6082 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6083 y + back_dy + left_dy)));
6084 should_turn_right = (can_turn_right &&
6086 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6087 y + back_dy + right_dy)));
6088 should_move_on = (can_move_on &&
6091 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6092 y + move_dy + left_dy) ||
6093 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6094 y + move_dy + right_dy)));
6096 if (should_turn_left || should_turn_right || should_move_on)
6098 if (should_turn_left && should_turn_right && should_move_on)
6099 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6100 rnd < 2 * rnd_value / 3 ? right_dir :
6102 else if (should_turn_left && should_turn_right)
6103 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6104 else if (should_turn_left && should_move_on)
6105 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6106 else if (should_turn_right && should_move_on)
6107 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6108 else if (should_turn_left)
6109 MovDir[x][y] = left_dir;
6110 else if (should_turn_right)
6111 MovDir[x][y] = right_dir;
6112 else if (should_move_on)
6113 MovDir[x][y] = old_move_dir;
6115 else if (can_move_on && rnd > rnd_value / 8)
6116 MovDir[x][y] = old_move_dir;
6117 else if (can_turn_left && can_turn_right)
6118 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6119 else if (can_turn_left && rnd > rnd_value / 8)
6120 MovDir[x][y] = left_dir;
6121 else if (can_turn_right && rnd > rnd_value/8)
6122 MovDir[x][y] = right_dir;
6124 MovDir[x][y] = back_dir;
6126 xx = x + move_xy[MovDir[x][y]].dx;
6127 yy = y + move_xy[MovDir[x][y]].dy;
6129 if (!IN_LEV_FIELD(xx, yy) ||
6130 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6131 MovDir[x][y] = old_move_dir;
6135 else if (element == EL_DRAGON)
6137 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6138 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6139 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6141 int rnd = RND(rnd_value);
6143 if (can_move_on && rnd > rnd_value / 8)
6144 MovDir[x][y] = old_move_dir;
6145 else if (can_turn_left && can_turn_right)
6146 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6147 else if (can_turn_left && rnd > rnd_value / 8)
6148 MovDir[x][y] = left_dir;
6149 else if (can_turn_right && rnd > rnd_value / 8)
6150 MovDir[x][y] = right_dir;
6152 MovDir[x][y] = back_dir;
6154 xx = x + move_xy[MovDir[x][y]].dx;
6155 yy = y + move_xy[MovDir[x][y]].dy;
6157 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6158 MovDir[x][y] = old_move_dir;
6162 else if (element == EL_MOLE)
6164 boolean can_move_on =
6165 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6166 IS_AMOEBOID(Feld[move_x][move_y]) ||
6167 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6170 boolean can_turn_left =
6171 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6172 IS_AMOEBOID(Feld[left_x][left_y])));
6174 boolean can_turn_right =
6175 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6176 IS_AMOEBOID(Feld[right_x][right_y])));
6178 if (can_turn_left && can_turn_right)
6179 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6180 else if (can_turn_left)
6181 MovDir[x][y] = left_dir;
6183 MovDir[x][y] = right_dir;
6186 if (MovDir[x][y] != old_move_dir)
6189 else if (element == EL_BALLOON)
6191 MovDir[x][y] = game.wind_direction;
6194 else if (element == EL_SPRING)
6196 #if USE_NEW_SPRING_BUMPER
6197 if (MovDir[x][y] & MV_HORIZONTAL)
6199 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6200 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6202 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6203 ResetGfxAnimation(move_x, move_y);
6204 DrawLevelField(move_x, move_y);
6206 MovDir[x][y] = back_dir;
6208 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6209 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6210 MovDir[x][y] = MV_NONE;
6213 if (MovDir[x][y] & MV_HORIZONTAL &&
6214 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6215 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6216 MovDir[x][y] = MV_NONE;
6221 else if (element == EL_ROBOT ||
6222 element == EL_SATELLITE ||
6223 element == EL_PENGUIN ||
6224 element == EL_EMC_ANDROID)
6226 int attr_x = -1, attr_y = -1;
6237 for (i = 0; i < MAX_PLAYERS; i++)
6239 struct PlayerInfo *player = &stored_player[i];
6240 int jx = player->jx, jy = player->jy;
6242 if (!player->active)
6246 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6254 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6255 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6256 game.engine_version < VERSION_IDENT(3,1,0,0)))
6262 if (element == EL_PENGUIN)
6265 static int xy[4][2] =
6273 for (i = 0; i < NUM_DIRECTIONS; i++)
6275 int ex = x + xy[i][0];
6276 int ey = y + xy[i][1];
6278 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6279 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6280 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6281 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6290 MovDir[x][y] = MV_NONE;
6292 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6293 else if (attr_x > x)
6294 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6296 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6297 else if (attr_y > y)
6298 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6300 if (element == EL_ROBOT)
6304 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6305 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6306 Moving2Blocked(x, y, &newx, &newy);
6308 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6309 MovDelay[x][y] = 8 + 8 * !RND(3);
6311 MovDelay[x][y] = 16;
6313 else if (element == EL_PENGUIN)
6319 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6321 boolean first_horiz = RND(2);
6322 int new_move_dir = MovDir[x][y];
6325 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6326 Moving2Blocked(x, y, &newx, &newy);
6328 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6332 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6333 Moving2Blocked(x, y, &newx, &newy);
6335 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6338 MovDir[x][y] = old_move_dir;
6342 else if (element == EL_SATELLITE)
6348 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6350 boolean first_horiz = RND(2);
6351 int new_move_dir = MovDir[x][y];
6354 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6355 Moving2Blocked(x, y, &newx, &newy);
6357 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6361 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6362 Moving2Blocked(x, y, &newx, &newy);
6364 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6367 MovDir[x][y] = old_move_dir;
6371 else if (element == EL_EMC_ANDROID)
6373 static int check_pos[16] =
6375 -1, /* 0 => (invalid) */
6376 7, /* 1 => MV_LEFT */
6377 3, /* 2 => MV_RIGHT */
6378 -1, /* 3 => (invalid) */
6380 0, /* 5 => MV_LEFT | MV_UP */
6381 2, /* 6 => MV_RIGHT | MV_UP */
6382 -1, /* 7 => (invalid) */
6383 5, /* 8 => MV_DOWN */
6384 6, /* 9 => MV_LEFT | MV_DOWN */
6385 4, /* 10 => MV_RIGHT | MV_DOWN */
6386 -1, /* 11 => (invalid) */
6387 -1, /* 12 => (invalid) */
6388 -1, /* 13 => (invalid) */
6389 -1, /* 14 => (invalid) */
6390 -1, /* 15 => (invalid) */
6398 { -1, -1, MV_LEFT | MV_UP },
6400 { +1, -1, MV_RIGHT | MV_UP },
6401 { +1, 0, MV_RIGHT },
6402 { +1, +1, MV_RIGHT | MV_DOWN },
6404 { -1, +1, MV_LEFT | MV_DOWN },
6407 int start_pos, check_order;
6408 boolean can_clone = FALSE;
6411 /* check if there is any free field around current position */
6412 for (i = 0; i < 8; i++)
6414 int newx = x + check_xy[i].dx;
6415 int newy = y + check_xy[i].dy;
6417 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6425 if (can_clone) /* randomly find an element to clone */
6429 start_pos = check_pos[RND(8)];
6430 check_order = (RND(2) ? -1 : +1);
6432 for (i = 0; i < 8; i++)
6434 int pos_raw = start_pos + i * check_order;
6435 int pos = (pos_raw + 8) % 8;
6436 int newx = x + check_xy[pos].dx;
6437 int newy = y + check_xy[pos].dy;
6439 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6441 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6442 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6444 Store[x][y] = Feld[newx][newy];
6453 if (can_clone) /* randomly find a direction to move */
6457 start_pos = check_pos[RND(8)];
6458 check_order = (RND(2) ? -1 : +1);
6460 for (i = 0; i < 8; i++)
6462 int pos_raw = start_pos + i * check_order;
6463 int pos = (pos_raw + 8) % 8;
6464 int newx = x + check_xy[pos].dx;
6465 int newy = y + check_xy[pos].dy;
6466 int new_move_dir = check_xy[pos].dir;
6468 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6470 MovDir[x][y] = new_move_dir;
6471 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6480 if (can_clone) /* cloning and moving successful */
6483 /* cannot clone -- try to move towards player */
6485 start_pos = check_pos[MovDir[x][y] & 0x0f];
6486 check_order = (RND(2) ? -1 : +1);
6488 for (i = 0; i < 3; i++)
6490 /* first check start_pos, then previous/next or (next/previous) pos */
6491 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6492 int pos = (pos_raw + 8) % 8;
6493 int newx = x + check_xy[pos].dx;
6494 int newy = y + check_xy[pos].dy;
6495 int new_move_dir = check_xy[pos].dir;
6497 if (IS_PLAYER(newx, newy))
6500 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6502 MovDir[x][y] = new_move_dir;
6503 MovDelay[x][y] = level.android_move_time * 8 + 1;
6510 else if (move_pattern == MV_TURNING_LEFT ||
6511 move_pattern == MV_TURNING_RIGHT ||
6512 move_pattern == MV_TURNING_LEFT_RIGHT ||
6513 move_pattern == MV_TURNING_RIGHT_LEFT ||
6514 move_pattern == MV_TURNING_RANDOM ||
6515 move_pattern == MV_ALL_DIRECTIONS)
6517 boolean can_turn_left =
6518 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6519 boolean can_turn_right =
6520 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6522 if (element_info[element].move_stepsize == 0) /* "not moving" */
6525 if (move_pattern == MV_TURNING_LEFT)
6526 MovDir[x][y] = left_dir;
6527 else if (move_pattern == MV_TURNING_RIGHT)
6528 MovDir[x][y] = right_dir;
6529 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6530 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6531 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6532 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6533 else if (move_pattern == MV_TURNING_RANDOM)
6534 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6535 can_turn_right && !can_turn_left ? right_dir :
6536 RND(2) ? left_dir : right_dir);
6537 else if (can_turn_left && can_turn_right)
6538 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6539 else if (can_turn_left)
6540 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6541 else if (can_turn_right)
6542 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6544 MovDir[x][y] = back_dir;
6546 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6548 else if (move_pattern == MV_HORIZONTAL ||
6549 move_pattern == MV_VERTICAL)
6551 if (move_pattern & old_move_dir)
6552 MovDir[x][y] = back_dir;
6553 else if (move_pattern == MV_HORIZONTAL)
6554 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6555 else if (move_pattern == MV_VERTICAL)
6556 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6558 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6560 else if (move_pattern & MV_ANY_DIRECTION)
6562 MovDir[x][y] = move_pattern;
6563 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6565 else if (move_pattern & MV_WIND_DIRECTION)
6567 MovDir[x][y] = game.wind_direction;
6568 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6570 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6572 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6573 MovDir[x][y] = left_dir;
6574 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6575 MovDir[x][y] = right_dir;
6577 if (MovDir[x][y] != old_move_dir)
6578 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6580 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6582 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6583 MovDir[x][y] = right_dir;
6584 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6585 MovDir[x][y] = left_dir;
6587 if (MovDir[x][y] != old_move_dir)
6588 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6590 else if (move_pattern == MV_TOWARDS_PLAYER ||
6591 move_pattern == MV_AWAY_FROM_PLAYER)
6593 int attr_x = -1, attr_y = -1;
6595 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6606 for (i = 0; i < MAX_PLAYERS; i++)
6608 struct PlayerInfo *player = &stored_player[i];
6609 int jx = player->jx, jy = player->jy;
6611 if (!player->active)
6615 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6623 MovDir[x][y] = MV_NONE;
6625 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6626 else if (attr_x > x)
6627 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6629 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6630 else if (attr_y > y)
6631 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6633 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6635 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6637 boolean first_horiz = RND(2);
6638 int new_move_dir = MovDir[x][y];
6640 if (element_info[element].move_stepsize == 0) /* "not moving" */
6642 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6643 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6649 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6650 Moving2Blocked(x, y, &newx, &newy);
6652 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6656 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6657 Moving2Blocked(x, y, &newx, &newy);
6659 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6662 MovDir[x][y] = old_move_dir;
6665 else if (move_pattern == MV_WHEN_PUSHED ||
6666 move_pattern == MV_WHEN_DROPPED)
6668 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6669 MovDir[x][y] = MV_NONE;
6673 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6675 static int test_xy[7][2] =
6685 static int test_dir[7] =
6695 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6696 int move_preference = -1000000; /* start with very low preference */
6697 int new_move_dir = MV_NONE;
6698 int start_test = RND(4);
6701 for (i = 0; i < NUM_DIRECTIONS; i++)
6703 int move_dir = test_dir[start_test + i];
6704 int move_dir_preference;
6706 xx = x + test_xy[start_test + i][0];
6707 yy = y + test_xy[start_test + i][1];
6709 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6710 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6712 new_move_dir = move_dir;
6717 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6720 move_dir_preference = -1 * RunnerVisit[xx][yy];
6721 if (hunter_mode && PlayerVisit[xx][yy] > 0)
6722 move_dir_preference = PlayerVisit[xx][yy];
6724 if (move_dir_preference > move_preference)
6726 /* prefer field that has not been visited for the longest time */
6727 move_preference = move_dir_preference;
6728 new_move_dir = move_dir;
6730 else if (move_dir_preference == move_preference &&
6731 move_dir == old_move_dir)
6733 /* prefer last direction when all directions are preferred equally */
6734 move_preference = move_dir_preference;
6735 new_move_dir = move_dir;
6739 MovDir[x][y] = new_move_dir;
6740 if (old_move_dir != new_move_dir)
6741 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6745 static void TurnRound(int x, int y)
6747 int direction = MovDir[x][y];
6751 GfxDir[x][y] = MovDir[x][y];
6753 if (direction != MovDir[x][y])
6757 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6759 ResetGfxFrame(x, y, FALSE);
6762 static boolean JustBeingPushed(int x, int y)
6766 for (i = 0; i < MAX_PLAYERS; i++)
6768 struct PlayerInfo *player = &stored_player[i];
6770 if (player->active && player->is_pushing && player->MovPos)
6772 int next_jx = player->jx + (player->jx - player->last_jx);
6773 int next_jy = player->jy + (player->jy - player->last_jy);
6775 if (x == next_jx && y == next_jy)
6783 void StartMoving(int x, int y)
6785 boolean started_moving = FALSE; /* some elements can fall _and_ move */
6786 int element = Feld[x][y];
6791 if (MovDelay[x][y] == 0)
6792 GfxAction[x][y] = ACTION_DEFAULT;
6794 if (CAN_FALL(element) && y < lev_fieldy - 1)
6796 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
6797 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6798 if (JustBeingPushed(x, y))
6801 if (element == EL_QUICKSAND_FULL)
6803 if (IS_FREE(x, y + 1))
6805 InitMovingField(x, y, MV_DOWN);
6806 started_moving = TRUE;
6808 Feld[x][y] = EL_QUICKSAND_EMPTYING;
6809 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6810 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6811 Store[x][y] = EL_ROCK;
6813 Store[x][y] = EL_ROCK;
6816 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6818 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6820 if (!MovDelay[x][y])
6821 MovDelay[x][y] = TILEY + 1;
6830 Feld[x][y] = EL_QUICKSAND_EMPTY;
6831 Feld[x][y + 1] = EL_QUICKSAND_FULL;
6832 Store[x][y + 1] = Store[x][y];
6835 PlayLevelSoundAction(x, y, ACTION_FILLING);
6838 else if (element == EL_QUICKSAND_FAST_FULL)
6840 if (IS_FREE(x, y + 1))
6842 InitMovingField(x, y, MV_DOWN);
6843 started_moving = TRUE;
6845 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6846 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6847 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6848 Store[x][y] = EL_ROCK;
6850 Store[x][y] = EL_ROCK;
6853 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6855 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6857 if (!MovDelay[x][y])
6858 MovDelay[x][y] = TILEY + 1;
6867 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6868 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6869 Store[x][y + 1] = Store[x][y];
6872 PlayLevelSoundAction(x, y, ACTION_FILLING);
6875 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6876 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6878 InitMovingField(x, y, MV_DOWN);
6879 started_moving = TRUE;
6881 Feld[x][y] = EL_QUICKSAND_FILLING;
6882 Store[x][y] = element;
6884 PlayLevelSoundAction(x, y, ACTION_FILLING);
6886 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6887 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6889 InitMovingField(x, y, MV_DOWN);
6890 started_moving = TRUE;
6892 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6893 Store[x][y] = element;
6895 PlayLevelSoundAction(x, y, ACTION_FILLING);
6897 else if (element == EL_MAGIC_WALL_FULL)
6899 if (IS_FREE(x, y + 1))
6901 InitMovingField(x, y, MV_DOWN);
6902 started_moving = TRUE;
6904 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6905 Store[x][y] = EL_CHANGED(Store[x][y]);
6907 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6909 if (!MovDelay[x][y])
6910 MovDelay[x][y] = TILEY/4 + 1;
6919 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6920 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6921 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6925 else if (element == EL_BD_MAGIC_WALL_FULL)
6927 if (IS_FREE(x, y + 1))
6929 InitMovingField(x, y, MV_DOWN);
6930 started_moving = TRUE;
6932 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6933 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6935 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6937 if (!MovDelay[x][y])
6938 MovDelay[x][y] = TILEY/4 + 1;
6947 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6948 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6949 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6953 else if (element == EL_DC_MAGIC_WALL_FULL)
6955 if (IS_FREE(x, y + 1))
6957 InitMovingField(x, y, MV_DOWN);
6958 started_moving = TRUE;
6960 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6961 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6963 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6965 if (!MovDelay[x][y])
6966 MovDelay[x][y] = TILEY/4 + 1;
6975 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6976 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6977 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6981 else if ((CAN_PASS_MAGIC_WALL(element) &&
6982 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6983 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6984 (CAN_PASS_DC_MAGIC_WALL(element) &&
6985 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6988 InitMovingField(x, y, MV_DOWN);
6989 started_moving = TRUE;
6992 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6993 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6994 EL_DC_MAGIC_WALL_FILLING);
6995 Store[x][y] = element;
6997 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6999 SplashAcid(x, y + 1);
7001 InitMovingField(x, y, MV_DOWN);
7002 started_moving = TRUE;
7004 Store[x][y] = EL_ACID;
7007 #if USE_FIX_IMPACT_COLLISION
7008 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7009 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7011 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7012 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7014 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7015 CAN_FALL(element) && WasJustFalling[x][y] &&
7016 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7018 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7019 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7020 (Feld[x][y + 1] == EL_BLOCKED)))
7022 /* this is needed for a special case not covered by calling "Impact()"
7023 from "ContinueMoving()": if an element moves to a tile directly below
7024 another element which was just falling on that tile (which was empty
7025 in the previous frame), the falling element above would just stop
7026 instead of smashing the element below (in previous version, the above
7027 element was just checked for "moving" instead of "falling", resulting
7028 in incorrect smashes caused by horizontal movement of the above
7029 element; also, the case of the player being the element to smash was
7030 simply not covered here... :-/ ) */
7032 CheckCollision[x][y] = 0;
7033 CheckImpact[x][y] = 0;
7037 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7039 if (MovDir[x][y] == MV_NONE)
7041 InitMovingField(x, y, MV_DOWN);
7042 started_moving = TRUE;
7045 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7047 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7048 MovDir[x][y] = MV_DOWN;
7050 InitMovingField(x, y, MV_DOWN);
7051 started_moving = TRUE;
7053 else if (element == EL_AMOEBA_DROP)
7055 Feld[x][y] = EL_AMOEBA_GROWING;
7056 Store[x][y] = EL_AMOEBA_WET;
7058 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7059 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7060 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7061 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7063 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7064 (IS_FREE(x - 1, y + 1) ||
7065 Feld[x - 1][y + 1] == EL_ACID));
7066 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7067 (IS_FREE(x + 1, y + 1) ||
7068 Feld[x + 1][y + 1] == EL_ACID));
7069 boolean can_fall_any = (can_fall_left || can_fall_right);
7070 boolean can_fall_both = (can_fall_left && can_fall_right);
7071 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7073 #if USE_NEW_ALL_SLIPPERY
7074 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7076 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7077 can_fall_right = FALSE;
7078 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7079 can_fall_left = FALSE;
7080 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7081 can_fall_right = FALSE;
7082 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7083 can_fall_left = FALSE;
7085 can_fall_any = (can_fall_left || can_fall_right);
7086 can_fall_both = FALSE;
7089 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7091 if (slippery_type == SLIPPERY_ONLY_LEFT)
7092 can_fall_right = FALSE;
7093 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7094 can_fall_left = FALSE;
7095 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7096 can_fall_right = FALSE;
7097 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7098 can_fall_left = FALSE;
7100 can_fall_any = (can_fall_left || can_fall_right);
7101 can_fall_both = (can_fall_left && can_fall_right);
7105 #if USE_NEW_ALL_SLIPPERY
7107 #if USE_NEW_SP_SLIPPERY
7108 /* !!! better use the same properties as for custom elements here !!! */
7109 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7110 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7112 can_fall_right = FALSE; /* slip down on left side */
7113 can_fall_both = FALSE;
7118 #if USE_NEW_ALL_SLIPPERY
7121 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7122 can_fall_right = FALSE; /* slip down on left side */
7124 can_fall_left = !(can_fall_right = RND(2));
7126 can_fall_both = FALSE;
7131 if (game.emulation == EMU_BOULDERDASH ||
7132 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7133 can_fall_right = FALSE; /* slip down on left side */
7135 can_fall_left = !(can_fall_right = RND(2));
7137 can_fall_both = FALSE;
7143 /* if not determined otherwise, prefer left side for slipping down */
7144 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7145 started_moving = TRUE;
7149 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7151 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7154 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7155 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7156 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7157 int belt_dir = game.belt_dir[belt_nr];
7159 if ((belt_dir == MV_LEFT && left_is_free) ||
7160 (belt_dir == MV_RIGHT && right_is_free))
7162 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7164 InitMovingField(x, y, belt_dir);
7165 started_moving = TRUE;
7167 Pushed[x][y] = TRUE;
7168 Pushed[nextx][y] = TRUE;
7170 GfxAction[x][y] = ACTION_DEFAULT;
7174 MovDir[x][y] = 0; /* if element was moving, stop it */
7179 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7181 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7183 if (CAN_MOVE(element) && !started_moving)
7186 int move_pattern = element_info[element].move_pattern;
7191 if (MovDir[x][y] == MV_NONE)
7193 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7194 x, y, element, element_info[element].token_name);
7195 printf("StartMoving(): This should never happen!\n");
7200 Moving2Blocked(x, y, &newx, &newy);
7202 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7205 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7206 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7208 WasJustMoving[x][y] = 0;
7209 CheckCollision[x][y] = 0;
7211 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7213 if (Feld[x][y] != element) /* element has changed */
7217 if (!MovDelay[x][y]) /* start new movement phase */
7219 /* all objects that can change their move direction after each step
7220 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7222 if (element != EL_YAMYAM &&
7223 element != EL_DARK_YAMYAM &&
7224 element != EL_PACMAN &&
7225 !(move_pattern & MV_ANY_DIRECTION) &&
7226 move_pattern != MV_TURNING_LEFT &&
7227 move_pattern != MV_TURNING_RIGHT &&
7228 move_pattern != MV_TURNING_LEFT_RIGHT &&
7229 move_pattern != MV_TURNING_RIGHT_LEFT &&
7230 move_pattern != MV_TURNING_RANDOM)
7234 if (MovDelay[x][y] && (element == EL_BUG ||
7235 element == EL_SPACESHIP ||
7236 element == EL_SP_SNIKSNAK ||
7237 element == EL_SP_ELECTRON ||
7238 element == EL_MOLE))
7239 DrawLevelField(x, y);
7243 if (MovDelay[x][y]) /* wait some time before next movement */
7247 if (element == EL_ROBOT ||
7248 element == EL_YAMYAM ||
7249 element == EL_DARK_YAMYAM)
7251 DrawLevelElementAnimationIfNeeded(x, y, element);
7252 PlayLevelSoundAction(x, y, ACTION_WAITING);
7254 else if (element == EL_SP_ELECTRON)
7255 DrawLevelElementAnimationIfNeeded(x, y, element);
7256 else if (element == EL_DRAGON)
7259 int dir = MovDir[x][y];
7260 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7261 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7262 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7263 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7264 dir == MV_UP ? IMG_FLAMES_1_UP :
7265 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7266 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7268 GfxAction[x][y] = ACTION_ATTACKING;
7270 if (IS_PLAYER(x, y))
7271 DrawPlayerField(x, y);
7273 DrawLevelField(x, y);
7275 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7277 for (i = 1; i <= 3; i++)
7279 int xx = x + i * dx;
7280 int yy = y + i * dy;
7281 int sx = SCREENX(xx);
7282 int sy = SCREENY(yy);
7283 int flame_graphic = graphic + (i - 1);
7285 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7290 int flamed = MovingOrBlocked2Element(xx, yy);
7294 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7296 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7297 RemoveMovingField(xx, yy);
7299 RemoveField(xx, yy);
7301 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7304 RemoveMovingField(xx, yy);
7307 ChangeDelay[xx][yy] = 0;
7309 Feld[xx][yy] = EL_FLAMES;
7311 if (IN_SCR_FIELD(sx, sy))
7313 DrawLevelFieldCrumbledSand(xx, yy);
7314 DrawGraphic(sx, sy, flame_graphic, frame);
7319 if (Feld[xx][yy] == EL_FLAMES)
7320 Feld[xx][yy] = EL_EMPTY;
7321 DrawLevelField(xx, yy);
7326 if (MovDelay[x][y]) /* element still has to wait some time */
7328 PlayLevelSoundAction(x, y, ACTION_WAITING);
7334 /* now make next step */
7336 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7338 if (DONT_COLLIDE_WITH(element) &&
7339 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7340 !PLAYER_ENEMY_PROTECTED(newx, newy))
7342 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7347 else if (CAN_MOVE_INTO_ACID(element) &&
7348 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7349 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7350 (MovDir[x][y] == MV_DOWN ||
7351 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7353 SplashAcid(newx, newy);
7354 Store[x][y] = EL_ACID;
7356 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7358 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7359 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7360 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7361 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7364 DrawLevelField(x, y);
7366 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7367 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7368 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7370 local_player->friends_still_needed--;
7371 if (!local_player->friends_still_needed &&
7372 !local_player->GameOver && AllPlayersGone)
7373 PlayerWins(local_player);
7377 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7379 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7380 DrawLevelField(newx, newy);
7382 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7384 else if (!IS_FREE(newx, newy))
7386 GfxAction[x][y] = ACTION_WAITING;
7388 if (IS_PLAYER(x, y))
7389 DrawPlayerField(x, y);
7391 DrawLevelField(x, y);
7396 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7398 if (IS_FOOD_PIG(Feld[newx][newy]))
7400 if (IS_MOVING(newx, newy))
7401 RemoveMovingField(newx, newy);
7404 Feld[newx][newy] = EL_EMPTY;
7405 DrawLevelField(newx, newy);
7408 PlayLevelSound(x, y, SND_PIG_DIGGING);
7410 else if (!IS_FREE(newx, newy))
7412 if (IS_PLAYER(x, y))
7413 DrawPlayerField(x, y);
7415 DrawLevelField(x, y);
7420 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7422 if (Store[x][y] != EL_EMPTY)
7424 boolean can_clone = FALSE;
7427 /* check if element to clone is still there */
7428 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7430 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7438 /* cannot clone or target field not free anymore -- do not clone */
7439 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7440 Store[x][y] = EL_EMPTY;
7443 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7445 if (IS_MV_DIAGONAL(MovDir[x][y]))
7447 int diagonal_move_dir = MovDir[x][y];
7448 int stored = Store[x][y];
7449 int change_delay = 8;
7452 /* android is moving diagonally */
7454 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7456 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7457 GfxElement[x][y] = EL_EMC_ANDROID;
7458 GfxAction[x][y] = ACTION_SHRINKING;
7459 GfxDir[x][y] = diagonal_move_dir;
7460 ChangeDelay[x][y] = change_delay;
7462 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7465 DrawLevelGraphicAnimation(x, y, graphic);
7466 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7468 if (Feld[newx][newy] == EL_ACID)
7470 SplashAcid(newx, newy);
7475 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7477 Store[newx][newy] = EL_EMC_ANDROID;
7478 GfxElement[newx][newy] = EL_EMC_ANDROID;
7479 GfxAction[newx][newy] = ACTION_GROWING;
7480 GfxDir[newx][newy] = diagonal_move_dir;
7481 ChangeDelay[newx][newy] = change_delay;
7483 graphic = el_act_dir2img(GfxElement[newx][newy],
7484 GfxAction[newx][newy], GfxDir[newx][newy]);
7486 DrawLevelGraphicAnimation(newx, newy, graphic);
7487 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7493 Feld[newx][newy] = EL_EMPTY;
7494 DrawLevelField(newx, newy);
7496 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7499 else if (!IS_FREE(newx, newy))
7502 if (IS_PLAYER(x, y))
7503 DrawPlayerField(x, y);
7505 DrawLevelField(x, y);
7511 else if (IS_CUSTOM_ELEMENT(element) &&
7512 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7514 int new_element = Feld[newx][newy];
7516 if (!IS_FREE(newx, newy))
7518 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7519 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7522 /* no element can dig solid indestructible elements */
7523 if (IS_INDESTRUCTIBLE(new_element) &&
7524 !IS_DIGGABLE(new_element) &&
7525 !IS_COLLECTIBLE(new_element))
7528 if (AmoebaNr[newx][newy] &&
7529 (new_element == EL_AMOEBA_FULL ||
7530 new_element == EL_BD_AMOEBA ||
7531 new_element == EL_AMOEBA_GROWING))
7533 AmoebaCnt[AmoebaNr[newx][newy]]--;
7534 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7537 if (IS_MOVING(newx, newy))
7538 RemoveMovingField(newx, newy);
7541 RemoveField(newx, newy);
7542 DrawLevelField(newx, newy);
7545 /* if digged element was about to explode, prevent the explosion */
7546 ExplodeField[newx][newy] = EX_TYPE_NONE;
7548 PlayLevelSoundAction(x, y, action);
7551 Store[newx][newy] = EL_EMPTY;
7553 /* this makes it possible to leave the removed element again */
7554 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7555 Store[newx][newy] = new_element;
7557 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7559 int move_leave_element = element_info[element].move_leave_element;
7561 /* this makes it possible to leave the removed element again */
7562 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7563 new_element : move_leave_element);
7567 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7569 RunnerVisit[x][y] = FrameCounter;
7570 PlayerVisit[x][y] /= 8; /* expire player visit path */
7573 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7575 if (!IS_FREE(newx, newy))
7577 if (IS_PLAYER(x, y))
7578 DrawPlayerField(x, y);
7580 DrawLevelField(x, y);
7586 boolean wanna_flame = !RND(10);
7587 int dx = newx - x, dy = newy - y;
7588 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7589 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7590 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7591 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7592 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7593 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7596 IS_CLASSIC_ENEMY(element1) ||
7597 IS_CLASSIC_ENEMY(element2)) &&
7598 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7599 element1 != EL_FLAMES && element2 != EL_FLAMES)
7601 ResetGfxAnimation(x, y);
7602 GfxAction[x][y] = ACTION_ATTACKING;
7604 if (IS_PLAYER(x, y))
7605 DrawPlayerField(x, y);
7607 DrawLevelField(x, y);
7609 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7611 MovDelay[x][y] = 50;
7615 RemoveField(newx, newy);
7617 Feld[newx][newy] = EL_FLAMES;
7618 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7621 RemoveField(newx1, newy1);
7623 Feld[newx1][newy1] = EL_FLAMES;
7625 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7628 RemoveField(newx2, newy2);
7630 Feld[newx2][newy2] = EL_FLAMES;
7637 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7638 Feld[newx][newy] == EL_DIAMOND)
7640 if (IS_MOVING(newx, newy))
7641 RemoveMovingField(newx, newy);
7644 Feld[newx][newy] = EL_EMPTY;
7645 DrawLevelField(newx, newy);
7648 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7650 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7651 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7653 if (AmoebaNr[newx][newy])
7655 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7656 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7657 Feld[newx][newy] == EL_BD_AMOEBA)
7658 AmoebaCnt[AmoebaNr[newx][newy]]--;
7663 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7665 RemoveMovingField(newx, newy);
7668 if (IS_MOVING(newx, newy))
7670 RemoveMovingField(newx, newy);
7675 Feld[newx][newy] = EL_EMPTY;
7676 DrawLevelField(newx, newy);
7679 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7681 else if ((element == EL_PACMAN || element == EL_MOLE)
7682 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7684 if (AmoebaNr[newx][newy])
7686 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7687 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7688 Feld[newx][newy] == EL_BD_AMOEBA)
7689 AmoebaCnt[AmoebaNr[newx][newy]]--;
7692 if (element == EL_MOLE)
7694 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7695 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7697 ResetGfxAnimation(x, y);
7698 GfxAction[x][y] = ACTION_DIGGING;
7699 DrawLevelField(x, y);
7701 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7703 return; /* wait for shrinking amoeba */
7705 else /* element == EL_PACMAN */
7707 Feld[newx][newy] = EL_EMPTY;
7708 DrawLevelField(newx, newy);
7709 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7712 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7713 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7714 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7716 /* wait for shrinking amoeba to completely disappear */
7719 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7721 /* object was running against a wall */
7726 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7727 if (move_pattern & MV_ANY_DIRECTION &&
7728 move_pattern == MovDir[x][y])
7730 int blocking_element =
7731 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7733 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7736 element = Feld[x][y]; /* element might have changed */
7740 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7741 DrawLevelElementAnimation(x, y, element);
7743 if (DONT_TOUCH(element))
7744 TestIfBadThingTouchesPlayer(x, y);
7749 InitMovingField(x, y, MovDir[x][y]);
7751 PlayLevelSoundAction(x, y, ACTION_MOVING);
7755 ContinueMoving(x, y);
7758 void ContinueMoving(int x, int y)
7760 int element = Feld[x][y];
7761 struct ElementInfo *ei = &element_info[element];
7762 int direction = MovDir[x][y];
7763 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7764 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7765 int newx = x + dx, newy = y + dy;
7766 int stored = Store[x][y];
7767 int stored_new = Store[newx][newy];
7768 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7769 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7770 boolean last_line = (newy == lev_fieldy - 1);
7772 MovPos[x][y] += getElementMoveStepsize(x, y);
7774 if (pushed_by_player) /* special case: moving object pushed by player */
7775 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7777 if (ABS(MovPos[x][y]) < TILEX)
7780 int ee = Feld[x][y];
7781 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7782 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7784 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7785 x, y, ABS(MovPos[x][y]),
7787 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7790 DrawLevelField(x, y);
7792 return; /* element is still moving */
7795 /* element reached destination field */
7797 Feld[x][y] = EL_EMPTY;
7798 Feld[newx][newy] = element;
7799 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7801 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7803 element = Feld[newx][newy] = EL_ACID;
7805 else if (element == EL_MOLE)
7807 Feld[x][y] = EL_SAND;
7809 DrawLevelFieldCrumbledSandNeighbours(x, y);
7811 else if (element == EL_QUICKSAND_FILLING)
7813 element = Feld[newx][newy] = get_next_element(element);
7814 Store[newx][newy] = Store[x][y];
7816 else if (element == EL_QUICKSAND_EMPTYING)
7818 Feld[x][y] = get_next_element(element);
7819 element = Feld[newx][newy] = Store[x][y];
7821 else if (element == EL_QUICKSAND_FAST_FILLING)
7823 element = Feld[newx][newy] = get_next_element(element);
7824 Store[newx][newy] = Store[x][y];
7826 else if (element == EL_QUICKSAND_FAST_EMPTYING)
7828 Feld[x][y] = get_next_element(element);
7829 element = Feld[newx][newy] = Store[x][y];
7831 else if (element == EL_MAGIC_WALL_FILLING)
7833 element = Feld[newx][newy] = get_next_element(element);
7834 if (!game.magic_wall_active)
7835 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7836 Store[newx][newy] = Store[x][y];
7838 else if (element == EL_MAGIC_WALL_EMPTYING)
7840 Feld[x][y] = get_next_element(element);
7841 if (!game.magic_wall_active)
7842 Feld[x][y] = EL_MAGIC_WALL_DEAD;
7843 element = Feld[newx][newy] = Store[x][y];
7845 #if USE_NEW_CUSTOM_VALUE
7846 InitField(newx, newy, FALSE);
7849 else if (element == EL_BD_MAGIC_WALL_FILLING)
7851 element = Feld[newx][newy] = get_next_element(element);
7852 if (!game.magic_wall_active)
7853 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7854 Store[newx][newy] = Store[x][y];
7856 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7858 Feld[x][y] = get_next_element(element);
7859 if (!game.magic_wall_active)
7860 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7861 element = Feld[newx][newy] = Store[x][y];
7863 #if USE_NEW_CUSTOM_VALUE
7864 InitField(newx, newy, FALSE);
7867 else if (element == EL_DC_MAGIC_WALL_FILLING)
7869 element = Feld[newx][newy] = get_next_element(element);
7870 if (!game.magic_wall_active)
7871 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7872 Store[newx][newy] = Store[x][y];
7874 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7876 Feld[x][y] = get_next_element(element);
7877 if (!game.magic_wall_active)
7878 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7879 element = Feld[newx][newy] = Store[x][y];
7881 #if USE_NEW_CUSTOM_VALUE
7882 InitField(newx, newy, FALSE);
7885 else if (element == EL_AMOEBA_DROPPING)
7887 Feld[x][y] = get_next_element(element);
7888 element = Feld[newx][newy] = Store[x][y];
7890 else if (element == EL_SOKOBAN_OBJECT)
7893 Feld[x][y] = Back[x][y];
7895 if (Back[newx][newy])
7896 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7898 Back[x][y] = Back[newx][newy] = 0;
7901 Store[x][y] = EL_EMPTY;
7906 MovDelay[newx][newy] = 0;
7908 if (CAN_CHANGE_OR_HAS_ACTION(element))
7910 /* copy element change control values to new field */
7911 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7912 ChangePage[newx][newy] = ChangePage[x][y];
7913 ChangeCount[newx][newy] = ChangeCount[x][y];
7914 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7917 #if USE_NEW_CUSTOM_VALUE
7918 CustomValue[newx][newy] = CustomValue[x][y];
7921 ChangeDelay[x][y] = 0;
7922 ChangePage[x][y] = -1;
7923 ChangeCount[x][y] = 0;
7924 ChangeEvent[x][y] = -1;
7926 #if USE_NEW_CUSTOM_VALUE
7927 CustomValue[x][y] = 0;
7930 /* copy animation control values to new field */
7931 GfxFrame[newx][newy] = GfxFrame[x][y];
7932 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7933 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7934 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7936 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7938 /* some elements can leave other elements behind after moving */
7940 if (ei->move_leave_element != EL_EMPTY &&
7941 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7942 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7944 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7945 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7946 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7949 int move_leave_element = ei->move_leave_element;
7953 /* this makes it possible to leave the removed element again */
7954 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7955 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7957 /* this makes it possible to leave the removed element again */
7958 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7959 move_leave_element = stored;
7962 /* this makes it possible to leave the removed element again */
7963 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7964 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7965 move_leave_element = stored;
7968 Feld[x][y] = move_leave_element;
7970 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7971 MovDir[x][y] = direction;
7973 InitField(x, y, FALSE);
7975 if (GFX_CRUMBLED(Feld[x][y]))
7976 DrawLevelFieldCrumbledSandNeighbours(x, y);
7978 if (ELEM_IS_PLAYER(move_leave_element))
7979 RelocatePlayer(x, y, move_leave_element);
7982 /* do this after checking for left-behind element */
7983 ResetGfxAnimation(x, y); /* reset animation values for old field */
7985 if (!CAN_MOVE(element) ||
7986 (CAN_FALL(element) && direction == MV_DOWN &&
7987 (element == EL_SPRING ||
7988 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7989 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7990 GfxDir[x][y] = MovDir[newx][newy] = 0;
7992 DrawLevelField(x, y);
7993 DrawLevelField(newx, newy);
7995 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7997 /* prevent pushed element from moving on in pushed direction */
7998 if (pushed_by_player && CAN_MOVE(element) &&
7999 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8000 !(element_info[element].move_pattern & direction))
8001 TurnRound(newx, newy);
8003 /* prevent elements on conveyor belt from moving on in last direction */
8004 if (pushed_by_conveyor && CAN_FALL(element) &&
8005 direction & MV_HORIZONTAL)
8006 MovDir[newx][newy] = 0;
8008 if (!pushed_by_player)
8010 int nextx = newx + dx, nexty = newy + dy;
8011 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8013 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8015 if (CAN_FALL(element) && direction == MV_DOWN)
8016 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8018 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8019 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8021 #if USE_FIX_IMPACT_COLLISION
8022 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8023 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8027 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8029 TestIfBadThingTouchesPlayer(newx, newy);
8030 TestIfBadThingTouchesFriend(newx, newy);
8032 if (!IS_CUSTOM_ELEMENT(element))
8033 TestIfBadThingTouchesOtherBadThing(newx, newy);
8035 else if (element == EL_PENGUIN)
8036 TestIfFriendTouchesBadThing(newx, newy);
8038 /* give the player one last chance (one more frame) to move away */
8039 if (CAN_FALL(element) && direction == MV_DOWN &&
8040 (last_line || (!IS_FREE(x, newy + 1) &&
8041 (!IS_PLAYER(x, newy + 1) ||
8042 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8045 if (pushed_by_player && !game.use_change_when_pushing_bug)
8047 int push_side = MV_DIR_OPPOSITE(direction);
8048 struct PlayerInfo *player = PLAYERINFO(x, y);
8050 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8051 player->index_bit, push_side);
8052 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8053 player->index_bit, push_side);
8056 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8057 MovDelay[newx][newy] = 1;
8059 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8061 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8064 if (ChangePage[newx][newy] != -1) /* delayed change */
8066 int page = ChangePage[newx][newy];
8067 struct ElementChangeInfo *change = &ei->change_page[page];
8069 ChangePage[newx][newy] = -1;
8071 if (change->can_change)
8073 if (ChangeElement(newx, newy, element, page))
8075 if (change->post_change_function)
8076 change->post_change_function(newx, newy);
8080 if (change->has_action)
8081 ExecuteCustomElementAction(newx, newy, element, page);
8085 TestIfElementHitsCustomElement(newx, newy, direction);
8086 TestIfPlayerTouchesCustomElement(newx, newy);
8087 TestIfElementTouchesCustomElement(newx, newy);
8089 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8090 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8091 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8092 MV_DIR_OPPOSITE(direction));
8095 int AmoebeNachbarNr(int ax, int ay)
8098 int element = Feld[ax][ay];
8100 static int xy[4][2] =
8108 for (i = 0; i < NUM_DIRECTIONS; i++)
8110 int x = ax + xy[i][0];
8111 int y = ay + xy[i][1];
8113 if (!IN_LEV_FIELD(x, y))
8116 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8117 group_nr = AmoebaNr[x][y];
8123 void AmoebenVereinigen(int ax, int ay)
8125 int i, x, y, xx, yy;
8126 int new_group_nr = AmoebaNr[ax][ay];
8127 static int xy[4][2] =
8135 if (new_group_nr == 0)
8138 for (i = 0; i < NUM_DIRECTIONS; i++)
8143 if (!IN_LEV_FIELD(x, y))
8146 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8147 Feld[x][y] == EL_BD_AMOEBA ||
8148 Feld[x][y] == EL_AMOEBA_DEAD) &&
8149 AmoebaNr[x][y] != new_group_nr)
8151 int old_group_nr = AmoebaNr[x][y];
8153 if (old_group_nr == 0)
8156 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8157 AmoebaCnt[old_group_nr] = 0;
8158 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8159 AmoebaCnt2[old_group_nr] = 0;
8161 SCAN_PLAYFIELD(xx, yy)
8163 if (AmoebaNr[xx][yy] == old_group_nr)
8164 AmoebaNr[xx][yy] = new_group_nr;
8170 void AmoebeUmwandeln(int ax, int ay)
8174 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8176 int group_nr = AmoebaNr[ax][ay];
8181 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8182 printf("AmoebeUmwandeln(): This should never happen!\n");
8187 SCAN_PLAYFIELD(x, y)
8189 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8192 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8196 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8197 SND_AMOEBA_TURNING_TO_GEM :
8198 SND_AMOEBA_TURNING_TO_ROCK));
8203 static int xy[4][2] =
8211 for (i = 0; i < NUM_DIRECTIONS; i++)
8216 if (!IN_LEV_FIELD(x, y))
8219 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8221 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8222 SND_AMOEBA_TURNING_TO_GEM :
8223 SND_AMOEBA_TURNING_TO_ROCK));
8230 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8233 int group_nr = AmoebaNr[ax][ay];
8234 boolean done = FALSE;
8239 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8240 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8245 SCAN_PLAYFIELD(x, y)
8247 if (AmoebaNr[x][y] == group_nr &&
8248 (Feld[x][y] == EL_AMOEBA_DEAD ||
8249 Feld[x][y] == EL_BD_AMOEBA ||
8250 Feld[x][y] == EL_AMOEBA_GROWING))
8253 Feld[x][y] = new_element;
8254 InitField(x, y, FALSE);
8255 DrawLevelField(x, y);
8261 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8262 SND_BD_AMOEBA_TURNING_TO_ROCK :
8263 SND_BD_AMOEBA_TURNING_TO_GEM));
8266 void AmoebeWaechst(int x, int y)
8268 static unsigned long sound_delay = 0;
8269 static unsigned long sound_delay_value = 0;
8271 if (!MovDelay[x][y]) /* start new growing cycle */
8275 if (DelayReached(&sound_delay, sound_delay_value))
8277 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8278 sound_delay_value = 30;
8282 if (MovDelay[x][y]) /* wait some time before growing bigger */
8285 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8287 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8288 6 - MovDelay[x][y]);
8290 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8293 if (!MovDelay[x][y])
8295 Feld[x][y] = Store[x][y];
8297 DrawLevelField(x, y);
8302 void AmoebaDisappearing(int x, int y)
8304 static unsigned long sound_delay = 0;
8305 static unsigned long sound_delay_value = 0;
8307 if (!MovDelay[x][y]) /* start new shrinking cycle */
8311 if (DelayReached(&sound_delay, sound_delay_value))
8312 sound_delay_value = 30;
8315 if (MovDelay[x][y]) /* wait some time before shrinking */
8318 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8320 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8321 6 - MovDelay[x][y]);
8323 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8326 if (!MovDelay[x][y])
8328 Feld[x][y] = EL_EMPTY;
8329 DrawLevelField(x, y);
8331 /* don't let mole enter this field in this cycle;
8332 (give priority to objects falling to this field from above) */
8338 void AmoebeAbleger(int ax, int ay)
8341 int element = Feld[ax][ay];
8342 int graphic = el2img(element);
8343 int newax = ax, neway = ay;
8344 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8345 static int xy[4][2] =
8353 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8355 Feld[ax][ay] = EL_AMOEBA_DEAD;
8356 DrawLevelField(ax, ay);
8360 if (IS_ANIMATED(graphic))
8361 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8363 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8364 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8366 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8369 if (MovDelay[ax][ay])
8373 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8376 int x = ax + xy[start][0];
8377 int y = ay + xy[start][1];
8379 if (!IN_LEV_FIELD(x, y))
8382 if (IS_FREE(x, y) ||
8383 CAN_GROW_INTO(Feld[x][y]) ||
8384 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8385 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8391 if (newax == ax && neway == ay)
8394 else /* normal or "filled" (BD style) amoeba */
8397 boolean waiting_for_player = FALSE;
8399 for (i = 0; i < NUM_DIRECTIONS; i++)
8401 int j = (start + i) % 4;
8402 int x = ax + xy[j][0];
8403 int y = ay + xy[j][1];
8405 if (!IN_LEV_FIELD(x, y))
8408 if (IS_FREE(x, y) ||
8409 CAN_GROW_INTO(Feld[x][y]) ||
8410 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8411 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8417 else if (IS_PLAYER(x, y))
8418 waiting_for_player = TRUE;
8421 if (newax == ax && neway == ay) /* amoeba cannot grow */
8423 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8425 Feld[ax][ay] = EL_AMOEBA_DEAD;
8426 DrawLevelField(ax, ay);
8427 AmoebaCnt[AmoebaNr[ax][ay]]--;
8429 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8431 if (element == EL_AMOEBA_FULL)
8432 AmoebeUmwandeln(ax, ay);
8433 else if (element == EL_BD_AMOEBA)
8434 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8439 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8441 /* amoeba gets larger by growing in some direction */
8443 int new_group_nr = AmoebaNr[ax][ay];
8446 if (new_group_nr == 0)
8448 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8449 printf("AmoebeAbleger(): This should never happen!\n");
8454 AmoebaNr[newax][neway] = new_group_nr;
8455 AmoebaCnt[new_group_nr]++;
8456 AmoebaCnt2[new_group_nr]++;
8458 /* if amoeba touches other amoeba(s) after growing, unify them */
8459 AmoebenVereinigen(newax, neway);
8461 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8463 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8469 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8470 (neway == lev_fieldy - 1 && newax != ax))
8472 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8473 Store[newax][neway] = element;
8475 else if (neway == ay || element == EL_EMC_DRIPPER)
8477 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8479 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8483 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8484 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8485 Store[ax][ay] = EL_AMOEBA_DROP;
8486 ContinueMoving(ax, ay);
8490 DrawLevelField(newax, neway);
8493 void Life(int ax, int ay)
8497 int element = Feld[ax][ay];
8498 int graphic = el2img(element);
8499 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8501 boolean changed = FALSE;
8503 if (IS_ANIMATED(graphic))
8504 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8509 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8510 MovDelay[ax][ay] = life_time;
8512 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8515 if (MovDelay[ax][ay])
8519 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8521 int xx = ax+x1, yy = ay+y1;
8524 if (!IN_LEV_FIELD(xx, yy))
8527 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8529 int x = xx+x2, y = yy+y2;
8531 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8534 if (((Feld[x][y] == element ||
8535 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8537 (IS_FREE(x, y) && Stop[x][y]))
8541 if (xx == ax && yy == ay) /* field in the middle */
8543 if (nachbarn < life_parameter[0] ||
8544 nachbarn > life_parameter[1])
8546 Feld[xx][yy] = EL_EMPTY;
8548 DrawLevelField(xx, yy);
8549 Stop[xx][yy] = TRUE;
8553 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8554 { /* free border field */
8555 if (nachbarn >= life_parameter[2] &&
8556 nachbarn <= life_parameter[3])
8558 Feld[xx][yy] = element;
8559 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8561 DrawLevelField(xx, yy);
8562 Stop[xx][yy] = TRUE;
8569 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8570 SND_GAME_OF_LIFE_GROWING);
8573 static void InitRobotWheel(int x, int y)
8575 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8578 static void RunRobotWheel(int x, int y)
8580 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8583 static void StopRobotWheel(int x, int y)
8585 if (ZX == x && ZY == y)
8589 static void InitTimegateWheel(int x, int y)
8591 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8594 static void RunTimegateWheel(int x, int y)
8596 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8599 static void InitMagicBallDelay(int x, int y)
8602 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8604 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8608 static void ActivateMagicBall(int bx, int by)
8612 if (level.ball_random)
8614 int pos_border = RND(8); /* select one of the eight border elements */
8615 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8616 int xx = pos_content % 3;
8617 int yy = pos_content / 3;
8622 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8623 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8627 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8629 int xx = x - bx + 1;
8630 int yy = y - by + 1;
8632 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8633 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8637 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8640 void CheckExit(int x, int y)
8642 if (local_player->gems_still_needed > 0 ||
8643 local_player->sokobanfields_still_needed > 0 ||
8644 local_player->lights_still_needed > 0)
8646 int element = Feld[x][y];
8647 int graphic = el2img(element);
8649 if (IS_ANIMATED(graphic))
8650 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8655 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8658 Feld[x][y] = EL_EXIT_OPENING;
8660 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8663 void CheckExitEM(int x, int y)
8665 if (local_player->gems_still_needed > 0 ||
8666 local_player->sokobanfields_still_needed > 0 ||
8667 local_player->lights_still_needed > 0)
8669 int element = Feld[x][y];
8670 int graphic = el2img(element);
8672 if (IS_ANIMATED(graphic))
8673 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8678 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8681 Feld[x][y] = EL_EM_EXIT_OPENING;
8683 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8686 void CheckExitSteel(int x, int y)
8688 if (local_player->gems_still_needed > 0 ||
8689 local_player->sokobanfields_still_needed > 0 ||
8690 local_player->lights_still_needed > 0)
8692 int element = Feld[x][y];
8693 int graphic = el2img(element);
8695 if (IS_ANIMATED(graphic))
8696 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8701 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8704 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8706 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8709 void CheckExitSteelEM(int x, int y)
8711 if (local_player->gems_still_needed > 0 ||
8712 local_player->sokobanfields_still_needed > 0 ||
8713 local_player->lights_still_needed > 0)
8715 int element = Feld[x][y];
8716 int graphic = el2img(element);
8718 if (IS_ANIMATED(graphic))
8719 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8724 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8727 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8729 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8732 void CheckExitSP(int x, int y)
8734 if (local_player->gems_still_needed > 0)
8736 int element = Feld[x][y];
8737 int graphic = el2img(element);
8739 if (IS_ANIMATED(graphic))
8740 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8745 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8748 Feld[x][y] = EL_SP_EXIT_OPENING;
8750 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8753 static void CloseAllOpenTimegates()
8757 SCAN_PLAYFIELD(x, y)
8759 int element = Feld[x][y];
8761 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8763 Feld[x][y] = EL_TIMEGATE_CLOSING;
8765 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8770 void DrawTwinkleOnField(int x, int y)
8772 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8775 if (Feld[x][y] == EL_BD_DIAMOND)
8778 if (MovDelay[x][y] == 0) /* next animation frame */
8779 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8781 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8785 if (setup.direct_draw && MovDelay[x][y])
8786 SetDrawtoField(DRAW_BUFFERED);
8788 DrawLevelElementAnimation(x, y, Feld[x][y]);
8790 if (MovDelay[x][y] != 0)
8792 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8793 10 - MovDelay[x][y]);
8795 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8797 if (setup.direct_draw)
8801 dest_x = FX + SCREENX(x) * TILEX;
8802 dest_y = FY + SCREENY(y) * TILEY;
8804 BlitBitmap(drawto_field, window,
8805 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8806 SetDrawtoField(DRAW_DIRECT);
8812 void MauerWaechst(int x, int y)
8816 if (!MovDelay[x][y]) /* next animation frame */
8817 MovDelay[x][y] = 3 * delay;
8819 if (MovDelay[x][y]) /* wait some time before next frame */
8823 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8825 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8826 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8828 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8831 if (!MovDelay[x][y])
8833 if (MovDir[x][y] == MV_LEFT)
8835 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8836 DrawLevelField(x - 1, y);
8838 else if (MovDir[x][y] == MV_RIGHT)
8840 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8841 DrawLevelField(x + 1, y);
8843 else if (MovDir[x][y] == MV_UP)
8845 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8846 DrawLevelField(x, y - 1);
8850 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8851 DrawLevelField(x, y + 1);
8854 Feld[x][y] = Store[x][y];
8856 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8857 DrawLevelField(x, y);
8862 void MauerAbleger(int ax, int ay)
8864 int element = Feld[ax][ay];
8865 int graphic = el2img(element);
8866 boolean oben_frei = FALSE, unten_frei = FALSE;
8867 boolean links_frei = FALSE, rechts_frei = FALSE;
8868 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8869 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8870 boolean new_wall = FALSE;
8872 if (IS_ANIMATED(graphic))
8873 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8875 if (!MovDelay[ax][ay]) /* start building new wall */
8876 MovDelay[ax][ay] = 6;
8878 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8881 if (MovDelay[ax][ay])
8885 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8887 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8889 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8891 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8894 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8895 element == EL_EXPANDABLE_WALL_ANY)
8899 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8900 Store[ax][ay-1] = element;
8901 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8902 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8903 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8904 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8909 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8910 Store[ax][ay+1] = element;
8911 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8912 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8913 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8914 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8919 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8920 element == EL_EXPANDABLE_WALL_ANY ||
8921 element == EL_EXPANDABLE_WALL ||
8922 element == EL_BD_EXPANDABLE_WALL)
8926 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8927 Store[ax-1][ay] = element;
8928 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8929 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8930 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8931 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8937 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8938 Store[ax+1][ay] = element;
8939 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8940 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8941 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8942 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8947 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8948 DrawLevelField(ax, ay);
8950 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8952 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8953 unten_massiv = TRUE;
8954 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8955 links_massiv = TRUE;
8956 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8957 rechts_massiv = TRUE;
8959 if (((oben_massiv && unten_massiv) ||
8960 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8961 element == EL_EXPANDABLE_WALL) &&
8962 ((links_massiv && rechts_massiv) ||
8963 element == EL_EXPANDABLE_WALL_VERTICAL))
8964 Feld[ax][ay] = EL_WALL;
8967 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8970 void MauerAblegerStahl(int ax, int ay)
8972 int element = Feld[ax][ay];
8973 int graphic = el2img(element);
8974 boolean oben_frei = FALSE, unten_frei = FALSE;
8975 boolean links_frei = FALSE, rechts_frei = FALSE;
8976 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8977 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8978 boolean new_wall = FALSE;
8980 if (IS_ANIMATED(graphic))
8981 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8983 if (!MovDelay[ax][ay]) /* start building new wall */
8984 MovDelay[ax][ay] = 6;
8986 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8989 if (MovDelay[ax][ay])
8993 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8995 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8997 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8999 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9002 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9003 element == EL_EXPANDABLE_STEELWALL_ANY)
9007 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9008 Store[ax][ay-1] = element;
9009 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9010 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9011 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9012 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9017 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9018 Store[ax][ay+1] = element;
9019 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9020 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9021 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9022 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9027 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9028 element == EL_EXPANDABLE_STEELWALL_ANY)
9032 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9033 Store[ax-1][ay] = element;
9034 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9035 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9036 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9037 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9043 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9044 Store[ax+1][ay] = element;
9045 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9046 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9047 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9048 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9053 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9055 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9056 unten_massiv = TRUE;
9057 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9058 links_massiv = TRUE;
9059 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9060 rechts_massiv = TRUE;
9062 if (((oben_massiv && unten_massiv) ||
9063 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9064 ((links_massiv && rechts_massiv) ||
9065 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9066 Feld[ax][ay] = EL_WALL;
9069 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9072 void CheckForDragon(int x, int y)
9075 boolean dragon_found = FALSE;
9076 static int xy[4][2] =
9084 for (i = 0; i < NUM_DIRECTIONS; i++)
9086 for (j = 0; j < 4; j++)
9088 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9090 if (IN_LEV_FIELD(xx, yy) &&
9091 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9093 if (Feld[xx][yy] == EL_DRAGON)
9094 dragon_found = TRUE;
9103 for (i = 0; i < NUM_DIRECTIONS; i++)
9105 for (j = 0; j < 3; j++)
9107 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9109 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9111 Feld[xx][yy] = EL_EMPTY;
9112 DrawLevelField(xx, yy);
9121 static void InitBuggyBase(int x, int y)
9123 int element = Feld[x][y];
9124 int activating_delay = FRAMES_PER_SECOND / 4;
9127 (element == EL_SP_BUGGY_BASE ?
9128 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9129 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9131 element == EL_SP_BUGGY_BASE_ACTIVE ?
9132 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9135 static void WarnBuggyBase(int x, int y)
9138 static int xy[4][2] =
9146 for (i = 0; i < NUM_DIRECTIONS; i++)
9148 int xx = x + xy[i][0];
9149 int yy = y + xy[i][1];
9151 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9153 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9160 static void InitTrap(int x, int y)
9162 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9165 static void ActivateTrap(int x, int y)
9167 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9170 static void ChangeActiveTrap(int x, int y)
9172 int graphic = IMG_TRAP_ACTIVE;
9174 /* if new animation frame was drawn, correct crumbled sand border */
9175 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9176 DrawLevelFieldCrumbledSand(x, y);
9179 static int getSpecialActionElement(int element, int number, int base_element)
9181 return (element != EL_EMPTY ? element :
9182 number != -1 ? base_element + number - 1 :
9186 static int getModifiedActionNumber(int value_old, int operator, int operand,
9187 int value_min, int value_max)
9189 int value_new = (operator == CA_MODE_SET ? operand :
9190 operator == CA_MODE_ADD ? value_old + operand :
9191 operator == CA_MODE_SUBTRACT ? value_old - operand :
9192 operator == CA_MODE_MULTIPLY ? value_old * operand :
9193 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9194 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9197 return (value_new < value_min ? value_min :
9198 value_new > value_max ? value_max :
9202 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9204 struct ElementInfo *ei = &element_info[element];
9205 struct ElementChangeInfo *change = &ei->change_page[page];
9206 int target_element = change->target_element;
9207 int action_type = change->action_type;
9208 int action_mode = change->action_mode;
9209 int action_arg = change->action_arg;
9212 if (!change->has_action)
9215 /* ---------- determine action paramater values -------------------------- */
9217 int level_time_value =
9218 (level.time > 0 ? TimeLeft :
9221 int action_arg_element =
9222 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9223 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9224 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9227 int action_arg_direction =
9228 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9229 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9230 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9231 change->actual_trigger_side :
9232 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9233 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9236 int action_arg_number_min =
9237 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9240 int action_arg_number_max =
9241 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9242 action_type == CA_SET_LEVEL_GEMS ? 999 :
9243 action_type == CA_SET_LEVEL_TIME ? 9999 :
9244 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9245 action_type == CA_SET_CE_VALUE ? 9999 :
9246 action_type == CA_SET_CE_SCORE ? 9999 :
9249 int action_arg_number_reset =
9250 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9251 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9252 action_type == CA_SET_LEVEL_TIME ? level.time :
9253 action_type == CA_SET_LEVEL_SCORE ? 0 :
9254 #if USE_NEW_CUSTOM_VALUE
9255 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9257 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9259 action_type == CA_SET_CE_SCORE ? 0 :
9262 int action_arg_number =
9263 (action_arg <= CA_ARG_MAX ? action_arg :
9264 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9265 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9266 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9267 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9268 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9269 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9270 #if USE_NEW_CUSTOM_VALUE
9271 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9273 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9275 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9276 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9277 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9278 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9279 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9280 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9281 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9282 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9283 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9284 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9285 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9288 int action_arg_number_old =
9289 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9290 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9291 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9292 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9293 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9296 int action_arg_number_new =
9297 getModifiedActionNumber(action_arg_number_old,
9298 action_mode, action_arg_number,
9299 action_arg_number_min, action_arg_number_max);
9301 int trigger_player_bits =
9302 (change->actual_trigger_player >= EL_PLAYER_1 &&
9303 change->actual_trigger_player <= EL_PLAYER_4 ?
9304 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9307 int action_arg_player_bits =
9308 (action_arg >= CA_ARG_PLAYER_1 &&
9309 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9310 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9313 /* ---------- execute action -------------------------------------------- */
9315 switch (action_type)
9322 /* ---------- level actions ------------------------------------------- */
9324 case CA_RESTART_LEVEL:
9326 game.restart_level = TRUE;
9331 case CA_SHOW_ENVELOPE:
9333 int element = getSpecialActionElement(action_arg_element,
9334 action_arg_number, EL_ENVELOPE_1);
9336 if (IS_ENVELOPE(element))
9337 local_player->show_envelope = element;
9342 case CA_SET_LEVEL_TIME:
9344 if (level.time > 0) /* only modify limited time value */
9346 TimeLeft = action_arg_number_new;
9349 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
9351 DisplayGameControlValues();
9353 DrawGameValue_Time(TimeLeft);
9356 if (!TimeLeft && setup.time_limit)
9357 for (i = 0; i < MAX_PLAYERS; i++)
9358 KillPlayer(&stored_player[i]);
9364 case CA_SET_LEVEL_SCORE:
9366 local_player->score = action_arg_number_new;
9369 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
9371 DisplayGameControlValues();
9373 DrawGameValue_Score(local_player->score);
9379 case CA_SET_LEVEL_GEMS:
9381 local_player->gems_still_needed = action_arg_number_new;
9384 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
9386 DisplayGameControlValues();
9388 DrawGameValue_Emeralds(local_player->gems_still_needed);
9394 #if !USE_PLAYER_GRAVITY
9395 case CA_SET_LEVEL_GRAVITY:
9397 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9398 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9399 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9405 case CA_SET_LEVEL_WIND:
9407 game.wind_direction = action_arg_direction;
9412 /* ---------- player actions ------------------------------------------ */
9414 case CA_MOVE_PLAYER:
9416 /* automatically move to the next field in specified direction */
9417 for (i = 0; i < MAX_PLAYERS; i++)
9418 if (trigger_player_bits & (1 << i))
9419 stored_player[i].programmed_action = action_arg_direction;
9424 case CA_EXIT_PLAYER:
9426 for (i = 0; i < MAX_PLAYERS; i++)
9427 if (action_arg_player_bits & (1 << i))
9428 PlayerWins(&stored_player[i]);
9433 case CA_KILL_PLAYER:
9435 for (i = 0; i < MAX_PLAYERS; i++)
9436 if (action_arg_player_bits & (1 << i))
9437 KillPlayer(&stored_player[i]);
9442 case CA_SET_PLAYER_KEYS:
9444 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9445 int element = getSpecialActionElement(action_arg_element,
9446 action_arg_number, EL_KEY_1);
9448 if (IS_KEY(element))
9450 for (i = 0; i < MAX_PLAYERS; i++)
9452 if (trigger_player_bits & (1 << i))
9454 stored_player[i].key[KEY_NR(element)] = key_state;
9456 DrawGameDoorValues();
9464 case CA_SET_PLAYER_SPEED:
9466 for (i = 0; i < MAX_PLAYERS; i++)
9468 if (trigger_player_bits & (1 << i))
9470 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9472 if (action_arg == CA_ARG_SPEED_FASTER &&
9473 stored_player[i].cannot_move)
9475 action_arg_number = STEPSIZE_VERY_SLOW;
9477 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9478 action_arg == CA_ARG_SPEED_FASTER)
9480 action_arg_number = 2;
9481 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9484 else if (action_arg == CA_ARG_NUMBER_RESET)
9486 action_arg_number = level.initial_player_stepsize[i];
9490 getModifiedActionNumber(move_stepsize,
9493 action_arg_number_min,
9494 action_arg_number_max);
9496 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9503 case CA_SET_PLAYER_SHIELD:
9505 for (i = 0; i < MAX_PLAYERS; i++)
9507 if (trigger_player_bits & (1 << i))
9509 if (action_arg == CA_ARG_SHIELD_OFF)
9511 stored_player[i].shield_normal_time_left = 0;
9512 stored_player[i].shield_deadly_time_left = 0;
9514 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9516 stored_player[i].shield_normal_time_left = 999999;
9518 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9520 stored_player[i].shield_normal_time_left = 999999;
9521 stored_player[i].shield_deadly_time_left = 999999;
9529 #if USE_PLAYER_GRAVITY
9530 case CA_SET_PLAYER_GRAVITY:
9532 for (i = 0; i < MAX_PLAYERS; i++)
9534 if (trigger_player_bits & (1 << i))
9536 stored_player[i].gravity =
9537 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9538 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9539 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9540 stored_player[i].gravity);
9548 case CA_SET_PLAYER_ARTWORK:
9550 for (i = 0; i < MAX_PLAYERS; i++)
9552 if (trigger_player_bits & (1 << i))
9554 int artwork_element = action_arg_element;
9556 if (action_arg == CA_ARG_ELEMENT_RESET)
9558 (level.use_artwork_element[i] ? level.artwork_element[i] :
9559 stored_player[i].element_nr);
9561 #if USE_GFX_RESET_PLAYER_ARTWORK
9562 if (stored_player[i].artwork_element != artwork_element)
9563 stored_player[i].Frame = 0;
9566 stored_player[i].artwork_element = artwork_element;
9568 SetPlayerWaiting(&stored_player[i], FALSE);
9570 /* set number of special actions for bored and sleeping animation */
9571 stored_player[i].num_special_action_bored =
9572 get_num_special_action(artwork_element,
9573 ACTION_BORING_1, ACTION_BORING_LAST);
9574 stored_player[i].num_special_action_sleeping =
9575 get_num_special_action(artwork_element,
9576 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9583 /* ---------- CE actions ---------------------------------------------- */
9585 case CA_SET_CE_VALUE:
9587 #if USE_NEW_CUSTOM_VALUE
9588 int last_ce_value = CustomValue[x][y];
9590 CustomValue[x][y] = action_arg_number_new;
9592 if (CustomValue[x][y] != last_ce_value)
9594 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9595 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9597 if (CustomValue[x][y] == 0)
9599 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9600 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9608 case CA_SET_CE_SCORE:
9610 #if USE_NEW_CUSTOM_VALUE
9611 int last_ce_score = ei->collect_score;
9613 ei->collect_score = action_arg_number_new;
9615 if (ei->collect_score != last_ce_score)
9617 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9618 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9620 if (ei->collect_score == 0)
9624 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9625 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9628 This is a very special case that seems to be a mixture between
9629 CheckElementChange() and CheckTriggeredElementChange(): while
9630 the first one only affects single elements that are triggered
9631 directly, the second one affects multiple elements in the playfield
9632 that are triggered indirectly by another element. This is a third
9633 case: Changing the CE score always affects multiple identical CEs,
9634 so every affected CE must be checked, not only the single CE for
9635 which the CE score was changed in the first place (as every instance
9636 of that CE shares the same CE score, and therefore also can change)!
9638 SCAN_PLAYFIELD(xx, yy)
9640 if (Feld[xx][yy] == element)
9641 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9642 CE_SCORE_GETS_ZERO);
9651 /* ---------- engine actions ------------------------------------------ */
9653 case CA_SET_ENGINE_SCAN_MODE:
9655 InitPlayfieldScanMode(action_arg);
9665 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9667 int old_element = Feld[x][y];
9668 int new_element = GetElementFromGroupElement(element);
9669 int previous_move_direction = MovDir[x][y];
9670 #if USE_NEW_CUSTOM_VALUE
9671 int last_ce_value = CustomValue[x][y];
9673 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9674 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9675 boolean add_player_onto_element = (new_element_is_player &&
9676 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9677 /* this breaks SnakeBite when a snake is
9678 halfway through a door that closes */
9679 /* NOW FIXED AT LEVEL INIT IN files.c */
9680 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9682 IS_WALKABLE(old_element));
9685 /* check if element under the player changes from accessible to unaccessible
9686 (needed for special case of dropping element which then changes) */
9687 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9688 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9696 if (!add_player_onto_element)
9698 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9699 RemoveMovingField(x, y);
9703 Feld[x][y] = new_element;
9705 #if !USE_GFX_RESET_GFX_ANIMATION
9706 ResetGfxAnimation(x, y);
9707 ResetRandomAnimationValue(x, y);
9710 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9711 MovDir[x][y] = previous_move_direction;
9713 #if USE_NEW_CUSTOM_VALUE
9714 if (element_info[new_element].use_last_ce_value)
9715 CustomValue[x][y] = last_ce_value;
9718 InitField_WithBug1(x, y, FALSE);
9720 new_element = Feld[x][y]; /* element may have changed */
9722 #if USE_GFX_RESET_GFX_ANIMATION
9723 ResetGfxAnimation(x, y);
9724 ResetRandomAnimationValue(x, y);
9727 DrawLevelField(x, y);
9729 if (GFX_CRUMBLED(new_element))
9730 DrawLevelFieldCrumbledSandNeighbours(x, y);
9734 /* check if element under the player changes from accessible to unaccessible
9735 (needed for special case of dropping element which then changes) */
9736 /* (must be checked after creating new element for walkable group elements) */
9737 #if USE_FIX_KILLED_BY_NON_WALKABLE
9738 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9739 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9746 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9747 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9756 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9757 if (new_element_is_player)
9758 RelocatePlayer(x, y, new_element);
9761 ChangeCount[x][y]++; /* count number of changes in the same frame */
9763 TestIfBadThingTouchesPlayer(x, y);
9764 TestIfPlayerTouchesCustomElement(x, y);
9765 TestIfElementTouchesCustomElement(x, y);
9768 static void CreateField(int x, int y, int element)
9770 CreateFieldExt(x, y, element, FALSE);
9773 static void CreateElementFromChange(int x, int y, int element)
9775 element = GET_VALID_RUNTIME_ELEMENT(element);
9777 #if USE_STOP_CHANGED_ELEMENTS
9778 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9780 int old_element = Feld[x][y];
9782 /* prevent changed element from moving in same engine frame
9783 unless both old and new element can either fall or move */
9784 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9785 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9790 CreateFieldExt(x, y, element, TRUE);
9793 static boolean ChangeElement(int x, int y, int element, int page)
9795 struct ElementInfo *ei = &element_info[element];
9796 struct ElementChangeInfo *change = &ei->change_page[page];
9797 int ce_value = CustomValue[x][y];
9798 int ce_score = ei->collect_score;
9800 int old_element = Feld[x][y];
9802 /* always use default change event to prevent running into a loop */
9803 if (ChangeEvent[x][y] == -1)
9804 ChangeEvent[x][y] = CE_DELAY;
9806 if (ChangeEvent[x][y] == CE_DELAY)
9808 /* reset actual trigger element, trigger player and action element */
9809 change->actual_trigger_element = EL_EMPTY;
9810 change->actual_trigger_player = EL_PLAYER_1;
9811 change->actual_trigger_side = CH_SIDE_NONE;
9812 change->actual_trigger_ce_value = 0;
9813 change->actual_trigger_ce_score = 0;
9816 /* do not change elements more than a specified maximum number of changes */
9817 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9820 ChangeCount[x][y]++; /* count number of changes in the same frame */
9822 if (change->explode)
9829 if (change->use_target_content)
9831 boolean complete_replace = TRUE;
9832 boolean can_replace[3][3];
9835 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9838 boolean is_walkable;
9839 boolean is_diggable;
9840 boolean is_collectible;
9841 boolean is_removable;
9842 boolean is_destructible;
9843 int ex = x + xx - 1;
9844 int ey = y + yy - 1;
9845 int content_element = change->target_content.e[xx][yy];
9848 can_replace[xx][yy] = TRUE;
9850 if (ex == x && ey == y) /* do not check changing element itself */
9853 if (content_element == EL_EMPTY_SPACE)
9855 can_replace[xx][yy] = FALSE; /* do not replace border with space */
9860 if (!IN_LEV_FIELD(ex, ey))
9862 can_replace[xx][yy] = FALSE;
9863 complete_replace = FALSE;
9870 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9871 e = MovingOrBlocked2Element(ex, ey);
9873 is_empty = (IS_FREE(ex, ey) ||
9874 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9876 is_walkable = (is_empty || IS_WALKABLE(e));
9877 is_diggable = (is_empty || IS_DIGGABLE(e));
9878 is_collectible = (is_empty || IS_COLLECTIBLE(e));
9879 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9880 is_removable = (is_diggable || is_collectible);
9882 can_replace[xx][yy] =
9883 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
9884 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
9885 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
9886 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
9887 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
9888 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9889 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9891 if (!can_replace[xx][yy])
9892 complete_replace = FALSE;
9895 if (!change->only_if_complete || complete_replace)
9897 boolean something_has_changed = FALSE;
9899 if (change->only_if_complete && change->use_random_replace &&
9900 RND(100) < change->random_percentage)
9903 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9905 int ex = x + xx - 1;
9906 int ey = y + yy - 1;
9907 int content_element;
9909 if (can_replace[xx][yy] && (!change->use_random_replace ||
9910 RND(100) < change->random_percentage))
9912 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9913 RemoveMovingField(ex, ey);
9915 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9917 content_element = change->target_content.e[xx][yy];
9918 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9919 ce_value, ce_score);
9921 CreateElementFromChange(ex, ey, target_element);
9923 something_has_changed = TRUE;
9925 /* for symmetry reasons, freeze newly created border elements */
9926 if (ex != x || ey != y)
9927 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9931 if (something_has_changed)
9933 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9934 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9940 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9941 ce_value, ce_score);
9943 if (element == EL_DIAGONAL_GROWING ||
9944 element == EL_DIAGONAL_SHRINKING)
9946 target_element = Store[x][y];
9948 Store[x][y] = EL_EMPTY;
9951 CreateElementFromChange(x, y, target_element);
9953 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9954 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9957 /* this uses direct change before indirect change */
9958 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9963 #if USE_NEW_DELAYED_ACTION
9965 static void HandleElementChange(int x, int y, int page)
9967 int element = MovingOrBlocked2Element(x, y);
9968 struct ElementInfo *ei = &element_info[element];
9969 struct ElementChangeInfo *change = &ei->change_page[page];
9972 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9973 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9976 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9977 x, y, element, element_info[element].token_name);
9978 printf("HandleElementChange(): This should never happen!\n");
9983 /* this can happen with classic bombs on walkable, changing elements */
9984 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9987 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9988 ChangeDelay[x][y] = 0;
9994 if (ChangeDelay[x][y] == 0) /* initialize element change */
9996 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9998 if (change->can_change)
10001 /* !!! not clear why graphic animation should be reset at all here !!! */
10002 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10003 #if USE_GFX_RESET_WHEN_NOT_MOVING
10004 /* when a custom element is about to change (for example by change delay),
10005 do not reset graphic animation when the custom element is moving */
10006 if (!IS_MOVING(x, y))
10009 ResetGfxAnimation(x, y);
10010 ResetRandomAnimationValue(x, y);
10014 if (change->pre_change_function)
10015 change->pre_change_function(x, y);
10019 ChangeDelay[x][y]--;
10021 if (ChangeDelay[x][y] != 0) /* continue element change */
10023 if (change->can_change)
10025 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10027 if (IS_ANIMATED(graphic))
10028 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10030 if (change->change_function)
10031 change->change_function(x, y);
10034 else /* finish element change */
10036 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10038 page = ChangePage[x][y];
10039 ChangePage[x][y] = -1;
10041 change = &ei->change_page[page];
10044 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10046 ChangeDelay[x][y] = 1; /* try change after next move step */
10047 ChangePage[x][y] = page; /* remember page to use for change */
10052 if (change->can_change)
10054 if (ChangeElement(x, y, element, page))
10056 if (change->post_change_function)
10057 change->post_change_function(x, y);
10061 if (change->has_action)
10062 ExecuteCustomElementAction(x, y, element, page);
10068 static void HandleElementChange(int x, int y, int page)
10070 int element = MovingOrBlocked2Element(x, y);
10071 struct ElementInfo *ei = &element_info[element];
10072 struct ElementChangeInfo *change = &ei->change_page[page];
10075 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10078 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10079 x, y, element, element_info[element].token_name);
10080 printf("HandleElementChange(): This should never happen!\n");
10085 /* this can happen with classic bombs on walkable, changing elements */
10086 if (!CAN_CHANGE(element))
10089 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10090 ChangeDelay[x][y] = 0;
10096 if (ChangeDelay[x][y] == 0) /* initialize element change */
10098 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10100 ResetGfxAnimation(x, y);
10101 ResetRandomAnimationValue(x, y);
10103 if (change->pre_change_function)
10104 change->pre_change_function(x, y);
10107 ChangeDelay[x][y]--;
10109 if (ChangeDelay[x][y] != 0) /* continue element change */
10111 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10113 if (IS_ANIMATED(graphic))
10114 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10116 if (change->change_function)
10117 change->change_function(x, y);
10119 else /* finish element change */
10121 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10123 page = ChangePage[x][y];
10124 ChangePage[x][y] = -1;
10126 change = &ei->change_page[page];
10129 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10131 ChangeDelay[x][y] = 1; /* try change after next move step */
10132 ChangePage[x][y] = page; /* remember page to use for change */
10137 if (ChangeElement(x, y, element, page))
10139 if (change->post_change_function)
10140 change->post_change_function(x, y);
10147 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10148 int trigger_element,
10150 int trigger_player,
10154 boolean change_done_any = FALSE;
10155 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10158 if (!(trigger_events[trigger_element][trigger_event]))
10162 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10163 trigger_event, recursion_loop_depth, recursion_loop_detected,
10164 recursion_loop_element, EL_NAME(recursion_loop_element));
10167 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10169 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10171 int element = EL_CUSTOM_START + i;
10172 boolean change_done = FALSE;
10175 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10176 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10179 for (p = 0; p < element_info[element].num_change_pages; p++)
10181 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10183 if (change->can_change_or_has_action &&
10184 change->has_event[trigger_event] &&
10185 change->trigger_side & trigger_side &&
10186 change->trigger_player & trigger_player &&
10187 change->trigger_page & trigger_page_bits &&
10188 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10190 change->actual_trigger_element = trigger_element;
10191 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10192 change->actual_trigger_side = trigger_side;
10193 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10194 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10196 if ((change->can_change && !change_done) || change->has_action)
10200 SCAN_PLAYFIELD(x, y)
10202 if (Feld[x][y] == element)
10204 if (change->can_change && !change_done)
10206 ChangeDelay[x][y] = 1;
10207 ChangeEvent[x][y] = trigger_event;
10209 HandleElementChange(x, y, p);
10211 #if USE_NEW_DELAYED_ACTION
10212 else if (change->has_action)
10214 ExecuteCustomElementAction(x, y, element, p);
10215 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10218 if (change->has_action)
10220 ExecuteCustomElementAction(x, y, element, p);
10221 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10227 if (change->can_change)
10229 change_done = TRUE;
10230 change_done_any = TRUE;
10237 RECURSION_LOOP_DETECTION_END();
10239 return change_done_any;
10242 static boolean CheckElementChangeExt(int x, int y,
10244 int trigger_element,
10246 int trigger_player,
10249 boolean change_done = FALSE;
10252 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10253 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10256 if (Feld[x][y] == EL_BLOCKED)
10258 Blocked2Moving(x, y, &x, &y);
10259 element = Feld[x][y];
10263 /* check if element has already changed */
10264 if (Feld[x][y] != element)
10267 /* check if element has already changed or is about to change after moving */
10268 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10269 Feld[x][y] != element) ||
10271 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10272 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10273 ChangePage[x][y] != -1)))
10278 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10279 trigger_event, recursion_loop_depth, recursion_loop_detected,
10280 recursion_loop_element, EL_NAME(recursion_loop_element));
10283 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10285 for (p = 0; p < element_info[element].num_change_pages; p++)
10287 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10289 /* check trigger element for all events where the element that is checked
10290 for changing interacts with a directly adjacent element -- this is
10291 different to element changes that affect other elements to change on the
10292 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10293 boolean check_trigger_element =
10294 (trigger_event == CE_TOUCHING_X ||
10295 trigger_event == CE_HITTING_X ||
10296 trigger_event == CE_HIT_BY_X ||
10298 /* this one was forgotten until 3.2.3 */
10299 trigger_event == CE_DIGGING_X);
10302 if (change->can_change_or_has_action &&
10303 change->has_event[trigger_event] &&
10304 change->trigger_side & trigger_side &&
10305 change->trigger_player & trigger_player &&
10306 (!check_trigger_element ||
10307 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10309 change->actual_trigger_element = trigger_element;
10310 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10311 change->actual_trigger_side = trigger_side;
10312 change->actual_trigger_ce_value = CustomValue[x][y];
10313 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10315 /* special case: trigger element not at (x,y) position for some events */
10316 if (check_trigger_element)
10328 { 0, 0 }, { 0, 0 }, { 0, 0 },
10332 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10333 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10335 change->actual_trigger_ce_value = CustomValue[xx][yy];
10336 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10339 if (change->can_change && !change_done)
10341 ChangeDelay[x][y] = 1;
10342 ChangeEvent[x][y] = trigger_event;
10344 HandleElementChange(x, y, p);
10346 change_done = TRUE;
10348 #if USE_NEW_DELAYED_ACTION
10349 else if (change->has_action)
10351 ExecuteCustomElementAction(x, y, element, p);
10352 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10355 if (change->has_action)
10357 ExecuteCustomElementAction(x, y, element, p);
10358 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10364 RECURSION_LOOP_DETECTION_END();
10366 return change_done;
10369 static void PlayPlayerSound(struct PlayerInfo *player)
10371 int jx = player->jx, jy = player->jy;
10372 int sound_element = player->artwork_element;
10373 int last_action = player->last_action_waiting;
10374 int action = player->action_waiting;
10376 if (player->is_waiting)
10378 if (action != last_action)
10379 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10381 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10385 if (action != last_action)
10386 StopSound(element_info[sound_element].sound[last_action]);
10388 if (last_action == ACTION_SLEEPING)
10389 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10393 static void PlayAllPlayersSound()
10397 for (i = 0; i < MAX_PLAYERS; i++)
10398 if (stored_player[i].active)
10399 PlayPlayerSound(&stored_player[i]);
10402 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10404 boolean last_waiting = player->is_waiting;
10405 int move_dir = player->MovDir;
10407 player->dir_waiting = move_dir;
10408 player->last_action_waiting = player->action_waiting;
10412 if (!last_waiting) /* not waiting -> waiting */
10414 player->is_waiting = TRUE;
10416 player->frame_counter_bored =
10418 game.player_boring_delay_fixed +
10419 GetSimpleRandom(game.player_boring_delay_random);
10420 player->frame_counter_sleeping =
10422 game.player_sleeping_delay_fixed +
10423 GetSimpleRandom(game.player_sleeping_delay_random);
10425 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10428 if (game.player_sleeping_delay_fixed +
10429 game.player_sleeping_delay_random > 0 &&
10430 player->anim_delay_counter == 0 &&
10431 player->post_delay_counter == 0 &&
10432 FrameCounter >= player->frame_counter_sleeping)
10433 player->is_sleeping = TRUE;
10434 else if (game.player_boring_delay_fixed +
10435 game.player_boring_delay_random > 0 &&
10436 FrameCounter >= player->frame_counter_bored)
10437 player->is_bored = TRUE;
10439 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10440 player->is_bored ? ACTION_BORING :
10443 if (player->is_sleeping && player->use_murphy)
10445 /* special case for sleeping Murphy when leaning against non-free tile */
10447 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10448 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10449 !IS_MOVING(player->jx - 1, player->jy)))
10450 move_dir = MV_LEFT;
10451 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10452 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10453 !IS_MOVING(player->jx + 1, player->jy)))
10454 move_dir = MV_RIGHT;
10456 player->is_sleeping = FALSE;
10458 player->dir_waiting = move_dir;
10461 if (player->is_sleeping)
10463 if (player->num_special_action_sleeping > 0)
10465 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10467 int last_special_action = player->special_action_sleeping;
10468 int num_special_action = player->num_special_action_sleeping;
10469 int special_action =
10470 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10471 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10472 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10473 last_special_action + 1 : ACTION_SLEEPING);
10474 int special_graphic =
10475 el_act_dir2img(player->artwork_element, special_action, move_dir);
10477 player->anim_delay_counter =
10478 graphic_info[special_graphic].anim_delay_fixed +
10479 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10480 player->post_delay_counter =
10481 graphic_info[special_graphic].post_delay_fixed +
10482 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10484 player->special_action_sleeping = special_action;
10487 if (player->anim_delay_counter > 0)
10489 player->action_waiting = player->special_action_sleeping;
10490 player->anim_delay_counter--;
10492 else if (player->post_delay_counter > 0)
10494 player->post_delay_counter--;
10498 else if (player->is_bored)
10500 if (player->num_special_action_bored > 0)
10502 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10504 int special_action =
10505 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10506 int special_graphic =
10507 el_act_dir2img(player->artwork_element, special_action, move_dir);
10509 player->anim_delay_counter =
10510 graphic_info[special_graphic].anim_delay_fixed +
10511 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10512 player->post_delay_counter =
10513 graphic_info[special_graphic].post_delay_fixed +
10514 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10516 player->special_action_bored = special_action;
10519 if (player->anim_delay_counter > 0)
10521 player->action_waiting = player->special_action_bored;
10522 player->anim_delay_counter--;
10524 else if (player->post_delay_counter > 0)
10526 player->post_delay_counter--;
10531 else if (last_waiting) /* waiting -> not waiting */
10533 player->is_waiting = FALSE;
10534 player->is_bored = FALSE;
10535 player->is_sleeping = FALSE;
10537 player->frame_counter_bored = -1;
10538 player->frame_counter_sleeping = -1;
10540 player->anim_delay_counter = 0;
10541 player->post_delay_counter = 0;
10543 player->dir_waiting = player->MovDir;
10544 player->action_waiting = ACTION_DEFAULT;
10546 player->special_action_bored = ACTION_DEFAULT;
10547 player->special_action_sleeping = ACTION_DEFAULT;
10551 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10553 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10554 int left = player_action & JOY_LEFT;
10555 int right = player_action & JOY_RIGHT;
10556 int up = player_action & JOY_UP;
10557 int down = player_action & JOY_DOWN;
10558 int button1 = player_action & JOY_BUTTON_1;
10559 int button2 = player_action & JOY_BUTTON_2;
10560 int dx = (left ? -1 : right ? 1 : 0);
10561 int dy = (up ? -1 : down ? 1 : 0);
10563 if (!player->active || tape.pausing)
10569 snapped = SnapField(player, dx, dy);
10573 dropped = DropElement(player);
10575 moved = MovePlayer(player, dx, dy);
10578 if (tape.single_step && tape.recording && !tape.pausing)
10580 if (button1 || (dropped && !moved))
10582 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10583 SnapField(player, 0, 0); /* stop snapping */
10587 SetPlayerWaiting(player, FALSE);
10589 return player_action;
10593 /* no actions for this player (no input at player's configured device) */
10595 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10596 SnapField(player, 0, 0);
10597 CheckGravityMovementWhenNotMoving(player);
10599 if (player->MovPos == 0)
10600 SetPlayerWaiting(player, TRUE);
10602 if (player->MovPos == 0) /* needed for tape.playing */
10603 player->is_moving = FALSE;
10605 player->is_dropping = FALSE;
10606 player->is_dropping_pressed = FALSE;
10607 player->drop_pressed_delay = 0;
10613 static void CheckLevelTime()
10617 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10619 if (level.native_em_level->lev->home == 0) /* all players at home */
10621 PlayerWins(local_player);
10623 AllPlayersGone = TRUE;
10625 level.native_em_level->lev->home = -1;
10628 if (level.native_em_level->ply[0]->alive == 0 &&
10629 level.native_em_level->ply[1]->alive == 0 &&
10630 level.native_em_level->ply[2]->alive == 0 &&
10631 level.native_em_level->ply[3]->alive == 0) /* all dead */
10632 AllPlayersGone = TRUE;
10635 if (TimeFrames >= FRAMES_PER_SECOND)
10640 for (i = 0; i < MAX_PLAYERS; i++)
10642 struct PlayerInfo *player = &stored_player[i];
10644 if (SHIELD_ON(player))
10646 player->shield_normal_time_left--;
10648 if (player->shield_deadly_time_left > 0)
10649 player->shield_deadly_time_left--;
10653 if (!local_player->LevelSolved && !level.use_step_counter)
10661 if (TimeLeft <= 10 && setup.time_limit)
10662 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10665 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
10667 DisplayGameControlValues();
10669 DrawGameValue_Time(TimeLeft);
10672 if (!TimeLeft && setup.time_limit)
10674 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10675 level.native_em_level->lev->killed_out_of_time = TRUE;
10677 for (i = 0; i < MAX_PLAYERS; i++)
10678 KillPlayer(&stored_player[i]);
10682 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10684 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
10686 DisplayGameControlValues();
10689 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10690 DrawGameValue_Time(TimePlayed);
10693 level.native_em_level->lev->time =
10694 (level.time == 0 ? TimePlayed : TimeLeft);
10697 if (tape.recording || tape.playing)
10698 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10701 DrawGameDoorValues();
10704 void AdvanceFrameAndPlayerCounters(int player_nr)
10708 /* advance frame counters (global frame counter and time frame counter) */
10712 /* advance player counters (counters for move delay, move animation etc.) */
10713 for (i = 0; i < MAX_PLAYERS; i++)
10715 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10716 int move_delay_value = stored_player[i].move_delay_value;
10717 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10719 if (!advance_player_counters) /* not all players may be affected */
10722 #if USE_NEW_PLAYER_ANIM
10723 if (move_frames == 0) /* less than one move per game frame */
10725 int stepsize = TILEX / move_delay_value;
10726 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10727 int count = (stored_player[i].is_moving ?
10728 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10730 if (count % delay == 0)
10735 stored_player[i].Frame += move_frames;
10737 if (stored_player[i].MovPos != 0)
10738 stored_player[i].StepFrame += move_frames;
10740 if (stored_player[i].move_delay > 0)
10741 stored_player[i].move_delay--;
10743 /* due to bugs in previous versions, counter must count up, not down */
10744 if (stored_player[i].push_delay != -1)
10745 stored_player[i].push_delay++;
10747 if (stored_player[i].drop_delay > 0)
10748 stored_player[i].drop_delay--;
10750 if (stored_player[i].is_dropping_pressed)
10751 stored_player[i].drop_pressed_delay++;
10755 void StartGameActions(boolean init_network_game, boolean record_tape,
10758 unsigned long new_random_seed = InitRND(random_seed);
10761 TapeStartRecording(new_random_seed);
10763 #if defined(NETWORK_AVALIABLE)
10764 if (init_network_game)
10766 SendToServer_StartPlaying();
10777 static unsigned long game_frame_delay = 0;
10778 unsigned long game_frame_delay_value;
10779 byte *recorded_player_action;
10780 byte summarized_player_action = 0;
10781 byte tape_action[MAX_PLAYERS];
10784 /* detect endless loops, caused by custom element programming */
10785 if (recursion_loop_detected && recursion_loop_depth == 0)
10787 char *message = getStringCat3("Internal Error ! Element ",
10788 EL_NAME(recursion_loop_element),
10789 " caused endless loop ! Quit the game ?");
10791 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10792 EL_NAME(recursion_loop_element));
10794 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10796 recursion_loop_detected = FALSE; /* if game should be continued */
10803 if (game.restart_level)
10804 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10806 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10808 if (level.native_em_level->lev->home == 0) /* all players at home */
10810 PlayerWins(local_player);
10812 AllPlayersGone = TRUE;
10814 level.native_em_level->lev->home = -1;
10817 if (level.native_em_level->ply[0]->alive == 0 &&
10818 level.native_em_level->ply[1]->alive == 0 &&
10819 level.native_em_level->ply[2]->alive == 0 &&
10820 level.native_em_level->ply[3]->alive == 0) /* all dead */
10821 AllPlayersGone = TRUE;
10824 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10827 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10830 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
10833 game_frame_delay_value =
10834 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10836 if (tape.playing && tape.warp_forward && !tape.pausing)
10837 game_frame_delay_value = 0;
10839 /* ---------- main game synchronization point ---------- */
10841 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10843 if (network_playing && !network_player_action_received)
10845 /* try to get network player actions in time */
10847 #if defined(NETWORK_AVALIABLE)
10848 /* last chance to get network player actions without main loop delay */
10849 HandleNetworking();
10852 /* game was quit by network peer */
10853 if (game_status != GAME_MODE_PLAYING)
10856 if (!network_player_action_received)
10857 return; /* failed to get network player actions in time */
10859 /* do not yet reset "network_player_action_received" (for tape.pausing) */
10865 /* at this point we know that we really continue executing the game */
10867 network_player_action_received = FALSE;
10869 /* when playing tape, read previously recorded player input from tape data */
10870 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10873 /* TapePlayAction() may return NULL when toggling to "pause before death" */
10878 if (tape.set_centered_player)
10880 game.centered_player_nr_next = tape.centered_player_nr_next;
10881 game.set_centered_player = TRUE;
10884 for (i = 0; i < MAX_PLAYERS; i++)
10886 summarized_player_action |= stored_player[i].action;
10888 if (!network_playing)
10889 stored_player[i].effective_action = stored_player[i].action;
10892 #if defined(NETWORK_AVALIABLE)
10893 if (network_playing)
10894 SendToServer_MovePlayer(summarized_player_action);
10897 if (!options.network && !setup.team_mode)
10898 local_player->effective_action = summarized_player_action;
10900 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10902 for (i = 0; i < MAX_PLAYERS; i++)
10903 stored_player[i].effective_action =
10904 (i == game.centered_player_nr ? summarized_player_action : 0);
10907 if (recorded_player_action != NULL)
10908 for (i = 0; i < MAX_PLAYERS; i++)
10909 stored_player[i].effective_action = recorded_player_action[i];
10911 for (i = 0; i < MAX_PLAYERS; i++)
10913 tape_action[i] = stored_player[i].effective_action;
10915 /* (this can only happen in the R'n'D game engine) */
10916 if (tape.recording && tape_action[i] && !tape.player_participates[i])
10917 tape.player_participates[i] = TRUE; /* player just appeared from CE */
10920 /* only record actions from input devices, but not programmed actions */
10921 if (tape.recording)
10922 TapeRecordAction(tape_action);
10924 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10926 GameActions_EM_Main();
10934 void GameActions_EM_Main()
10936 byte effective_action[MAX_PLAYERS];
10937 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10940 for (i = 0; i < MAX_PLAYERS; i++)
10941 effective_action[i] = stored_player[i].effective_action;
10943 GameActions_EM(effective_action, warp_mode);
10947 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10950 void GameActions_RND()
10952 int magic_wall_x = 0, magic_wall_y = 0;
10953 int i, x, y, element, graphic;
10955 InitPlayfieldScanModeVars();
10957 #if USE_ONE_MORE_CHANGE_PER_FRAME
10958 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10960 SCAN_PLAYFIELD(x, y)
10962 ChangeCount[x][y] = 0;
10963 ChangeEvent[x][y] = -1;
10968 if (game.set_centered_player)
10970 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10972 /* switching to "all players" only possible if all players fit to screen */
10973 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10975 game.centered_player_nr_next = game.centered_player_nr;
10976 game.set_centered_player = FALSE;
10979 /* do not switch focus to non-existing (or non-active) player */
10980 if (game.centered_player_nr_next >= 0 &&
10981 !stored_player[game.centered_player_nr_next].active)
10983 game.centered_player_nr_next = game.centered_player_nr;
10984 game.set_centered_player = FALSE;
10988 if (game.set_centered_player &&
10989 ScreenMovPos == 0) /* screen currently aligned at tile position */
10993 if (game.centered_player_nr_next == -1)
10995 setScreenCenteredToAllPlayers(&sx, &sy);
10999 sx = stored_player[game.centered_player_nr_next].jx;
11000 sy = stored_player[game.centered_player_nr_next].jy;
11003 game.centered_player_nr = game.centered_player_nr_next;
11004 game.set_centered_player = FALSE;
11006 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11007 DrawGameDoorValues();
11010 for (i = 0; i < MAX_PLAYERS; i++)
11012 int actual_player_action = stored_player[i].effective_action;
11015 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11016 - rnd_equinox_tetrachloride 048
11017 - rnd_equinox_tetrachloride_ii 096
11018 - rnd_emanuel_schmieg 002
11019 - doctor_sloan_ww 001, 020
11021 if (stored_player[i].MovPos == 0)
11022 CheckGravityMovement(&stored_player[i]);
11025 /* overwrite programmed action with tape action */
11026 if (stored_player[i].programmed_action)
11027 actual_player_action = stored_player[i].programmed_action;
11029 PlayerActions(&stored_player[i], actual_player_action);
11031 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11034 ScrollScreen(NULL, SCROLL_GO_ON);
11036 /* for backwards compatibility, the following code emulates a fixed bug that
11037 occured when pushing elements (causing elements that just made their last
11038 pushing step to already (if possible) make their first falling step in the
11039 same game frame, which is bad); this code is also needed to use the famous
11040 "spring push bug" which is used in older levels and might be wanted to be
11041 used also in newer levels, but in this case the buggy pushing code is only
11042 affecting the "spring" element and no other elements */
11044 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11046 for (i = 0; i < MAX_PLAYERS; i++)
11048 struct PlayerInfo *player = &stored_player[i];
11049 int x = player->jx;
11050 int y = player->jy;
11052 if (player->active && player->is_pushing && player->is_moving &&
11054 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11055 Feld[x][y] == EL_SPRING))
11057 ContinueMoving(x, y);
11059 /* continue moving after pushing (this is actually a bug) */
11060 if (!IS_MOVING(x, y))
11061 Stop[x][y] = FALSE;
11067 debug_print_timestamp(0, "start main loop profiling");
11070 SCAN_PLAYFIELD(x, y)
11072 ChangeCount[x][y] = 0;
11073 ChangeEvent[x][y] = -1;
11075 /* this must be handled before main playfield loop */
11076 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11079 if (MovDelay[x][y] <= 0)
11083 #if USE_NEW_SNAP_DELAY
11084 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11087 if (MovDelay[x][y] <= 0)
11090 DrawLevelField(x, y);
11092 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11098 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11100 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11101 printf("GameActions(): This should never happen!\n");
11103 ChangePage[x][y] = -1;
11107 Stop[x][y] = FALSE;
11108 if (WasJustMoving[x][y] > 0)
11109 WasJustMoving[x][y]--;
11110 if (WasJustFalling[x][y] > 0)
11111 WasJustFalling[x][y]--;
11112 if (CheckCollision[x][y] > 0)
11113 CheckCollision[x][y]--;
11114 if (CheckImpact[x][y] > 0)
11115 CheckImpact[x][y]--;
11119 /* reset finished pushing action (not done in ContinueMoving() to allow
11120 continuous pushing animation for elements with zero push delay) */
11121 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11123 ResetGfxAnimation(x, y);
11124 DrawLevelField(x, y);
11128 if (IS_BLOCKED(x, y))
11132 Blocked2Moving(x, y, &oldx, &oldy);
11133 if (!IS_MOVING(oldx, oldy))
11135 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11136 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11137 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11138 printf("GameActions(): This should never happen!\n");
11145 debug_print_timestamp(0, "- time for pre-main loop:");
11148 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11149 SCAN_PLAYFIELD(x, y)
11151 element = Feld[x][y];
11152 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11157 int element2 = element;
11158 int graphic2 = graphic;
11160 int element2 = Feld[x][y];
11161 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11163 int last_gfx_frame = GfxFrame[x][y];
11165 if (graphic_info[graphic2].anim_global_sync)
11166 GfxFrame[x][y] = FrameCounter;
11167 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11168 GfxFrame[x][y] = CustomValue[x][y];
11169 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11170 GfxFrame[x][y] = element_info[element2].collect_score;
11171 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11172 GfxFrame[x][y] = ChangeDelay[x][y];
11174 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11175 DrawLevelGraphicAnimation(x, y, graphic2);
11178 ResetGfxFrame(x, y, TRUE);
11182 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11183 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11184 ResetRandomAnimationValue(x, y);
11188 SetRandomAnimationValue(x, y);
11192 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11195 #endif // -------------------- !!! TEST ONLY !!! --------------------
11198 debug_print_timestamp(0, "- time for TEST loop: -->");
11201 SCAN_PLAYFIELD(x, y)
11203 element = Feld[x][y];
11204 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11206 ResetGfxFrame(x, y, TRUE);
11208 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11209 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11210 ResetRandomAnimationValue(x, y);
11212 SetRandomAnimationValue(x, y);
11214 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11216 if (IS_INACTIVE(element))
11218 if (IS_ANIMATED(graphic))
11219 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11224 /* this may take place after moving, so 'element' may have changed */
11225 if (IS_CHANGING(x, y) &&
11226 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11228 int page = element_info[element].event_page_nr[CE_DELAY];
11231 HandleElementChange(x, y, page);
11233 if (CAN_CHANGE(element))
11234 HandleElementChange(x, y, page);
11236 if (HAS_ACTION(element))
11237 ExecuteCustomElementAction(x, y, element, page);
11240 element = Feld[x][y];
11241 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11244 #if 0 // ---------------------------------------------------------------------
11246 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11250 element = Feld[x][y];
11251 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11253 if (IS_ANIMATED(graphic) &&
11254 !IS_MOVING(x, y) &&
11256 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11258 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11259 DrawTwinkleOnField(x, y);
11261 else if (IS_MOVING(x, y))
11262 ContinueMoving(x, y);
11269 case EL_EM_EXIT_OPEN:
11270 case EL_SP_EXIT_OPEN:
11271 case EL_STEEL_EXIT_OPEN:
11272 case EL_EM_STEEL_EXIT_OPEN:
11273 case EL_SP_TERMINAL:
11274 case EL_SP_TERMINAL_ACTIVE:
11275 case EL_EXTRA_TIME:
11276 case EL_SHIELD_NORMAL:
11277 case EL_SHIELD_DEADLY:
11278 if (IS_ANIMATED(graphic))
11279 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11282 case EL_DYNAMITE_ACTIVE:
11283 case EL_EM_DYNAMITE_ACTIVE:
11284 case EL_DYNABOMB_PLAYER_1_ACTIVE:
11285 case EL_DYNABOMB_PLAYER_2_ACTIVE:
11286 case EL_DYNABOMB_PLAYER_3_ACTIVE:
11287 case EL_DYNABOMB_PLAYER_4_ACTIVE:
11288 case EL_SP_DISK_RED_ACTIVE:
11289 CheckDynamite(x, y);
11292 case EL_AMOEBA_GROWING:
11293 AmoebeWaechst(x, y);
11296 case EL_AMOEBA_SHRINKING:
11297 AmoebaDisappearing(x, y);
11300 #if !USE_NEW_AMOEBA_CODE
11301 case EL_AMOEBA_WET:
11302 case EL_AMOEBA_DRY:
11303 case EL_AMOEBA_FULL:
11305 case EL_EMC_DRIPPER:
11306 AmoebeAbleger(x, y);
11310 case EL_GAME_OF_LIFE:
11315 case EL_EXIT_CLOSED:
11319 case EL_EM_EXIT_CLOSED:
11323 case EL_STEEL_EXIT_CLOSED:
11324 CheckExitSteel(x, y);
11327 case EL_EM_STEEL_EXIT_CLOSED:
11328 CheckExitSteelEM(x, y);
11331 case EL_SP_EXIT_CLOSED:
11335 case EL_EXPANDABLE_WALL_GROWING:
11336 case EL_EXPANDABLE_STEELWALL_GROWING:
11337 MauerWaechst(x, y);
11340 case EL_EXPANDABLE_WALL:
11341 case EL_EXPANDABLE_WALL_HORIZONTAL:
11342 case EL_EXPANDABLE_WALL_VERTICAL:
11343 case EL_EXPANDABLE_WALL_ANY:
11344 case EL_BD_EXPANDABLE_WALL:
11345 MauerAbleger(x, y);
11348 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11349 case EL_EXPANDABLE_STEELWALL_VERTICAL:
11350 case EL_EXPANDABLE_STEELWALL_ANY:
11351 MauerAblegerStahl(x, y);
11355 CheckForDragon(x, y);
11361 case EL_ELEMENT_SNAPPING:
11362 case EL_DIAGONAL_SHRINKING:
11363 case EL_DIAGONAL_GROWING:
11366 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11368 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11373 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11374 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11379 #else // ---------------------------------------------------------------------
11381 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11385 element = Feld[x][y];
11386 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11388 if (IS_ANIMATED(graphic) &&
11389 !IS_MOVING(x, y) &&
11391 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11393 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11394 DrawTwinkleOnField(x, y);
11396 else if ((element == EL_ACID ||
11397 element == EL_EXIT_OPEN ||
11398 element == EL_EM_EXIT_OPEN ||
11399 element == EL_SP_EXIT_OPEN ||
11400 element == EL_STEEL_EXIT_OPEN ||
11401 element == EL_EM_STEEL_EXIT_OPEN ||
11402 element == EL_SP_TERMINAL ||
11403 element == EL_SP_TERMINAL_ACTIVE ||
11404 element == EL_EXTRA_TIME ||
11405 element == EL_SHIELD_NORMAL ||
11406 element == EL_SHIELD_DEADLY) &&
11407 IS_ANIMATED(graphic))
11408 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11409 else if (IS_MOVING(x, y))
11410 ContinueMoving(x, y);
11411 else if (IS_ACTIVE_BOMB(element))
11412 CheckDynamite(x, y);
11413 else if (element == EL_AMOEBA_GROWING)
11414 AmoebeWaechst(x, y);
11415 else if (element == EL_AMOEBA_SHRINKING)
11416 AmoebaDisappearing(x, y);
11418 #if !USE_NEW_AMOEBA_CODE
11419 else if (IS_AMOEBALIVE(element))
11420 AmoebeAbleger(x, y);
11423 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11425 else if (element == EL_EXIT_CLOSED)
11427 else if (element == EL_EM_EXIT_CLOSED)
11429 else if (element == EL_STEEL_EXIT_CLOSED)
11430 CheckExitSteel(x, y);
11431 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11432 CheckExitSteelEM(x, y);
11433 else if (element == EL_SP_EXIT_CLOSED)
11435 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11436 element == EL_EXPANDABLE_STEELWALL_GROWING)
11437 MauerWaechst(x, y);
11438 else if (element == EL_EXPANDABLE_WALL ||
11439 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11440 element == EL_EXPANDABLE_WALL_VERTICAL ||
11441 element == EL_EXPANDABLE_WALL_ANY ||
11442 element == EL_BD_EXPANDABLE_WALL)
11443 MauerAbleger(x, y);
11444 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11445 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11446 element == EL_EXPANDABLE_STEELWALL_ANY)
11447 MauerAblegerStahl(x, y);
11448 else if (element == EL_FLAMES)
11449 CheckForDragon(x, y);
11450 else if (element == EL_EXPLOSION)
11451 ; /* drawing of correct explosion animation is handled separately */
11452 else if (element == EL_ELEMENT_SNAPPING ||
11453 element == EL_DIAGONAL_SHRINKING ||
11454 element == EL_DIAGONAL_GROWING)
11456 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11458 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11460 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11461 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11463 #endif // ---------------------------------------------------------------------
11465 if (IS_BELT_ACTIVE(element))
11466 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11468 if (game.magic_wall_active)
11470 int jx = local_player->jx, jy = local_player->jy;
11472 /* play the element sound at the position nearest to the player */
11473 if ((element == EL_MAGIC_WALL_FULL ||
11474 element == EL_MAGIC_WALL_ACTIVE ||
11475 element == EL_MAGIC_WALL_EMPTYING ||
11476 element == EL_BD_MAGIC_WALL_FULL ||
11477 element == EL_BD_MAGIC_WALL_ACTIVE ||
11478 element == EL_BD_MAGIC_WALL_EMPTYING ||
11479 element == EL_DC_MAGIC_WALL_FULL ||
11480 element == EL_DC_MAGIC_WALL_ACTIVE ||
11481 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11482 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11491 debug_print_timestamp(0, "- time for MAIN loop: -->");
11494 #if USE_NEW_AMOEBA_CODE
11495 /* new experimental amoeba growth stuff */
11496 if (!(FrameCounter % 8))
11498 static unsigned long random = 1684108901;
11500 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11502 x = RND(lev_fieldx);
11503 y = RND(lev_fieldy);
11504 element = Feld[x][y];
11506 if (!IS_PLAYER(x,y) &&
11507 (element == EL_EMPTY ||
11508 CAN_GROW_INTO(element) ||
11509 element == EL_QUICKSAND_EMPTY ||
11510 element == EL_QUICKSAND_FAST_EMPTY ||
11511 element == EL_ACID_SPLASH_LEFT ||
11512 element == EL_ACID_SPLASH_RIGHT))
11514 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11515 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11516 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11517 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11518 Feld[x][y] = EL_AMOEBA_DROP;
11521 random = random * 129 + 1;
11527 if (game.explosions_delayed)
11530 game.explosions_delayed = FALSE;
11532 SCAN_PLAYFIELD(x, y)
11534 element = Feld[x][y];
11536 if (ExplodeField[x][y])
11537 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11538 else if (element == EL_EXPLOSION)
11539 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11541 ExplodeField[x][y] = EX_TYPE_NONE;
11544 game.explosions_delayed = TRUE;
11547 if (game.magic_wall_active)
11549 if (!(game.magic_wall_time_left % 4))
11551 int element = Feld[magic_wall_x][magic_wall_y];
11553 if (element == EL_BD_MAGIC_WALL_FULL ||
11554 element == EL_BD_MAGIC_WALL_ACTIVE ||
11555 element == EL_BD_MAGIC_WALL_EMPTYING)
11556 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11557 else if (element == EL_DC_MAGIC_WALL_FULL ||
11558 element == EL_DC_MAGIC_WALL_ACTIVE ||
11559 element == EL_DC_MAGIC_WALL_EMPTYING)
11560 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11562 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11565 if (game.magic_wall_time_left > 0)
11567 game.magic_wall_time_left--;
11568 if (!game.magic_wall_time_left)
11570 SCAN_PLAYFIELD(x, y)
11572 element = Feld[x][y];
11574 if (element == EL_MAGIC_WALL_ACTIVE ||
11575 element == EL_MAGIC_WALL_FULL)
11577 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11578 DrawLevelField(x, y);
11580 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11581 element == EL_BD_MAGIC_WALL_FULL)
11583 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11584 DrawLevelField(x, y);
11586 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11587 element == EL_DC_MAGIC_WALL_FULL)
11589 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11590 DrawLevelField(x, y);
11594 game.magic_wall_active = FALSE;
11599 if (game.light_time_left > 0)
11601 game.light_time_left--;
11603 if (game.light_time_left == 0)
11604 RedrawAllLightSwitchesAndInvisibleElements();
11607 if (game.timegate_time_left > 0)
11609 game.timegate_time_left--;
11611 if (game.timegate_time_left == 0)
11612 CloseAllOpenTimegates();
11615 if (game.lenses_time_left > 0)
11617 game.lenses_time_left--;
11619 if (game.lenses_time_left == 0)
11620 RedrawAllInvisibleElementsForLenses();
11623 if (game.magnify_time_left > 0)
11625 game.magnify_time_left--;
11627 if (game.magnify_time_left == 0)
11628 RedrawAllInvisibleElementsForMagnifier();
11631 for (i = 0; i < MAX_PLAYERS; i++)
11633 struct PlayerInfo *player = &stored_player[i];
11635 if (SHIELD_ON(player))
11637 if (player->shield_deadly_time_left)
11638 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11639 else if (player->shield_normal_time_left)
11640 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11647 PlayAllPlayersSound();
11649 if (options.debug) /* calculate frames per second */
11651 static unsigned long fps_counter = 0;
11652 static int fps_frames = 0;
11653 unsigned long fps_delay_ms = Counter() - fps_counter;
11657 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11659 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11662 fps_counter = Counter();
11665 redraw_mask |= REDRAW_FPS;
11668 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11670 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11672 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11674 local_player->show_envelope = 0;
11678 debug_print_timestamp(0, "stop main loop profiling ");
11679 printf("----------------------------------------------------------\n");
11682 /* use random number generator in every frame to make it less predictable */
11683 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11687 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11689 int min_x = x, min_y = y, max_x = x, max_y = y;
11692 for (i = 0; i < MAX_PLAYERS; i++)
11694 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11696 if (!stored_player[i].active || &stored_player[i] == player)
11699 min_x = MIN(min_x, jx);
11700 min_y = MIN(min_y, jy);
11701 max_x = MAX(max_x, jx);
11702 max_y = MAX(max_y, jy);
11705 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11708 static boolean AllPlayersInVisibleScreen()
11712 for (i = 0; i < MAX_PLAYERS; i++)
11714 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11716 if (!stored_player[i].active)
11719 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11726 void ScrollLevel(int dx, int dy)
11729 static Bitmap *bitmap_db_field2 = NULL;
11730 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11737 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11738 /* only horizontal XOR vertical scroll direction allowed */
11739 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11744 if (bitmap_db_field2 == NULL)
11745 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11747 /* needed when blitting directly to same bitmap -- should not be needed with
11748 recent SDL libraries, but apparently does not work in 1.2.11 directly */
11749 BlitBitmap(drawto_field, bitmap_db_field2,
11750 FX + TILEX * (dx == -1) - softscroll_offset,
11751 FY + TILEY * (dy == -1) - softscroll_offset,
11752 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11753 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11754 FX + TILEX * (dx == 1) - softscroll_offset,
11755 FY + TILEY * (dy == 1) - softscroll_offset);
11756 BlitBitmap(bitmap_db_field2, drawto_field,
11757 FX + TILEX * (dx == 1) - softscroll_offset,
11758 FY + TILEY * (dy == 1) - softscroll_offset,
11759 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11760 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11761 FX + TILEX * (dx == 1) - softscroll_offset,
11762 FY + TILEY * (dy == 1) - softscroll_offset);
11767 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11768 int xsize = (BX2 - BX1 + 1);
11769 int ysize = (BY2 - BY1 + 1);
11770 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11771 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11772 int step = (start < end ? +1 : -1);
11774 for (i = start; i != end; i += step)
11776 BlitBitmap(drawto_field, drawto_field,
11777 FX + TILEX * (dx != 0 ? i + step : 0),
11778 FY + TILEY * (dy != 0 ? i + step : 0),
11779 TILEX * (dx != 0 ? 1 : xsize),
11780 TILEY * (dy != 0 ? 1 : ysize),
11781 FX + TILEX * (dx != 0 ? i : 0),
11782 FY + TILEY * (dy != 0 ? i : 0));
11787 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11789 BlitBitmap(drawto_field, drawto_field,
11790 FX + TILEX * (dx == -1) - softscroll_offset,
11791 FY + TILEY * (dy == -1) - softscroll_offset,
11792 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11793 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11794 FX + TILEX * (dx == 1) - softscroll_offset,
11795 FY + TILEY * (dy == 1) - softscroll_offset);
11801 x = (dx == 1 ? BX1 : BX2);
11802 for (y = BY1; y <= BY2; y++)
11803 DrawScreenField(x, y);
11808 y = (dy == 1 ? BY1 : BY2);
11809 for (x = BX1; x <= BX2; x++)
11810 DrawScreenField(x, y);
11813 redraw_mask |= REDRAW_FIELD;
11816 static boolean canFallDown(struct PlayerInfo *player)
11818 int jx = player->jx, jy = player->jy;
11820 return (IN_LEV_FIELD(jx, jy + 1) &&
11821 (IS_FREE(jx, jy + 1) ||
11822 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11823 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11824 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11827 static boolean canPassField(int x, int y, int move_dir)
11829 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11830 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11831 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11832 int nextx = x + dx;
11833 int nexty = y + dy;
11834 int element = Feld[x][y];
11836 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11837 !CAN_MOVE(element) &&
11838 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11839 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11840 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11843 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11845 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11846 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11847 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11851 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11852 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11853 (IS_DIGGABLE(Feld[newx][newy]) ||
11854 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11855 canPassField(newx, newy, move_dir)));
11858 static void CheckGravityMovement(struct PlayerInfo *player)
11860 #if USE_PLAYER_GRAVITY
11861 if (player->gravity && !player->programmed_action)
11863 if (game.gravity && !player->programmed_action)
11866 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11867 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11868 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11869 int jx = player->jx, jy = player->jy;
11870 boolean player_is_moving_to_valid_field =
11871 (!player_is_snapping &&
11872 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11873 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11874 boolean player_can_fall_down = canFallDown(player);
11876 if (player_can_fall_down &&
11877 !player_is_moving_to_valid_field)
11878 player->programmed_action = MV_DOWN;
11882 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11884 return CheckGravityMovement(player);
11886 #if USE_PLAYER_GRAVITY
11887 if (player->gravity && !player->programmed_action)
11889 if (game.gravity && !player->programmed_action)
11892 int jx = player->jx, jy = player->jy;
11893 boolean field_under_player_is_free =
11894 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11895 boolean player_is_standing_on_valid_field =
11896 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11897 (IS_WALKABLE(Feld[jx][jy]) &&
11898 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11900 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11901 player->programmed_action = MV_DOWN;
11906 MovePlayerOneStep()
11907 -----------------------------------------------------------------------------
11908 dx, dy: direction (non-diagonal) to try to move the player to
11909 real_dx, real_dy: direction as read from input device (can be diagonal)
11912 boolean MovePlayerOneStep(struct PlayerInfo *player,
11913 int dx, int dy, int real_dx, int real_dy)
11915 int jx = player->jx, jy = player->jy;
11916 int new_jx = jx + dx, new_jy = jy + dy;
11917 #if !USE_FIXED_DONT_RUN_INTO
11921 boolean player_can_move = !player->cannot_move;
11923 if (!player->active || (!dx && !dy))
11924 return MP_NO_ACTION;
11926 player->MovDir = (dx < 0 ? MV_LEFT :
11927 dx > 0 ? MV_RIGHT :
11929 dy > 0 ? MV_DOWN : MV_NONE);
11931 if (!IN_LEV_FIELD(new_jx, new_jy))
11932 return MP_NO_ACTION;
11934 if (!player_can_move)
11936 if (player->MovPos == 0)
11938 player->is_moving = FALSE;
11939 player->is_digging = FALSE;
11940 player->is_collecting = FALSE;
11941 player->is_snapping = FALSE;
11942 player->is_pushing = FALSE;
11947 if (!options.network && game.centered_player_nr == -1 &&
11948 !AllPlayersInSight(player, new_jx, new_jy))
11949 return MP_NO_ACTION;
11951 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11952 return MP_NO_ACTION;
11955 #if !USE_FIXED_DONT_RUN_INTO
11956 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11958 /* (moved to DigField()) */
11959 if (player_can_move && DONT_RUN_INTO(element))
11961 if (element == EL_ACID && dx == 0 && dy == 1)
11963 SplashAcid(new_jx, new_jy);
11964 Feld[jx][jy] = EL_PLAYER_1;
11965 InitMovingField(jx, jy, MV_DOWN);
11966 Store[jx][jy] = EL_ACID;
11967 ContinueMoving(jx, jy);
11968 BuryPlayer(player);
11971 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11977 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11978 if (can_move != MP_MOVING)
11981 /* check if DigField() has caused relocation of the player */
11982 if (player->jx != jx || player->jy != jy)
11983 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11985 StorePlayer[jx][jy] = 0;
11986 player->last_jx = jx;
11987 player->last_jy = jy;
11988 player->jx = new_jx;
11989 player->jy = new_jy;
11990 StorePlayer[new_jx][new_jy] = player->element_nr;
11992 if (player->move_delay_value_next != -1)
11994 player->move_delay_value = player->move_delay_value_next;
11995 player->move_delay_value_next = -1;
11999 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12001 player->step_counter++;
12003 PlayerVisit[jx][jy] = FrameCounter;
12005 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12006 player->is_moving = TRUE;
12010 /* should better be called in MovePlayer(), but this breaks some tapes */
12011 ScrollPlayer(player, SCROLL_INIT);
12017 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12019 int jx = player->jx, jy = player->jy;
12020 int old_jx = jx, old_jy = jy;
12021 int moved = MP_NO_ACTION;
12023 if (!player->active)
12028 if (player->MovPos == 0)
12030 player->is_moving = FALSE;
12031 player->is_digging = FALSE;
12032 player->is_collecting = FALSE;
12033 player->is_snapping = FALSE;
12034 player->is_pushing = FALSE;
12040 if (player->move_delay > 0)
12043 player->move_delay = -1; /* set to "uninitialized" value */
12045 /* store if player is automatically moved to next field */
12046 player->is_auto_moving = (player->programmed_action != MV_NONE);
12048 /* remove the last programmed player action */
12049 player->programmed_action = 0;
12051 if (player->MovPos)
12053 /* should only happen if pre-1.2 tape recordings are played */
12054 /* this is only for backward compatibility */
12056 int original_move_delay_value = player->move_delay_value;
12059 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12063 /* scroll remaining steps with finest movement resolution */
12064 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12066 while (player->MovPos)
12068 ScrollPlayer(player, SCROLL_GO_ON);
12069 ScrollScreen(NULL, SCROLL_GO_ON);
12071 AdvanceFrameAndPlayerCounters(player->index_nr);
12077 player->move_delay_value = original_move_delay_value;
12080 player->is_active = FALSE;
12082 if (player->last_move_dir & MV_HORIZONTAL)
12084 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12085 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12089 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12090 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12093 #if USE_FIXED_BORDER_RUNNING_GFX
12094 if (!moved && !player->is_active)
12096 player->is_moving = FALSE;
12097 player->is_digging = FALSE;
12098 player->is_collecting = FALSE;
12099 player->is_snapping = FALSE;
12100 player->is_pushing = FALSE;
12108 if (moved & MP_MOVING && !ScreenMovPos &&
12109 (player->index_nr == game.centered_player_nr ||
12110 game.centered_player_nr == -1))
12112 if (moved & MP_MOVING && !ScreenMovPos &&
12113 (player == local_player || !options.network))
12116 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12117 int offset = (setup.scroll_delay ? 3 : 0);
12119 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12121 /* actual player has left the screen -- scroll in that direction */
12122 if (jx != old_jx) /* player has moved horizontally */
12123 scroll_x += (jx - old_jx);
12124 else /* player has moved vertically */
12125 scroll_y += (jy - old_jy);
12129 if (jx != old_jx) /* player has moved horizontally */
12131 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12132 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12133 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12135 /* don't scroll over playfield boundaries */
12136 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12137 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12139 /* don't scroll more than one field at a time */
12140 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12142 /* don't scroll against the player's moving direction */
12143 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12144 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12145 scroll_x = old_scroll_x;
12147 else /* player has moved vertically */
12149 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12150 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12151 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12153 /* don't scroll over playfield boundaries */
12154 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12155 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12157 /* don't scroll more than one field at a time */
12158 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12160 /* don't scroll against the player's moving direction */
12161 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12162 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12163 scroll_y = old_scroll_y;
12167 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12170 if (!options.network && game.centered_player_nr == -1 &&
12171 !AllPlayersInVisibleScreen())
12173 scroll_x = old_scroll_x;
12174 scroll_y = old_scroll_y;
12178 if (!options.network && !AllPlayersInVisibleScreen())
12180 scroll_x = old_scroll_x;
12181 scroll_y = old_scroll_y;
12186 ScrollScreen(player, SCROLL_INIT);
12187 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12192 player->StepFrame = 0;
12194 if (moved & MP_MOVING)
12196 if (old_jx != jx && old_jy == jy)
12197 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12198 else if (old_jx == jx && old_jy != jy)
12199 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12201 DrawLevelField(jx, jy); /* for "crumbled sand" */
12203 player->last_move_dir = player->MovDir;
12204 player->is_moving = TRUE;
12205 player->is_snapping = FALSE;
12206 player->is_switching = FALSE;
12207 player->is_dropping = FALSE;
12208 player->is_dropping_pressed = FALSE;
12209 player->drop_pressed_delay = 0;
12212 /* should better be called here than above, but this breaks some tapes */
12213 ScrollPlayer(player, SCROLL_INIT);
12218 CheckGravityMovementWhenNotMoving(player);
12220 player->is_moving = FALSE;
12222 /* at this point, the player is allowed to move, but cannot move right now
12223 (e.g. because of something blocking the way) -- ensure that the player
12224 is also allowed to move in the next frame (in old versions before 3.1.1,
12225 the player was forced to wait again for eight frames before next try) */
12227 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12228 player->move_delay = 0; /* allow direct movement in the next frame */
12231 if (player->move_delay == -1) /* not yet initialized by DigField() */
12232 player->move_delay = player->move_delay_value;
12234 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12236 TestIfPlayerTouchesBadThing(jx, jy);
12237 TestIfPlayerTouchesCustomElement(jx, jy);
12240 if (!player->active)
12241 RemovePlayer(player);
12246 void ScrollPlayer(struct PlayerInfo *player, int mode)
12248 int jx = player->jx, jy = player->jy;
12249 int last_jx = player->last_jx, last_jy = player->last_jy;
12250 int move_stepsize = TILEX / player->move_delay_value;
12252 #if USE_NEW_PLAYER_SPEED
12253 if (!player->active)
12256 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12259 if (!player->active || player->MovPos == 0)
12263 if (mode == SCROLL_INIT)
12265 player->actual_frame_counter = FrameCounter;
12266 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12268 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12269 Feld[last_jx][last_jy] == EL_EMPTY)
12271 int last_field_block_delay = 0; /* start with no blocking at all */
12272 int block_delay_adjustment = player->block_delay_adjustment;
12274 /* if player blocks last field, add delay for exactly one move */
12275 if (player->block_last_field)
12277 last_field_block_delay += player->move_delay_value;
12279 /* when blocking enabled, prevent moving up despite gravity */
12280 #if USE_PLAYER_GRAVITY
12281 if (player->gravity && player->MovDir == MV_UP)
12282 block_delay_adjustment = -1;
12284 if (game.gravity && player->MovDir == MV_UP)
12285 block_delay_adjustment = -1;
12289 /* add block delay adjustment (also possible when not blocking) */
12290 last_field_block_delay += block_delay_adjustment;
12292 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12293 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12296 #if USE_NEW_PLAYER_SPEED
12297 if (player->MovPos != 0) /* player has not yet reached destination */
12303 else if (!FrameReached(&player->actual_frame_counter, 1))
12306 #if USE_NEW_PLAYER_SPEED
12307 if (player->MovPos != 0)
12309 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12310 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12312 /* before DrawPlayer() to draw correct player graphic for this case */
12313 if (player->MovPos == 0)
12314 CheckGravityMovement(player);
12317 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12318 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12320 /* before DrawPlayer() to draw correct player graphic for this case */
12321 if (player->MovPos == 0)
12322 CheckGravityMovement(player);
12325 if (player->MovPos == 0) /* player reached destination field */
12327 if (player->move_delay_reset_counter > 0)
12329 player->move_delay_reset_counter--;
12331 if (player->move_delay_reset_counter == 0)
12333 /* continue with normal speed after quickly moving through gate */
12334 HALVE_PLAYER_SPEED(player);
12336 /* be able to make the next move without delay */
12337 player->move_delay = 0;
12341 player->last_jx = jx;
12342 player->last_jy = jy;
12344 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12345 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12346 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12347 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12348 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12349 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12351 DrawPlayer(player); /* needed here only to cleanup last field */
12352 RemovePlayer(player);
12354 if (local_player->friends_still_needed == 0 ||
12355 IS_SP_ELEMENT(Feld[jx][jy]))
12356 PlayerWins(player);
12359 /* this breaks one level: "machine", level 000 */
12361 int move_direction = player->MovDir;
12362 int enter_side = MV_DIR_OPPOSITE(move_direction);
12363 int leave_side = move_direction;
12364 int old_jx = last_jx;
12365 int old_jy = last_jy;
12366 int old_element = Feld[old_jx][old_jy];
12367 int new_element = Feld[jx][jy];
12369 if (IS_CUSTOM_ELEMENT(old_element))
12370 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12372 player->index_bit, leave_side);
12374 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12375 CE_PLAYER_LEAVES_X,
12376 player->index_bit, leave_side);
12378 if (IS_CUSTOM_ELEMENT(new_element))
12379 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12380 player->index_bit, enter_side);
12382 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12383 CE_PLAYER_ENTERS_X,
12384 player->index_bit, enter_side);
12386 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12387 CE_MOVE_OF_X, move_direction);
12390 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12392 TestIfPlayerTouchesBadThing(jx, jy);
12393 TestIfPlayerTouchesCustomElement(jx, jy);
12395 /* needed because pushed element has not yet reached its destination,
12396 so it would trigger a change event at its previous field location */
12397 if (!player->is_pushing)
12398 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12400 if (!player->active)
12401 RemovePlayer(player);
12404 if (!local_player->LevelSolved && level.use_step_counter)
12414 if (TimeLeft <= 10 && setup.time_limit)
12415 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12418 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
12420 DisplayGameControlValues();
12422 DrawGameValue_Time(TimeLeft);
12425 if (!TimeLeft && setup.time_limit)
12426 for (i = 0; i < MAX_PLAYERS; i++)
12427 KillPlayer(&stored_player[i]);
12430 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12432 game_control_value[GAME_CONTROL_TIME] = TimePlayed;
12434 DisplayGameControlValues();
12437 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12438 DrawGameValue_Time(TimePlayed);
12442 if (tape.single_step && tape.recording && !tape.pausing &&
12443 !player->programmed_action)
12444 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12448 void ScrollScreen(struct PlayerInfo *player, int mode)
12450 static unsigned long screen_frame_counter = 0;
12452 if (mode == SCROLL_INIT)
12454 /* set scrolling step size according to actual player's moving speed */
12455 ScrollStepSize = TILEX / player->move_delay_value;
12457 screen_frame_counter = FrameCounter;
12458 ScreenMovDir = player->MovDir;
12459 ScreenMovPos = player->MovPos;
12460 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12463 else if (!FrameReached(&screen_frame_counter, 1))
12468 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12469 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12470 redraw_mask |= REDRAW_FIELD;
12473 ScreenMovDir = MV_NONE;
12476 void TestIfPlayerTouchesCustomElement(int x, int y)
12478 static int xy[4][2] =
12485 static int trigger_sides[4][2] =
12487 /* center side border side */
12488 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12489 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12490 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12491 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12493 static int touch_dir[4] =
12495 MV_LEFT | MV_RIGHT,
12500 int center_element = Feld[x][y]; /* should always be non-moving! */
12503 for (i = 0; i < NUM_DIRECTIONS; i++)
12505 int xx = x + xy[i][0];
12506 int yy = y + xy[i][1];
12507 int center_side = trigger_sides[i][0];
12508 int border_side = trigger_sides[i][1];
12509 int border_element;
12511 if (!IN_LEV_FIELD(xx, yy))
12514 if (IS_PLAYER(x, y))
12516 struct PlayerInfo *player = PLAYERINFO(x, y);
12518 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12519 border_element = Feld[xx][yy]; /* may be moving! */
12520 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12521 border_element = Feld[xx][yy];
12522 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12523 border_element = MovingOrBlocked2Element(xx, yy);
12525 continue; /* center and border element do not touch */
12527 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12528 player->index_bit, border_side);
12529 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12530 CE_PLAYER_TOUCHES_X,
12531 player->index_bit, border_side);
12533 else if (IS_PLAYER(xx, yy))
12535 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12537 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12539 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12540 continue; /* center and border element do not touch */
12543 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12544 player->index_bit, center_side);
12545 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12546 CE_PLAYER_TOUCHES_X,
12547 player->index_bit, center_side);
12553 #if USE_ELEMENT_TOUCHING_BUGFIX
12555 void TestIfElementTouchesCustomElement(int x, int y)
12557 static int xy[4][2] =
12564 static int trigger_sides[4][2] =
12566 /* center side border side */
12567 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12568 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12569 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12570 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12572 static int touch_dir[4] =
12574 MV_LEFT | MV_RIGHT,
12579 boolean change_center_element = FALSE;
12580 int center_element = Feld[x][y]; /* should always be non-moving! */
12581 int border_element_old[NUM_DIRECTIONS];
12584 for (i = 0; i < NUM_DIRECTIONS; i++)
12586 int xx = x + xy[i][0];
12587 int yy = y + xy[i][1];
12588 int border_element;
12590 border_element_old[i] = -1;
12592 if (!IN_LEV_FIELD(xx, yy))
12595 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12596 border_element = Feld[xx][yy]; /* may be moving! */
12597 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12598 border_element = Feld[xx][yy];
12599 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12600 border_element = MovingOrBlocked2Element(xx, yy);
12602 continue; /* center and border element do not touch */
12604 border_element_old[i] = border_element;
12607 for (i = 0; i < NUM_DIRECTIONS; i++)
12609 int xx = x + xy[i][0];
12610 int yy = y + xy[i][1];
12611 int center_side = trigger_sides[i][0];
12612 int border_element = border_element_old[i];
12614 if (border_element == -1)
12617 /* check for change of border element */
12618 CheckElementChangeBySide(xx, yy, border_element, center_element,
12619 CE_TOUCHING_X, center_side);
12622 for (i = 0; i < NUM_DIRECTIONS; i++)
12624 int border_side = trigger_sides[i][1];
12625 int border_element = border_element_old[i];
12627 if (border_element == -1)
12630 /* check for change of center element (but change it only once) */
12631 if (!change_center_element)
12632 change_center_element =
12633 CheckElementChangeBySide(x, y, center_element, border_element,
12634 CE_TOUCHING_X, border_side);
12640 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12642 static int xy[4][2] =
12649 static int trigger_sides[4][2] =
12651 /* center side border side */
12652 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12653 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12654 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12655 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12657 static int touch_dir[4] =
12659 MV_LEFT | MV_RIGHT,
12664 boolean change_center_element = FALSE;
12665 int center_element = Feld[x][y]; /* should always be non-moving! */
12668 for (i = 0; i < NUM_DIRECTIONS; i++)
12670 int xx = x + xy[i][0];
12671 int yy = y + xy[i][1];
12672 int center_side = trigger_sides[i][0];
12673 int border_side = trigger_sides[i][1];
12674 int border_element;
12676 if (!IN_LEV_FIELD(xx, yy))
12679 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12680 border_element = Feld[xx][yy]; /* may be moving! */
12681 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12682 border_element = Feld[xx][yy];
12683 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12684 border_element = MovingOrBlocked2Element(xx, yy);
12686 continue; /* center and border element do not touch */
12688 /* check for change of center element (but change it only once) */
12689 if (!change_center_element)
12690 change_center_element =
12691 CheckElementChangeBySide(x, y, center_element, border_element,
12692 CE_TOUCHING_X, border_side);
12694 /* check for change of border element */
12695 CheckElementChangeBySide(xx, yy, border_element, center_element,
12696 CE_TOUCHING_X, center_side);
12702 void TestIfElementHitsCustomElement(int x, int y, int direction)
12704 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12705 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12706 int hitx = x + dx, hity = y + dy;
12707 int hitting_element = Feld[x][y];
12708 int touched_element;
12710 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12713 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12714 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12716 if (IN_LEV_FIELD(hitx, hity))
12718 int opposite_direction = MV_DIR_OPPOSITE(direction);
12719 int hitting_side = direction;
12720 int touched_side = opposite_direction;
12721 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12722 MovDir[hitx][hity] != direction ||
12723 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12729 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12730 CE_HITTING_X, touched_side);
12732 CheckElementChangeBySide(hitx, hity, touched_element,
12733 hitting_element, CE_HIT_BY_X, hitting_side);
12735 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12736 CE_HIT_BY_SOMETHING, opposite_direction);
12740 /* "hitting something" is also true when hitting the playfield border */
12741 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12742 CE_HITTING_SOMETHING, direction);
12746 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12748 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12749 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12750 int hitx = x + dx, hity = y + dy;
12751 int hitting_element = Feld[x][y];
12752 int touched_element;
12754 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12755 !IS_FREE(hitx, hity) &&
12756 (!IS_MOVING(hitx, hity) ||
12757 MovDir[hitx][hity] != direction ||
12758 ABS(MovPos[hitx][hity]) <= TILEY / 2));
12761 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12765 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12769 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12770 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12772 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12773 EP_CAN_SMASH_EVERYTHING, direction);
12775 if (IN_LEV_FIELD(hitx, hity))
12777 int opposite_direction = MV_DIR_OPPOSITE(direction);
12778 int hitting_side = direction;
12779 int touched_side = opposite_direction;
12781 int touched_element = MovingOrBlocked2Element(hitx, hity);
12784 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12785 MovDir[hitx][hity] != direction ||
12786 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12795 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12796 CE_SMASHED_BY_SOMETHING, opposite_direction);
12798 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12799 CE_OTHER_IS_SMASHING, touched_side);
12801 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12802 CE_OTHER_GETS_SMASHED, hitting_side);
12808 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12810 int i, kill_x = -1, kill_y = -1;
12812 int bad_element = -1;
12813 static int test_xy[4][2] =
12820 static int test_dir[4] =
12828 for (i = 0; i < NUM_DIRECTIONS; i++)
12830 int test_x, test_y, test_move_dir, test_element;
12832 test_x = good_x + test_xy[i][0];
12833 test_y = good_y + test_xy[i][1];
12835 if (!IN_LEV_FIELD(test_x, test_y))
12839 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12841 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12843 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12844 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12846 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12847 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12851 bad_element = test_element;
12857 if (kill_x != -1 || kill_y != -1)
12859 if (IS_PLAYER(good_x, good_y))
12861 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12863 if (player->shield_deadly_time_left > 0 &&
12864 !IS_INDESTRUCTIBLE(bad_element))
12865 Bang(kill_x, kill_y);
12866 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12867 KillPlayer(player);
12870 Bang(good_x, good_y);
12874 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12876 int i, kill_x = -1, kill_y = -1;
12877 int bad_element = Feld[bad_x][bad_y];
12878 static int test_xy[4][2] =
12885 static int touch_dir[4] =
12887 MV_LEFT | MV_RIGHT,
12892 static int test_dir[4] =
12900 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12903 for (i = 0; i < NUM_DIRECTIONS; i++)
12905 int test_x, test_y, test_move_dir, test_element;
12907 test_x = bad_x + test_xy[i][0];
12908 test_y = bad_y + test_xy[i][1];
12909 if (!IN_LEV_FIELD(test_x, test_y))
12913 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12915 test_element = Feld[test_x][test_y];
12917 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12918 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12920 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12921 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12923 /* good thing is player or penguin that does not move away */
12924 if (IS_PLAYER(test_x, test_y))
12926 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12928 if (bad_element == EL_ROBOT && player->is_moving)
12929 continue; /* robot does not kill player if he is moving */
12931 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12933 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12934 continue; /* center and border element do not touch */
12941 else if (test_element == EL_PENGUIN)
12950 if (kill_x != -1 || kill_y != -1)
12952 if (IS_PLAYER(kill_x, kill_y))
12954 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12956 if (player->shield_deadly_time_left > 0 &&
12957 !IS_INDESTRUCTIBLE(bad_element))
12958 Bang(bad_x, bad_y);
12959 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12960 KillPlayer(player);
12963 Bang(kill_x, kill_y);
12967 void TestIfPlayerTouchesBadThing(int x, int y)
12969 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12972 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12974 TestIfGoodThingHitsBadThing(x, y, move_dir);
12977 void TestIfBadThingTouchesPlayer(int x, int y)
12979 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12982 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12984 TestIfBadThingHitsGoodThing(x, y, move_dir);
12987 void TestIfFriendTouchesBadThing(int x, int y)
12989 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12992 void TestIfBadThingTouchesFriend(int x, int y)
12994 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12997 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12999 int i, kill_x = bad_x, kill_y = bad_y;
13000 static int xy[4][2] =
13008 for (i = 0; i < NUM_DIRECTIONS; i++)
13012 x = bad_x + xy[i][0];
13013 y = bad_y + xy[i][1];
13014 if (!IN_LEV_FIELD(x, y))
13017 element = Feld[x][y];
13018 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13019 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13027 if (kill_x != bad_x || kill_y != bad_y)
13028 Bang(bad_x, bad_y);
13031 void KillPlayer(struct PlayerInfo *player)
13033 int jx = player->jx, jy = player->jy;
13035 if (!player->active)
13038 /* the following code was introduced to prevent an infinite loop when calling
13040 -> CheckTriggeredElementChangeExt()
13041 -> ExecuteCustomElementAction()
13043 -> (infinitely repeating the above sequence of function calls)
13044 which occurs when killing the player while having a CE with the setting
13045 "kill player X when explosion of <player X>"; the solution using a new
13046 field "player->killed" was chosen for backwards compatibility, although
13047 clever use of the fields "player->active" etc. would probably also work */
13049 if (player->killed)
13053 player->killed = TRUE;
13055 /* remove accessible field at the player's position */
13056 Feld[jx][jy] = EL_EMPTY;
13058 /* deactivate shield (else Bang()/Explode() would not work right) */
13059 player->shield_normal_time_left = 0;
13060 player->shield_deadly_time_left = 0;
13063 BuryPlayer(player);
13066 static void KillPlayerUnlessEnemyProtected(int x, int y)
13068 if (!PLAYER_ENEMY_PROTECTED(x, y))
13069 KillPlayer(PLAYERINFO(x, y));
13072 static void KillPlayerUnlessExplosionProtected(int x, int y)
13074 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13075 KillPlayer(PLAYERINFO(x, y));
13078 void BuryPlayer(struct PlayerInfo *player)
13080 int jx = player->jx, jy = player->jy;
13082 if (!player->active)
13085 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13086 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13088 player->GameOver = TRUE;
13089 RemovePlayer(player);
13092 void RemovePlayer(struct PlayerInfo *player)
13094 int jx = player->jx, jy = player->jy;
13095 int i, found = FALSE;
13097 player->present = FALSE;
13098 player->active = FALSE;
13100 if (!ExplodeField[jx][jy])
13101 StorePlayer[jx][jy] = 0;
13103 if (player->is_moving)
13104 DrawLevelField(player->last_jx, player->last_jy);
13106 for (i = 0; i < MAX_PLAYERS; i++)
13107 if (stored_player[i].active)
13111 AllPlayersGone = TRUE;
13117 #if USE_NEW_SNAP_DELAY
13118 static void setFieldForSnapping(int x, int y, int element, int direction)
13120 struct ElementInfo *ei = &element_info[element];
13121 int direction_bit = MV_DIR_TO_BIT(direction);
13122 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13123 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13124 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13126 Feld[x][y] = EL_ELEMENT_SNAPPING;
13127 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13129 ResetGfxAnimation(x, y);
13131 GfxElement[x][y] = element;
13132 GfxAction[x][y] = action;
13133 GfxDir[x][y] = direction;
13134 GfxFrame[x][y] = -1;
13139 =============================================================================
13140 checkDiagonalPushing()
13141 -----------------------------------------------------------------------------
13142 check if diagonal input device direction results in pushing of object
13143 (by checking if the alternative direction is walkable, diggable, ...)
13144 =============================================================================
13147 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13148 int x, int y, int real_dx, int real_dy)
13150 int jx, jy, dx, dy, xx, yy;
13152 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13155 /* diagonal direction: check alternative direction */
13160 xx = jx + (dx == 0 ? real_dx : 0);
13161 yy = jy + (dy == 0 ? real_dy : 0);
13163 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13167 =============================================================================
13169 -----------------------------------------------------------------------------
13170 x, y: field next to player (non-diagonal) to try to dig to
13171 real_dx, real_dy: direction as read from input device (can be diagonal)
13172 =============================================================================
13175 int DigField(struct PlayerInfo *player,
13176 int oldx, int oldy, int x, int y,
13177 int real_dx, int real_dy, int mode)
13179 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13180 boolean player_was_pushing = player->is_pushing;
13181 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13182 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13183 int jx = oldx, jy = oldy;
13184 int dx = x - jx, dy = y - jy;
13185 int nextx = x + dx, nexty = y + dy;
13186 int move_direction = (dx == -1 ? MV_LEFT :
13187 dx == +1 ? MV_RIGHT :
13189 dy == +1 ? MV_DOWN : MV_NONE);
13190 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13191 int dig_side = MV_DIR_OPPOSITE(move_direction);
13192 int old_element = Feld[jx][jy];
13193 #if USE_FIXED_DONT_RUN_INTO
13194 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13200 if (is_player) /* function can also be called by EL_PENGUIN */
13202 if (player->MovPos == 0)
13204 player->is_digging = FALSE;
13205 player->is_collecting = FALSE;
13208 if (player->MovPos == 0) /* last pushing move finished */
13209 player->is_pushing = FALSE;
13211 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13213 player->is_switching = FALSE;
13214 player->push_delay = -1;
13216 return MP_NO_ACTION;
13220 #if !USE_FIXED_DONT_RUN_INTO
13221 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13222 return MP_NO_ACTION;
13225 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13226 old_element = Back[jx][jy];
13228 /* in case of element dropped at player position, check background */
13229 else if (Back[jx][jy] != EL_EMPTY &&
13230 game.engine_version >= VERSION_IDENT(2,2,0,0))
13231 old_element = Back[jx][jy];
13233 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13234 return MP_NO_ACTION; /* field has no opening in this direction */
13236 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13237 return MP_NO_ACTION; /* field has no opening in this direction */
13239 #if USE_FIXED_DONT_RUN_INTO
13240 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13244 Feld[jx][jy] = player->artwork_element;
13245 InitMovingField(jx, jy, MV_DOWN);
13246 Store[jx][jy] = EL_ACID;
13247 ContinueMoving(jx, jy);
13248 BuryPlayer(player);
13250 return MP_DONT_RUN_INTO;
13254 #if USE_FIXED_DONT_RUN_INTO
13255 if (player_can_move && DONT_RUN_INTO(element))
13257 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13259 return MP_DONT_RUN_INTO;
13263 #if USE_FIXED_DONT_RUN_INTO
13264 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13265 return MP_NO_ACTION;
13268 #if !USE_FIXED_DONT_RUN_INTO
13269 element = Feld[x][y];
13272 collect_count = element_info[element].collect_count_initial;
13274 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13275 return MP_NO_ACTION;
13277 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13278 player_can_move = player_can_move_or_snap;
13280 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13281 game.engine_version >= VERSION_IDENT(2,2,0,0))
13283 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13284 player->index_bit, dig_side);
13285 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13286 player->index_bit, dig_side);
13288 if (element == EL_DC_LANDMINE)
13291 if (Feld[x][y] != element) /* field changed by snapping */
13294 return MP_NO_ACTION;
13297 #if USE_PLAYER_GRAVITY
13298 if (player->gravity && is_player && !player->is_auto_moving &&
13299 canFallDown(player) && move_direction != MV_DOWN &&
13300 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13301 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13303 if (game.gravity && is_player && !player->is_auto_moving &&
13304 canFallDown(player) && move_direction != MV_DOWN &&
13305 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13306 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13309 if (player_can_move &&
13310 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13312 int sound_element = SND_ELEMENT(element);
13313 int sound_action = ACTION_WALKING;
13315 if (IS_RND_GATE(element))
13317 if (!player->key[RND_GATE_NR(element)])
13318 return MP_NO_ACTION;
13320 else if (IS_RND_GATE_GRAY(element))
13322 if (!player->key[RND_GATE_GRAY_NR(element)])
13323 return MP_NO_ACTION;
13325 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13327 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13328 return MP_NO_ACTION;
13330 else if (element == EL_EXIT_OPEN ||
13331 element == EL_EM_EXIT_OPEN ||
13332 element == EL_STEEL_EXIT_OPEN ||
13333 element == EL_EM_STEEL_EXIT_OPEN ||
13334 element == EL_SP_EXIT_OPEN ||
13335 element == EL_SP_EXIT_OPENING)
13337 sound_action = ACTION_PASSING; /* player is passing exit */
13339 else if (element == EL_EMPTY)
13341 sound_action = ACTION_MOVING; /* nothing to walk on */
13344 /* play sound from background or player, whatever is available */
13345 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13346 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13348 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13350 else if (player_can_move &&
13351 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13353 if (!ACCESS_FROM(element, opposite_direction))
13354 return MP_NO_ACTION; /* field not accessible from this direction */
13356 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13357 return MP_NO_ACTION;
13359 if (IS_EM_GATE(element))
13361 if (!player->key[EM_GATE_NR(element)])
13362 return MP_NO_ACTION;
13364 else if (IS_EM_GATE_GRAY(element))
13366 if (!player->key[EM_GATE_GRAY_NR(element)])
13367 return MP_NO_ACTION;
13369 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13371 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13372 return MP_NO_ACTION;
13374 else if (IS_EMC_GATE(element))
13376 if (!player->key[EMC_GATE_NR(element)])
13377 return MP_NO_ACTION;
13379 else if (IS_EMC_GATE_GRAY(element))
13381 if (!player->key[EMC_GATE_GRAY_NR(element)])
13382 return MP_NO_ACTION;
13384 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13386 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13387 return MP_NO_ACTION;
13389 else if (element == EL_DC_GATE_WHITE ||
13390 element == EL_DC_GATE_WHITE_GRAY ||
13391 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13393 if (player->num_white_keys == 0)
13394 return MP_NO_ACTION;
13396 player->num_white_keys--;
13398 else if (IS_SP_PORT(element))
13400 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13401 element == EL_SP_GRAVITY_PORT_RIGHT ||
13402 element == EL_SP_GRAVITY_PORT_UP ||
13403 element == EL_SP_GRAVITY_PORT_DOWN)
13404 #if USE_PLAYER_GRAVITY
13405 player->gravity = !player->gravity;
13407 game.gravity = !game.gravity;
13409 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13410 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13411 element == EL_SP_GRAVITY_ON_PORT_UP ||
13412 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13413 #if USE_PLAYER_GRAVITY
13414 player->gravity = TRUE;
13416 game.gravity = TRUE;
13418 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13419 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13420 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13421 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13422 #if USE_PLAYER_GRAVITY
13423 player->gravity = FALSE;
13425 game.gravity = FALSE;
13429 /* automatically move to the next field with double speed */
13430 player->programmed_action = move_direction;
13432 if (player->move_delay_reset_counter == 0)
13434 player->move_delay_reset_counter = 2; /* two double speed steps */
13436 DOUBLE_PLAYER_SPEED(player);
13439 PlayLevelSoundAction(x, y, ACTION_PASSING);
13441 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13445 if (mode != DF_SNAP)
13447 GfxElement[x][y] = GFX_ELEMENT(element);
13448 player->is_digging = TRUE;
13451 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13453 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13454 player->index_bit, dig_side);
13456 if (mode == DF_SNAP)
13458 #if USE_NEW_SNAP_DELAY
13459 if (level.block_snap_field)
13460 setFieldForSnapping(x, y, element, move_direction);
13462 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13464 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13467 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13468 player->index_bit, dig_side);
13471 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13475 if (is_player && mode != DF_SNAP)
13477 GfxElement[x][y] = element;
13478 player->is_collecting = TRUE;
13481 if (element == EL_SPEED_PILL)
13483 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13485 else if (element == EL_EXTRA_TIME && level.time > 0)
13487 TimeLeft += level.extra_time;
13490 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13492 DisplayGameControlValues();
13494 DrawGameValue_Time(TimeLeft);
13497 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13499 player->shield_normal_time_left += level.shield_normal_time;
13500 if (element == EL_SHIELD_DEADLY)
13501 player->shield_deadly_time_left += level.shield_deadly_time;
13503 else if (element == EL_DYNAMITE ||
13504 element == EL_EM_DYNAMITE ||
13505 element == EL_SP_DISK_RED)
13507 if (player->inventory_size < MAX_INVENTORY_SIZE)
13508 player->inventory_element[player->inventory_size++] = element;
13510 DrawGameDoorValues();
13512 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13514 player->dynabomb_count++;
13515 player->dynabombs_left++;
13517 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13519 player->dynabomb_size++;
13521 else if (element == EL_DYNABOMB_INCREASE_POWER)
13523 player->dynabomb_xl = TRUE;
13525 else if (IS_KEY(element))
13527 player->key[KEY_NR(element)] = TRUE;
13529 DrawGameDoorValues();
13531 else if (element == EL_DC_KEY_WHITE)
13533 player->num_white_keys++;
13535 /* display white keys? */
13536 /* DrawGameDoorValues(); */
13538 else if (IS_ENVELOPE(element))
13540 player->show_envelope = element;
13542 else if (element == EL_EMC_LENSES)
13544 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13546 RedrawAllInvisibleElementsForLenses();
13548 else if (element == EL_EMC_MAGNIFIER)
13550 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13552 RedrawAllInvisibleElementsForMagnifier();
13554 else if (IS_DROPPABLE(element) ||
13555 IS_THROWABLE(element)) /* can be collected and dropped */
13559 if (collect_count == 0)
13560 player->inventory_infinite_element = element;
13562 for (i = 0; i < collect_count; i++)
13563 if (player->inventory_size < MAX_INVENTORY_SIZE)
13564 player->inventory_element[player->inventory_size++] = element;
13566 DrawGameDoorValues();
13568 else if (collect_count > 0)
13570 local_player->gems_still_needed -= collect_count;
13571 if (local_player->gems_still_needed < 0)
13572 local_player->gems_still_needed = 0;
13575 game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
13577 DisplayGameControlValues();
13579 DrawGameValue_Emeralds(local_player->gems_still_needed);
13583 RaiseScoreElement(element);
13584 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13587 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13588 player->index_bit, dig_side);
13590 if (mode == DF_SNAP)
13592 #if USE_NEW_SNAP_DELAY
13593 if (level.block_snap_field)
13594 setFieldForSnapping(x, y, element, move_direction);
13596 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13598 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13601 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13602 player->index_bit, dig_side);
13605 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13607 if (mode == DF_SNAP && element != EL_BD_ROCK)
13608 return MP_NO_ACTION;
13610 if (CAN_FALL(element) && dy)
13611 return MP_NO_ACTION;
13613 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13614 !(element == EL_SPRING && level.use_spring_bug))
13615 return MP_NO_ACTION;
13617 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13618 ((move_direction & MV_VERTICAL &&
13619 ((element_info[element].move_pattern & MV_LEFT &&
13620 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13621 (element_info[element].move_pattern & MV_RIGHT &&
13622 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13623 (move_direction & MV_HORIZONTAL &&
13624 ((element_info[element].move_pattern & MV_UP &&
13625 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13626 (element_info[element].move_pattern & MV_DOWN &&
13627 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13628 return MP_NO_ACTION;
13630 /* do not push elements already moving away faster than player */
13631 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13632 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13633 return MP_NO_ACTION;
13635 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13637 if (player->push_delay_value == -1 || !player_was_pushing)
13638 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13640 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13642 if (player->push_delay_value == -1)
13643 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13645 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13647 if (!player->is_pushing)
13648 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13651 player->is_pushing = TRUE;
13652 player->is_active = TRUE;
13654 if (!(IN_LEV_FIELD(nextx, nexty) &&
13655 (IS_FREE(nextx, nexty) ||
13656 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13657 IS_SB_ELEMENT(element)))))
13658 return MP_NO_ACTION;
13660 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13661 return MP_NO_ACTION;
13663 if (player->push_delay == -1) /* new pushing; restart delay */
13664 player->push_delay = 0;
13666 if (player->push_delay < player->push_delay_value &&
13667 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13668 element != EL_SPRING && element != EL_BALLOON)
13670 /* make sure that there is no move delay before next try to push */
13671 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13672 player->move_delay = 0;
13674 return MP_NO_ACTION;
13677 if (IS_SB_ELEMENT(element))
13679 if (element == EL_SOKOBAN_FIELD_FULL)
13681 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13682 local_player->sokobanfields_still_needed++;
13685 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13687 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13688 local_player->sokobanfields_still_needed--;
13691 Feld[x][y] = EL_SOKOBAN_OBJECT;
13693 if (Back[x][y] == Back[nextx][nexty])
13694 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13695 else if (Back[x][y] != 0)
13696 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13699 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13702 if (local_player->sokobanfields_still_needed == 0 &&
13703 game.emulation == EMU_SOKOBAN)
13705 PlayerWins(player);
13707 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13711 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13713 InitMovingField(x, y, move_direction);
13714 GfxAction[x][y] = ACTION_PUSHING;
13716 if (mode == DF_SNAP)
13717 ContinueMoving(x, y);
13719 MovPos[x][y] = (dx != 0 ? dx : dy);
13721 Pushed[x][y] = TRUE;
13722 Pushed[nextx][nexty] = TRUE;
13724 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13725 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13727 player->push_delay_value = -1; /* get new value later */
13729 /* check for element change _after_ element has been pushed */
13730 if (game.use_change_when_pushing_bug)
13732 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13733 player->index_bit, dig_side);
13734 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13735 player->index_bit, dig_side);
13738 else if (IS_SWITCHABLE(element))
13740 if (PLAYER_SWITCHING(player, x, y))
13742 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13743 player->index_bit, dig_side);
13748 player->is_switching = TRUE;
13749 player->switch_x = x;
13750 player->switch_y = y;
13752 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13754 if (element == EL_ROBOT_WHEEL)
13756 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13760 DrawLevelField(x, y);
13762 else if (element == EL_SP_TERMINAL)
13766 SCAN_PLAYFIELD(xx, yy)
13768 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13770 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13771 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13774 else if (IS_BELT_SWITCH(element))
13776 ToggleBeltSwitch(x, y);
13778 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13779 element == EL_SWITCHGATE_SWITCH_DOWN ||
13780 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13781 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13783 ToggleSwitchgateSwitch(x, y);
13785 else if (element == EL_LIGHT_SWITCH ||
13786 element == EL_LIGHT_SWITCH_ACTIVE)
13788 ToggleLightSwitch(x, y);
13790 else if (element == EL_TIMEGATE_SWITCH ||
13791 element == EL_DC_TIMEGATE_SWITCH)
13793 ActivateTimegateSwitch(x, y);
13795 else if (element == EL_BALLOON_SWITCH_LEFT ||
13796 element == EL_BALLOON_SWITCH_RIGHT ||
13797 element == EL_BALLOON_SWITCH_UP ||
13798 element == EL_BALLOON_SWITCH_DOWN ||
13799 element == EL_BALLOON_SWITCH_NONE ||
13800 element == EL_BALLOON_SWITCH_ANY)
13802 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13803 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13804 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13805 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13806 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13809 else if (element == EL_LAMP)
13811 Feld[x][y] = EL_LAMP_ACTIVE;
13812 local_player->lights_still_needed--;
13814 ResetGfxAnimation(x, y);
13815 DrawLevelField(x, y);
13817 else if (element == EL_TIME_ORB_FULL)
13819 Feld[x][y] = EL_TIME_ORB_EMPTY;
13821 if (level.time > 0 || level.use_time_orb_bug)
13823 TimeLeft += level.time_orb_time;
13826 game_control_value[GAME_CONTROL_TIME] = TimeLeft;
13828 DisplayGameControlValues();
13830 DrawGameValue_Time(TimeLeft);
13834 ResetGfxAnimation(x, y);
13835 DrawLevelField(x, y);
13837 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13838 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13842 game.ball_state = !game.ball_state;
13844 SCAN_PLAYFIELD(xx, yy)
13846 int e = Feld[xx][yy];
13848 if (game.ball_state)
13850 if (e == EL_EMC_MAGIC_BALL)
13851 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13852 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13853 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13857 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13858 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13859 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13860 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13865 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13866 player->index_bit, dig_side);
13868 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13869 player->index_bit, dig_side);
13871 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13872 player->index_bit, dig_side);
13878 if (!PLAYER_SWITCHING(player, x, y))
13880 player->is_switching = TRUE;
13881 player->switch_x = x;
13882 player->switch_y = y;
13884 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13885 player->index_bit, dig_side);
13886 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13887 player->index_bit, dig_side);
13889 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13890 player->index_bit, dig_side);
13891 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13892 player->index_bit, dig_side);
13895 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13896 player->index_bit, dig_side);
13897 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13898 player->index_bit, dig_side);
13900 return MP_NO_ACTION;
13903 player->push_delay = -1;
13905 if (is_player) /* function can also be called by EL_PENGUIN */
13907 if (Feld[x][y] != element) /* really digged/collected something */
13909 player->is_collecting = !player->is_digging;
13910 player->is_active = TRUE;
13917 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13919 int jx = player->jx, jy = player->jy;
13920 int x = jx + dx, y = jy + dy;
13921 int snap_direction = (dx == -1 ? MV_LEFT :
13922 dx == +1 ? MV_RIGHT :
13924 dy == +1 ? MV_DOWN : MV_NONE);
13925 boolean can_continue_snapping = (level.continuous_snapping &&
13926 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13928 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13931 if (!player->active || !IN_LEV_FIELD(x, y))
13939 if (player->MovPos == 0)
13940 player->is_pushing = FALSE;
13942 player->is_snapping = FALSE;
13944 if (player->MovPos == 0)
13946 player->is_moving = FALSE;
13947 player->is_digging = FALSE;
13948 player->is_collecting = FALSE;
13954 #if USE_NEW_CONTINUOUS_SNAPPING
13955 /* prevent snapping with already pressed snap key when not allowed */
13956 if (player->is_snapping && !can_continue_snapping)
13959 if (player->is_snapping)
13963 player->MovDir = snap_direction;
13965 if (player->MovPos == 0)
13967 player->is_moving = FALSE;
13968 player->is_digging = FALSE;
13969 player->is_collecting = FALSE;
13972 player->is_dropping = FALSE;
13973 player->is_dropping_pressed = FALSE;
13974 player->drop_pressed_delay = 0;
13976 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13979 player->is_snapping = TRUE;
13980 player->is_active = TRUE;
13982 if (player->MovPos == 0)
13984 player->is_moving = FALSE;
13985 player->is_digging = FALSE;
13986 player->is_collecting = FALSE;
13989 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13990 DrawLevelField(player->last_jx, player->last_jy);
13992 DrawLevelField(x, y);
13997 boolean DropElement(struct PlayerInfo *player)
13999 int old_element, new_element;
14000 int dropx = player->jx, dropy = player->jy;
14001 int drop_direction = player->MovDir;
14002 int drop_side = drop_direction;
14003 int drop_element = (player->inventory_size > 0 ?
14004 player->inventory_element[player->inventory_size - 1] :
14005 player->inventory_infinite_element != EL_UNDEFINED ?
14006 player->inventory_infinite_element :
14007 player->dynabombs_left > 0 ?
14008 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14011 player->is_dropping_pressed = TRUE;
14013 /* do not drop an element on top of another element; when holding drop key
14014 pressed without moving, dropped element must move away before the next
14015 element can be dropped (this is especially important if the next element
14016 is dynamite, which can be placed on background for historical reasons) */
14017 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14020 if (IS_THROWABLE(drop_element))
14022 dropx += GET_DX_FROM_DIR(drop_direction);
14023 dropy += GET_DY_FROM_DIR(drop_direction);
14025 if (!IN_LEV_FIELD(dropx, dropy))
14029 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14030 new_element = drop_element; /* default: no change when dropping */
14032 /* check if player is active, not moving and ready to drop */
14033 if (!player->active || player->MovPos || player->drop_delay > 0)
14036 /* check if player has anything that can be dropped */
14037 if (new_element == EL_UNDEFINED)
14040 /* check if drop key was pressed long enough for EM style dynamite */
14041 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14044 /* check if anything can be dropped at the current position */
14045 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14048 /* collected custom elements can only be dropped on empty fields */
14049 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14052 if (old_element != EL_EMPTY)
14053 Back[dropx][dropy] = old_element; /* store old element on this field */
14055 ResetGfxAnimation(dropx, dropy);
14056 ResetRandomAnimationValue(dropx, dropy);
14058 if (player->inventory_size > 0 ||
14059 player->inventory_infinite_element != EL_UNDEFINED)
14061 if (player->inventory_size > 0)
14063 player->inventory_size--;
14065 DrawGameDoorValues();
14067 if (new_element == EL_DYNAMITE)
14068 new_element = EL_DYNAMITE_ACTIVE;
14069 else if (new_element == EL_EM_DYNAMITE)
14070 new_element = EL_EM_DYNAMITE_ACTIVE;
14071 else if (new_element == EL_SP_DISK_RED)
14072 new_element = EL_SP_DISK_RED_ACTIVE;
14075 Feld[dropx][dropy] = new_element;
14077 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14078 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14079 el2img(Feld[dropx][dropy]), 0);
14081 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14083 /* needed if previous element just changed to "empty" in the last frame */
14084 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14086 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14087 player->index_bit, drop_side);
14088 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14090 player->index_bit, drop_side);
14092 TestIfElementTouchesCustomElement(dropx, dropy);
14094 else /* player is dropping a dyna bomb */
14096 player->dynabombs_left--;
14098 Feld[dropx][dropy] = new_element;
14100 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14101 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14102 el2img(Feld[dropx][dropy]), 0);
14104 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14107 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14108 InitField_WithBug1(dropx, dropy, FALSE);
14110 new_element = Feld[dropx][dropy]; /* element might have changed */
14112 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14113 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14115 int move_direction, nextx, nexty;
14117 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14118 MovDir[dropx][dropy] = drop_direction;
14120 move_direction = MovDir[dropx][dropy];
14121 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14122 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14124 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14126 #if USE_FIX_IMPACT_COLLISION
14127 /* do not cause impact style collision by dropping elements that can fall */
14128 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14130 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14134 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14135 player->is_dropping = TRUE;
14137 player->drop_pressed_delay = 0;
14138 player->is_dropping_pressed = FALSE;
14140 player->drop_x = dropx;
14141 player->drop_y = dropy;
14146 /* ------------------------------------------------------------------------- */
14147 /* game sound playing functions */
14148 /* ------------------------------------------------------------------------- */
14150 static int *loop_sound_frame = NULL;
14151 static int *loop_sound_volume = NULL;
14153 void InitPlayLevelSound()
14155 int num_sounds = getSoundListSize();
14157 checked_free(loop_sound_frame);
14158 checked_free(loop_sound_volume);
14160 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14161 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14164 static void PlayLevelSound(int x, int y, int nr)
14166 int sx = SCREENX(x), sy = SCREENY(y);
14167 int volume, stereo_position;
14168 int max_distance = 8;
14169 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14171 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14172 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14175 if (!IN_LEV_FIELD(x, y) ||
14176 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14177 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14180 volume = SOUND_MAX_VOLUME;
14182 if (!IN_SCR_FIELD(sx, sy))
14184 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14185 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14187 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14190 stereo_position = (SOUND_MAX_LEFT +
14191 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14192 (SCR_FIELDX + 2 * max_distance));
14194 if (IS_LOOP_SOUND(nr))
14196 /* This assures that quieter loop sounds do not overwrite louder ones,
14197 while restarting sound volume comparison with each new game frame. */
14199 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14202 loop_sound_volume[nr] = volume;
14203 loop_sound_frame[nr] = FrameCounter;
14206 PlaySoundExt(nr, volume, stereo_position, type);
14209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14211 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14212 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14213 y < LEVELY(BY1) ? LEVELY(BY1) :
14214 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14218 static void PlayLevelSoundAction(int x, int y, int action)
14220 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14225 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14227 if (sound_effect != SND_UNDEFINED)
14228 PlayLevelSound(x, y, sound_effect);
14231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14234 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14236 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14237 PlayLevelSound(x, y, sound_effect);
14240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14242 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14244 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14245 PlayLevelSound(x, y, sound_effect);
14248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14250 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14252 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14253 StopSound(sound_effect);
14256 static void PlayLevelMusic()
14258 if (levelset.music[level_nr] != MUS_UNDEFINED)
14259 PlayMusic(levelset.music[level_nr]); /* from config file */
14261 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14264 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14266 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14267 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14268 int x = xx - 1 - offset;
14269 int y = yy - 1 - offset;
14274 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14278 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14282 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14286 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14290 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14294 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14298 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14301 case SAMPLE_android_clone:
14302 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14305 case SAMPLE_android_move:
14306 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14309 case SAMPLE_spring:
14310 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14314 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14318 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14321 case SAMPLE_eater_eat:
14322 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14326 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14329 case SAMPLE_collect:
14330 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14333 case SAMPLE_diamond:
14334 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14337 case SAMPLE_squash:
14338 /* !!! CHECK THIS !!! */
14340 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14342 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14346 case SAMPLE_wonderfall:
14347 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14351 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14355 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14359 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14363 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14367 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14371 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14374 case SAMPLE_wonder:
14375 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14379 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14382 case SAMPLE_exit_open:
14383 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14386 case SAMPLE_exit_leave:
14387 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14390 case SAMPLE_dynamite:
14391 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14395 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14399 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14403 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14407 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14411 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14415 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14419 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14425 void ChangeTime(int value)
14427 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14431 /* EMC game engine uses value from time counter of RND game engine */
14432 level.native_em_level->lev->time = *time;
14434 DrawGameValue_Time(*time);
14437 void RaiseScore(int value)
14439 /* EMC game engine and RND game engine have separate score counters */
14440 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14441 &level.native_em_level->lev->score : &local_player->score);
14445 DrawGameValue_Score(*score);
14449 void RaiseScore(int value)
14451 local_player->score += value;
14454 game_control_value[GAME_CONTROL_SCORE] = local_player->score;
14456 DisplayGameControlValues();
14458 DrawGameValue_Score(local_player->score);
14462 void RaiseScoreElement(int element)
14467 case EL_BD_DIAMOND:
14468 case EL_EMERALD_YELLOW:
14469 case EL_EMERALD_RED:
14470 case EL_EMERALD_PURPLE:
14471 case EL_SP_INFOTRON:
14472 RaiseScore(level.score[SC_EMERALD]);
14475 RaiseScore(level.score[SC_DIAMOND]);
14478 RaiseScore(level.score[SC_CRYSTAL]);
14481 RaiseScore(level.score[SC_PEARL]);
14484 case EL_BD_BUTTERFLY:
14485 case EL_SP_ELECTRON:
14486 RaiseScore(level.score[SC_BUG]);
14489 case EL_BD_FIREFLY:
14490 case EL_SP_SNIKSNAK:
14491 RaiseScore(level.score[SC_SPACESHIP]);
14494 case EL_DARK_YAMYAM:
14495 RaiseScore(level.score[SC_YAMYAM]);
14498 RaiseScore(level.score[SC_ROBOT]);
14501 RaiseScore(level.score[SC_PACMAN]);
14504 RaiseScore(level.score[SC_NUT]);
14507 case EL_EM_DYNAMITE:
14508 case EL_SP_DISK_RED:
14509 case EL_DYNABOMB_INCREASE_NUMBER:
14510 case EL_DYNABOMB_INCREASE_SIZE:
14511 case EL_DYNABOMB_INCREASE_POWER:
14512 RaiseScore(level.score[SC_DYNAMITE]);
14514 case EL_SHIELD_NORMAL:
14515 case EL_SHIELD_DEADLY:
14516 RaiseScore(level.score[SC_SHIELD]);
14518 case EL_EXTRA_TIME:
14519 RaiseScore(level.extra_time_score);
14533 case EL_DC_KEY_WHITE:
14534 RaiseScore(level.score[SC_KEY]);
14537 RaiseScore(element_info[element].collect_score);
14542 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14544 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14546 #if defined(NETWORK_AVALIABLE)
14547 if (options.network)
14548 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14557 FadeSkipNextFadeIn();
14559 fading = fading_none;
14563 OpenDoor(DOOR_CLOSE_1);
14566 game_status = GAME_MODE_MAIN;
14569 DrawAndFadeInMainMenu(REDRAW_FIELD);
14577 FadeOut(REDRAW_FIELD);
14580 game_status = GAME_MODE_MAIN;
14582 DrawAndFadeInMainMenu(REDRAW_FIELD);
14586 else /* continue playing the game */
14588 if (tape.playing && tape.deactivate_display)
14589 TapeDeactivateDisplayOff(TRUE);
14591 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14593 if (tape.playing && tape.deactivate_display)
14594 TapeDeactivateDisplayOn();
14598 void RequestQuitGame(boolean ask_if_really_quit)
14600 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14601 boolean skip_request = AllPlayersGone || quick_quit;
14603 RequestQuitGameExt(skip_request, quick_quit,
14604 "Do you really want to quit the game ?");
14608 /* ------------------------------------------------------------------------- */
14609 /* random generator functions */
14610 /* ------------------------------------------------------------------------- */
14612 unsigned int InitEngineRandom_RND(long seed)
14614 game.num_random_calls = 0;
14617 unsigned int rnd_seed = InitEngineRandom(seed);
14619 printf("::: START RND: %d\n", rnd_seed);
14624 return InitEngineRandom(seed);
14630 unsigned int RND(int max)
14634 game.num_random_calls++;
14636 return GetEngineRandom(max);
14643 /* ------------------------------------------------------------------------- */
14644 /* game engine snapshot handling functions */
14645 /* ------------------------------------------------------------------------- */
14647 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
14649 struct EngineSnapshotInfo
14651 /* runtime values for custom element collect score */
14652 int collect_score[NUM_CUSTOM_ELEMENTS];
14654 /* runtime values for group element choice position */
14655 int choice_pos[NUM_GROUP_ELEMENTS];
14657 /* runtime values for belt position animations */
14658 int belt_graphic[4 * NUM_BELT_PARTS];
14659 int belt_anim_mode[4 * NUM_BELT_PARTS];
14662 struct EngineSnapshotNodeInfo
14669 static struct EngineSnapshotInfo engine_snapshot_rnd;
14670 static ListNode *engine_snapshot_list = NULL;
14671 static char *snapshot_level_identifier = NULL;
14672 static int snapshot_level_nr = -1;
14674 void FreeEngineSnapshot()
14676 while (engine_snapshot_list != NULL)
14677 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14680 setString(&snapshot_level_identifier, NULL);
14681 snapshot_level_nr = -1;
14684 static void SaveEngineSnapshotValues_RND()
14686 static int belt_base_active_element[4] =
14688 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14689 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14690 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14691 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14695 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14697 int element = EL_CUSTOM_START + i;
14699 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14702 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14704 int element = EL_GROUP_START + i;
14706 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14709 for (i = 0; i < 4; i++)
14711 for (j = 0; j < NUM_BELT_PARTS; j++)
14713 int element = belt_base_active_element[i] + j;
14714 int graphic = el2img(element);
14715 int anim_mode = graphic_info[graphic].anim_mode;
14717 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14718 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14723 static void LoadEngineSnapshotValues_RND()
14725 unsigned long num_random_calls = game.num_random_calls;
14728 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14730 int element = EL_CUSTOM_START + i;
14732 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14735 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14737 int element = EL_GROUP_START + i;
14739 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14742 for (i = 0; i < 4; i++)
14744 for (j = 0; j < NUM_BELT_PARTS; j++)
14746 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14747 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14749 graphic_info[graphic].anim_mode = anim_mode;
14753 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14755 InitRND(tape.random_seed);
14756 for (i = 0; i < num_random_calls; i++)
14760 if (game.num_random_calls != num_random_calls)
14762 Error(ERR_INFO, "number of random calls out of sync");
14763 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14764 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14765 Error(ERR_EXIT, "this should not happen -- please debug");
14769 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14771 struct EngineSnapshotNodeInfo *bi =
14772 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14774 bi->buffer_orig = buffer;
14775 bi->buffer_copy = checked_malloc(size);
14778 memcpy(bi->buffer_copy, buffer, size);
14780 addNodeToList(&engine_snapshot_list, NULL, bi);
14783 void SaveEngineSnapshot()
14785 FreeEngineSnapshot(); /* free previous snapshot, if needed */
14787 if (level_editor_test_game) /* do not save snapshots from editor */
14790 /* copy some special values to a structure better suited for the snapshot */
14792 SaveEngineSnapshotValues_RND();
14793 SaveEngineSnapshotValues_EM();
14795 /* save values stored in special snapshot structure */
14797 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14798 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14800 /* save further RND engine values */
14802 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14804 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14806 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14807 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14808 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14811 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14812 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14813 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14814 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14815 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14817 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14818 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14819 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14821 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14823 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14825 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14826 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14828 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14829 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14830 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14831 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14832 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14833 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14834 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14835 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14836 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14837 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14838 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14839 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14840 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14841 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14842 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14843 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14844 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14845 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14847 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14848 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14850 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14851 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14852 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14854 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14855 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14857 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14858 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14859 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14860 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14861 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14863 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14864 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14866 /* save level identification information */
14868 setString(&snapshot_level_identifier, leveldir_current->identifier);
14869 snapshot_level_nr = level_nr;
14872 ListNode *node = engine_snapshot_list;
14875 while (node != NULL)
14877 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14882 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14886 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14888 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14891 void LoadEngineSnapshot()
14893 ListNode *node = engine_snapshot_list;
14895 if (engine_snapshot_list == NULL)
14898 while (node != NULL)
14900 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14905 /* restore special values from snapshot structure */
14907 LoadEngineSnapshotValues_RND();
14908 LoadEngineSnapshotValues_EM();
14911 boolean CheckEngineSnapshot()
14913 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14914 snapshot_level_nr == level_nr);
14918 /* ---------- new game button stuff ---------------------------------------- */
14920 /* graphic position values for game buttons */
14921 #define GAME_BUTTON_XSIZE 30
14922 #define GAME_BUTTON_YSIZE 30
14923 #define GAME_BUTTON_XPOS 5
14924 #define GAME_BUTTON_YPOS 215
14925 #define SOUND_BUTTON_XPOS 5
14926 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14928 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14929 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14930 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14931 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14932 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14933 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14941 } gamebutton_info[NUM_GAME_BUTTONS] =
14945 &game.button.stop.x, &game.button.stop.y,
14946 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14951 &game.button.pause.x, &game.button.pause.y,
14952 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14953 GAME_CTRL_ID_PAUSE,
14957 &game.button.play.x, &game.button.play.y,
14958 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14963 &game.button.sound_music.x, &game.button.sound_music.y,
14964 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14965 SOUND_CTRL_ID_MUSIC,
14966 "background music on/off"
14969 &game.button.sound_loops.x, &game.button.sound_loops.y,
14970 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
14971 SOUND_CTRL_ID_LOOPS,
14972 "sound loops on/off"
14975 &game.button.sound_simple.x,&game.button.sound_simple.y,
14976 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
14977 SOUND_CTRL_ID_SIMPLE,
14978 "normal sounds on/off"
14982 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
14987 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
14988 GAME_CTRL_ID_PAUSE,
14992 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
14997 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
14998 SOUND_CTRL_ID_MUSIC,
14999 "background music on/off"
15002 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15003 SOUND_CTRL_ID_LOOPS,
15004 "sound loops on/off"
15007 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15008 SOUND_CTRL_ID_SIMPLE,
15009 "normal sounds on/off"
15014 void CreateGameButtons()
15018 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15020 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15021 struct GadgetInfo *gi;
15024 unsigned long event_mask;
15026 int gd_xoffset, gd_yoffset;
15027 int gd_x1, gd_x2, gd_y1, gd_y2;
15030 x = DX + *gamebutton_info[i].x;
15031 y = DY + *gamebutton_info[i].y;
15032 gd_xoffset = gamebutton_info[i].gd_x;
15033 gd_yoffset = gamebutton_info[i].gd_y;
15034 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15035 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15037 if (id == GAME_CTRL_ID_STOP ||
15038 id == GAME_CTRL_ID_PAUSE ||
15039 id == GAME_CTRL_ID_PLAY)
15041 button_type = GD_TYPE_NORMAL_BUTTON;
15043 event_mask = GD_EVENT_RELEASED;
15044 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15045 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15049 button_type = GD_TYPE_CHECK_BUTTON;
15051 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15052 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15053 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15054 event_mask = GD_EVENT_PRESSED;
15055 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15056 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15059 gi = CreateGadget(GDI_CUSTOM_ID, id,
15060 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15065 GDI_X, DX + gd_xoffset,
15066 GDI_Y, DY + gd_yoffset,
15068 GDI_WIDTH, GAME_BUTTON_XSIZE,
15069 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15070 GDI_TYPE, button_type,
15071 GDI_STATE, GD_BUTTON_UNPRESSED,
15072 GDI_CHECKED, checked,
15073 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15074 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15075 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15076 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15077 GDI_EVENT_MASK, event_mask,
15078 GDI_CALLBACK_ACTION, HandleGameButtons,
15082 Error(ERR_EXIT, "cannot create gadget");
15084 game_gadget[id] = gi;
15088 void FreeGameButtons()
15092 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15093 FreeGadget(game_gadget[i]);
15096 static void MapGameButtons()
15100 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15101 MapGadget(game_gadget[i]);
15104 void UnmapGameButtons()
15108 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15109 UnmapGadget(game_gadget[i]);
15112 static void HandleGameButtons(struct GadgetInfo *gi)
15114 int id = gi->custom_id;
15116 if (game_status != GAME_MODE_PLAYING)
15121 case GAME_CTRL_ID_STOP:
15125 RequestQuitGame(TRUE);
15128 case GAME_CTRL_ID_PAUSE:
15129 if (options.network)
15131 #if defined(NETWORK_AVALIABLE)
15133 SendToServer_ContinuePlaying();
15135 SendToServer_PausePlaying();
15139 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15142 case GAME_CTRL_ID_PLAY:
15145 #if defined(NETWORK_AVALIABLE)
15146 if (options.network)
15147 SendToServer_ContinuePlaying();
15151 tape.pausing = FALSE;
15152 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15157 case SOUND_CTRL_ID_MUSIC:
15158 if (setup.sound_music)
15160 setup.sound_music = FALSE;
15163 else if (audio.music_available)
15165 setup.sound = setup.sound_music = TRUE;
15167 SetAudioMode(setup.sound);
15173 case SOUND_CTRL_ID_LOOPS:
15174 if (setup.sound_loops)
15175 setup.sound_loops = FALSE;
15176 else if (audio.loops_available)
15178 setup.sound = setup.sound_loops = TRUE;
15179 SetAudioMode(setup.sound);
15183 case SOUND_CTRL_ID_SIMPLE:
15184 if (setup.sound_simple)
15185 setup.sound_simple = FALSE;
15186 else if (audio.sound_available)
15188 setup.sound = setup.sound_simple = TRUE;
15189 SetAudioMode(setup.sound);