1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_TIME_ANIM 35
126 #define GAME_PANEL_HEALTH 36
127 #define GAME_PANEL_HEALTH_ANIM 37
128 #define GAME_PANEL_FRAME 38
129 #define GAME_PANEL_SHIELD_NORMAL 39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 40
131 #define GAME_PANEL_SHIELD_DEADLY 41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 42
133 #define GAME_PANEL_EXIT 43
134 #define GAME_PANEL_EMC_MAGIC_BALL 44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45
136 #define GAME_PANEL_LIGHT_SWITCH 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 47
138 #define GAME_PANEL_TIMEGATE_SWITCH 48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 49
140 #define GAME_PANEL_SWITCHGATE_SWITCH 50
141 #define GAME_PANEL_EMC_LENSES 51
142 #define GAME_PANEL_EMC_LENSES_TIME 52
143 #define GAME_PANEL_EMC_MAGNIFIER 53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 54
145 #define GAME_PANEL_BALLOON_SWITCH 55
146 #define GAME_PANEL_DYNABOMB_NUMBER 56
147 #define GAME_PANEL_DYNABOMB_SIZE 57
148 #define GAME_PANEL_DYNABOMB_POWER 58
149 #define GAME_PANEL_PENGUINS 59
150 #define GAME_PANEL_SOKOBAN_OBJECTS 60
151 #define GAME_PANEL_SOKOBAN_FIELDS 61
152 #define GAME_PANEL_ROBOT_WHEEL 62
153 #define GAME_PANEL_CONVEYOR_BELT_1 63
154 #define GAME_PANEL_CONVEYOR_BELT_2 64
155 #define GAME_PANEL_CONVEYOR_BELT_3 65
156 #define GAME_PANEL_CONVEYOR_BELT_4 66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70
161 #define GAME_PANEL_MAGIC_WALL 71
162 #define GAME_PANEL_MAGIC_WALL_TIME 72
163 #define GAME_PANEL_GRAVITY_STATE 73
164 #define GAME_PANEL_GRAPHIC_1 74
165 #define GAME_PANEL_GRAPHIC_2 75
166 #define GAME_PANEL_GRAPHIC_3 76
167 #define GAME_PANEL_GRAPHIC_4 77
168 #define GAME_PANEL_GRAPHIC_5 78
169 #define GAME_PANEL_GRAPHIC_6 79
170 #define GAME_PANEL_GRAPHIC_7 80
171 #define GAME_PANEL_GRAPHIC_8 81
172 #define GAME_PANEL_ELEMENT_1 82
173 #define GAME_PANEL_ELEMENT_2 83
174 #define GAME_PANEL_ELEMENT_3 84
175 #define GAME_PANEL_ELEMENT_4 85
176 #define GAME_PANEL_ELEMENT_5 86
177 #define GAME_PANEL_ELEMENT_6 87
178 #define GAME_PANEL_ELEMENT_7 88
179 #define GAME_PANEL_ELEMENT_8 89
180 #define GAME_PANEL_ELEMENT_COUNT_1 90
181 #define GAME_PANEL_ELEMENT_COUNT_2 91
182 #define GAME_PANEL_ELEMENT_COUNT_3 92
183 #define GAME_PANEL_ELEMENT_COUNT_4 93
184 #define GAME_PANEL_ELEMENT_COUNT_5 94
185 #define GAME_PANEL_ELEMENT_COUNT_6 95
186 #define GAME_PANEL_ELEMENT_COUNT_7 96
187 #define GAME_PANEL_ELEMENT_COUNT_8 97
188 #define GAME_PANEL_CE_SCORE_1 98
189 #define GAME_PANEL_CE_SCORE_2 99
190 #define GAME_PANEL_CE_SCORE_3 100
191 #define GAME_PANEL_CE_SCORE_4 101
192 #define GAME_PANEL_CE_SCORE_5 102
193 #define GAME_PANEL_CE_SCORE_6 103
194 #define GAME_PANEL_CE_SCORE_7 104
195 #define GAME_PANEL_CE_SCORE_8 105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 113
204 #define GAME_PANEL_PLAYER_NAME 114
205 #define GAME_PANEL_LEVEL_NAME 115
206 #define GAME_PANEL_LEVEL_AUTHOR 116
208 #define NUM_GAME_PANEL_CONTROLS 117
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int graphic, graphic_active;
227 int value, last_value;
228 int frame, last_frame;
233 static struct GamePanelControlInfo game_panel_controls[] =
236 GAME_PANEL_LEVEL_NUMBER,
237 &game.panel.level_number,
246 GAME_PANEL_INVENTORY_COUNT,
247 &game.panel.inventory_count,
251 GAME_PANEL_INVENTORY_FIRST_1,
252 &game.panel.inventory_first[0],
256 GAME_PANEL_INVENTORY_FIRST_2,
257 &game.panel.inventory_first[1],
261 GAME_PANEL_INVENTORY_FIRST_3,
262 &game.panel.inventory_first[2],
266 GAME_PANEL_INVENTORY_FIRST_4,
267 &game.panel.inventory_first[3],
271 GAME_PANEL_INVENTORY_FIRST_5,
272 &game.panel.inventory_first[4],
276 GAME_PANEL_INVENTORY_FIRST_6,
277 &game.panel.inventory_first[5],
281 GAME_PANEL_INVENTORY_FIRST_7,
282 &game.panel.inventory_first[6],
286 GAME_PANEL_INVENTORY_FIRST_8,
287 &game.panel.inventory_first[7],
291 GAME_PANEL_INVENTORY_LAST_1,
292 &game.panel.inventory_last[0],
296 GAME_PANEL_INVENTORY_LAST_2,
297 &game.panel.inventory_last[1],
301 GAME_PANEL_INVENTORY_LAST_3,
302 &game.panel.inventory_last[2],
306 GAME_PANEL_INVENTORY_LAST_4,
307 &game.panel.inventory_last[3],
311 GAME_PANEL_INVENTORY_LAST_5,
312 &game.panel.inventory_last[4],
316 GAME_PANEL_INVENTORY_LAST_6,
317 &game.panel.inventory_last[5],
321 GAME_PANEL_INVENTORY_LAST_7,
322 &game.panel.inventory_last[6],
326 GAME_PANEL_INVENTORY_LAST_8,
327 &game.panel.inventory_last[7],
371 GAME_PANEL_KEY_WHITE,
372 &game.panel.key_white,
376 GAME_PANEL_KEY_WHITE_COUNT,
377 &game.panel.key_white_count,
386 GAME_PANEL_HIGHSCORE,
387 &game.panel.highscore,
411 GAME_PANEL_TIME_ANIM,
412 &game.panel.time_anim,
415 IMG_GFX_GAME_PANEL_TIME_ANIM,
416 IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
424 GAME_PANEL_HEALTH_ANIM,
425 &game.panel.health_anim,
428 IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429 IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
437 GAME_PANEL_SHIELD_NORMAL,
438 &game.panel.shield_normal,
442 GAME_PANEL_SHIELD_NORMAL_TIME,
443 &game.panel.shield_normal_time,
447 GAME_PANEL_SHIELD_DEADLY,
448 &game.panel.shield_deadly,
452 GAME_PANEL_SHIELD_DEADLY_TIME,
453 &game.panel.shield_deadly_time,
462 GAME_PANEL_EMC_MAGIC_BALL,
463 &game.panel.emc_magic_ball,
467 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468 &game.panel.emc_magic_ball_switch,
472 GAME_PANEL_LIGHT_SWITCH,
473 &game.panel.light_switch,
477 GAME_PANEL_LIGHT_SWITCH_TIME,
478 &game.panel.light_switch_time,
482 GAME_PANEL_TIMEGATE_SWITCH,
483 &game.panel.timegate_switch,
487 GAME_PANEL_TIMEGATE_SWITCH_TIME,
488 &game.panel.timegate_switch_time,
492 GAME_PANEL_SWITCHGATE_SWITCH,
493 &game.panel.switchgate_switch,
497 GAME_PANEL_EMC_LENSES,
498 &game.panel.emc_lenses,
502 GAME_PANEL_EMC_LENSES_TIME,
503 &game.panel.emc_lenses_time,
507 GAME_PANEL_EMC_MAGNIFIER,
508 &game.panel.emc_magnifier,
512 GAME_PANEL_EMC_MAGNIFIER_TIME,
513 &game.panel.emc_magnifier_time,
517 GAME_PANEL_BALLOON_SWITCH,
518 &game.panel.balloon_switch,
522 GAME_PANEL_DYNABOMB_NUMBER,
523 &game.panel.dynabomb_number,
527 GAME_PANEL_DYNABOMB_SIZE,
528 &game.panel.dynabomb_size,
532 GAME_PANEL_DYNABOMB_POWER,
533 &game.panel.dynabomb_power,
538 &game.panel.penguins,
542 GAME_PANEL_SOKOBAN_OBJECTS,
543 &game.panel.sokoban_objects,
547 GAME_PANEL_SOKOBAN_FIELDS,
548 &game.panel.sokoban_fields,
552 GAME_PANEL_ROBOT_WHEEL,
553 &game.panel.robot_wheel,
557 GAME_PANEL_CONVEYOR_BELT_1,
558 &game.panel.conveyor_belt[0],
562 GAME_PANEL_CONVEYOR_BELT_2,
563 &game.panel.conveyor_belt[1],
567 GAME_PANEL_CONVEYOR_BELT_3,
568 &game.panel.conveyor_belt[2],
572 GAME_PANEL_CONVEYOR_BELT_4,
573 &game.panel.conveyor_belt[3],
577 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578 &game.panel.conveyor_belt_switch[0],
582 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583 &game.panel.conveyor_belt_switch[1],
587 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588 &game.panel.conveyor_belt_switch[2],
592 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593 &game.panel.conveyor_belt_switch[3],
597 GAME_PANEL_MAGIC_WALL,
598 &game.panel.magic_wall,
602 GAME_PANEL_MAGIC_WALL_TIME,
603 &game.panel.magic_wall_time,
607 GAME_PANEL_GRAVITY_STATE,
608 &game.panel.gravity_state,
612 GAME_PANEL_GRAPHIC_1,
613 &game.panel.graphic[0],
617 GAME_PANEL_GRAPHIC_2,
618 &game.panel.graphic[1],
622 GAME_PANEL_GRAPHIC_3,
623 &game.panel.graphic[2],
627 GAME_PANEL_GRAPHIC_4,
628 &game.panel.graphic[3],
632 GAME_PANEL_GRAPHIC_5,
633 &game.panel.graphic[4],
637 GAME_PANEL_GRAPHIC_6,
638 &game.panel.graphic[5],
642 GAME_PANEL_GRAPHIC_7,
643 &game.panel.graphic[6],
647 GAME_PANEL_GRAPHIC_8,
648 &game.panel.graphic[7],
652 GAME_PANEL_ELEMENT_1,
653 &game.panel.element[0],
657 GAME_PANEL_ELEMENT_2,
658 &game.panel.element[1],
662 GAME_PANEL_ELEMENT_3,
663 &game.panel.element[2],
667 GAME_PANEL_ELEMENT_4,
668 &game.panel.element[3],
672 GAME_PANEL_ELEMENT_5,
673 &game.panel.element[4],
677 GAME_PANEL_ELEMENT_6,
678 &game.panel.element[5],
682 GAME_PANEL_ELEMENT_7,
683 &game.panel.element[6],
687 GAME_PANEL_ELEMENT_8,
688 &game.panel.element[7],
692 GAME_PANEL_ELEMENT_COUNT_1,
693 &game.panel.element_count[0],
697 GAME_PANEL_ELEMENT_COUNT_2,
698 &game.panel.element_count[1],
702 GAME_PANEL_ELEMENT_COUNT_3,
703 &game.panel.element_count[2],
707 GAME_PANEL_ELEMENT_COUNT_4,
708 &game.panel.element_count[3],
712 GAME_PANEL_ELEMENT_COUNT_5,
713 &game.panel.element_count[4],
717 GAME_PANEL_ELEMENT_COUNT_6,
718 &game.panel.element_count[5],
722 GAME_PANEL_ELEMENT_COUNT_7,
723 &game.panel.element_count[6],
727 GAME_PANEL_ELEMENT_COUNT_8,
728 &game.panel.element_count[7],
732 GAME_PANEL_CE_SCORE_1,
733 &game.panel.ce_score[0],
737 GAME_PANEL_CE_SCORE_2,
738 &game.panel.ce_score[1],
742 GAME_PANEL_CE_SCORE_3,
743 &game.panel.ce_score[2],
747 GAME_PANEL_CE_SCORE_4,
748 &game.panel.ce_score[3],
752 GAME_PANEL_CE_SCORE_5,
753 &game.panel.ce_score[4],
757 GAME_PANEL_CE_SCORE_6,
758 &game.panel.ce_score[5],
762 GAME_PANEL_CE_SCORE_7,
763 &game.panel.ce_score[6],
767 GAME_PANEL_CE_SCORE_8,
768 &game.panel.ce_score[7],
772 GAME_PANEL_CE_SCORE_1_ELEMENT,
773 &game.panel.ce_score_element[0],
777 GAME_PANEL_CE_SCORE_2_ELEMENT,
778 &game.panel.ce_score_element[1],
782 GAME_PANEL_CE_SCORE_3_ELEMENT,
783 &game.panel.ce_score_element[2],
787 GAME_PANEL_CE_SCORE_4_ELEMENT,
788 &game.panel.ce_score_element[3],
792 GAME_PANEL_CE_SCORE_5_ELEMENT,
793 &game.panel.ce_score_element[4],
797 GAME_PANEL_CE_SCORE_6_ELEMENT,
798 &game.panel.ce_score_element[5],
802 GAME_PANEL_CE_SCORE_7_ELEMENT,
803 &game.panel.ce_score_element[6],
807 GAME_PANEL_CE_SCORE_8_ELEMENT,
808 &game.panel.ce_score_element[7],
812 GAME_PANEL_PLAYER_NAME,
813 &game.panel.player_name,
817 GAME_PANEL_LEVEL_NAME,
818 &game.panel.level_name,
822 GAME_PANEL_LEVEL_AUTHOR,
823 &game.panel.level_author,
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING 3
836 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION 2
838 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF -1
842 #define INITIAL_MOVE_DELAY_ON 0
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED 32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED 4
848 #define MOVE_DELAY_MAX_SPEED 1
850 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
853 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
858 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
860 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 #define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP 0
1009 #define GAME_CTRL_ID_PAUSE 1
1010 #define GAME_CTRL_ID_PLAY 2
1011 #define GAME_CTRL_ID_UNDO 3
1012 #define GAME_CTRL_ID_REDO 4
1013 #define GAME_CTRL_ID_SAVE 5
1014 #define GAME_CTRL_ID_PAUSE2 6
1015 #define GAME_CTRL_ID_LOAD 7
1016 #define GAME_CTRL_ID_PANEL_STOP 8
1017 #define GAME_CTRL_ID_PANEL_PAUSE 9
1018 #define GAME_CTRL_ID_PANEL_PLAY 10
1019 #define SOUND_CTRL_ID_MUSIC 11
1020 #define SOUND_CTRL_ID_LOOPS 12
1021 #define SOUND_CTRL_ID_SIMPLE 13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC 14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS 15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE 16
1026 #define NUM_GAME_BUTTONS 17
1029 /* forward declaration for internal use */
1031 static void CreateField(int, int, int);
1033 static void ResetGfxAnimation(int, int);
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1069 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1071 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev) \
1075 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1077 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1079 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1091 static void HandleGameButtons(struct GadgetInfo *);
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1125 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1127 if (recursion_loop_detected) \
1130 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1132 recursion_loop_detected = TRUE; \
1133 recursion_loop_element = (e); \
1136 recursion_loop_depth++; \
1139 #define RECURSION_LOOP_DETECTION_END() \
1141 recursion_loop_depth--; \
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1148 static int map_player_action[MAX_PLAYERS];
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after */
1153 /* a specified time, eventually calling a function when changing */
1154 /* ------------------------------------------------------------------------- */
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1174 struct ChangingElementInfo
1179 void (*pre_change_function)(int x, int y);
1180 void (*change_function)(int x, int y);
1181 void (*post_change_function)(int x, int y);
1184 static struct ChangingElementInfo change_delay_list[] =
1219 EL_STEEL_EXIT_OPENING,
1227 EL_STEEL_EXIT_CLOSING,
1228 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1283 EL_SWITCHGATE_OPENING,
1291 EL_SWITCHGATE_CLOSING,
1292 EL_SWITCHGATE_CLOSED,
1299 EL_TIMEGATE_OPENING,
1307 EL_TIMEGATE_CLOSING,
1316 EL_ACID_SPLASH_LEFT,
1324 EL_ACID_SPLASH_RIGHT,
1333 EL_SP_BUGGY_BASE_ACTIVATING,
1340 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVE,
1348 EL_SP_BUGGY_BASE_ACTIVE,
1372 EL_ROBOT_WHEEL_ACTIVE,
1380 EL_TIMEGATE_SWITCH_ACTIVE,
1388 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH,
1396 EL_EMC_MAGIC_BALL_ACTIVE,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1404 EL_EMC_SPRING_BUMPER_ACTIVE,
1405 EL_EMC_SPRING_BUMPER,
1412 EL_DIAGONAL_SHRINKING,
1420 EL_DIAGONAL_GROWING,
1441 int push_delay_fixed, push_delay_random;
1445 { EL_SPRING, 0, 0 },
1446 { EL_BALLOON, 0, 0 },
1448 { EL_SOKOBAN_OBJECT, 2, 0 },
1449 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1450 { EL_SATELLITE, 2, 0 },
1451 { EL_SP_DISK_YELLOW, 2, 0 },
1453 { EL_UNDEFINED, 0, 0 },
1461 move_stepsize_list[] =
1463 { EL_AMOEBA_DROP, 2 },
1464 { EL_AMOEBA_DROPPING, 2 },
1465 { EL_QUICKSAND_FILLING, 1 },
1466 { EL_QUICKSAND_EMPTYING, 1 },
1467 { EL_QUICKSAND_FAST_FILLING, 2 },
1468 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469 { EL_MAGIC_WALL_FILLING, 2 },
1470 { EL_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_BD_MAGIC_WALL_FILLING, 2 },
1472 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1473 { EL_DC_MAGIC_WALL_FILLING, 2 },
1474 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_UNDEFINED, 0 },
1484 collect_count_list[] =
1487 { EL_BD_DIAMOND, 1 },
1488 { EL_EMERALD_YELLOW, 1 },
1489 { EL_EMERALD_RED, 1 },
1490 { EL_EMERALD_PURPLE, 1 },
1492 { EL_SP_INFOTRON, 1 },
1496 { EL_UNDEFINED, 0 },
1504 access_direction_list[] =
1506 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1508 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1509 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1512 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1513 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1514 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1515 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1516 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1518 { EL_SP_PORT_LEFT, MV_RIGHT },
1519 { EL_SP_PORT_RIGHT, MV_LEFT },
1520 { EL_SP_PORT_UP, MV_DOWN },
1521 { EL_SP_PORT_DOWN, MV_UP },
1522 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1523 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1524 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1529 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1538 { EL_UNDEFINED, MV_NONE }
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1546 IS_JUST_CHANGING(x, y))
1548 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1556 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1557 (y) >= 0 && (y) <= lev_fieldy - 1; \
1558 (y) += playfield_scan_delta_y) \
1559 for ((x) = playfield_scan_start_x; \
1560 (x) >= 0 && (x) <= lev_fieldx - 1; \
1561 (x) += playfield_scan_delta_x)
1564 void DEBUG_SetMaximumDynamite()
1568 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570 local_player->inventory_element[local_player->inventory_size++] =
1575 static void InitPlayfieldScanModeVars()
1577 if (game.use_reverse_scan_direction)
1579 playfield_scan_start_x = lev_fieldx - 1;
1580 playfield_scan_start_y = lev_fieldy - 1;
1582 playfield_scan_delta_x = -1;
1583 playfield_scan_delta_y = -1;
1587 playfield_scan_start_x = 0;
1588 playfield_scan_start_y = 0;
1590 playfield_scan_delta_x = 1;
1591 playfield_scan_delta_y = 1;
1595 static void InitPlayfieldScanMode(int mode)
1597 game.use_reverse_scan_direction =
1598 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600 InitPlayfieldScanModeVars();
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1606 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608 /* make sure that stepsize value is always a power of 2 */
1609 move_stepsize = (1 << log_2(move_stepsize));
1611 return TILEX / move_stepsize;
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1617 int player_nr = player->index_nr;
1618 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621 /* do no immediately change move delay -- the player might just be moving */
1622 player->move_delay_value_next = move_delay;
1624 /* information if player can move must be set separately */
1625 player->cannot_move = cannot_move;
1629 player->move_delay = game.initial_move_delay[player_nr];
1630 player->move_delay_value = game.initial_move_delay_value[player_nr];
1632 player->move_delay_value_next = -1;
1634 player->move_delay_reset_counter = 0;
1638 void GetPlayerConfig()
1640 GameFrameDelay = setup.game_frame_delay;
1642 if (!audio.sound_available)
1643 setup.sound_simple = FALSE;
1645 if (!audio.loops_available)
1646 setup.sound_loops = FALSE;
1648 if (!audio.music_available)
1649 setup.sound_music = FALSE;
1651 if (!video.fullscreen_available)
1652 setup.fullscreen = FALSE;
1654 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656 SetAudioMode(setup.sound);
1659 int GetElementFromGroupElement(int element)
1661 if (IS_GROUP_ELEMENT(element))
1663 struct ElementGroupInfo *group = element_info[element].group;
1664 int last_anim_random_frame = gfx.anim_random_frame;
1667 if (group->choice_mode == ANIM_RANDOM)
1668 gfx.anim_random_frame = RND(group->num_elements_resolved);
1670 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671 group->choice_mode, 0,
1674 if (group->choice_mode == ANIM_RANDOM)
1675 gfx.anim_random_frame = last_anim_random_frame;
1677 group->choice_pos++;
1679 element = group->element_resolved[element_pos];
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 if (element == EL_SP_MURPHY)
1691 if (stored_player[0].present)
1693 Feld[x][y] = EL_SP_MURPHY_CLONE;
1699 stored_player[0].initial_element = element;
1700 stored_player[0].use_murphy = TRUE;
1702 if (!level.use_artwork_element[0])
1703 stored_player[0].artwork_element = EL_SP_MURPHY;
1706 Feld[x][y] = EL_PLAYER_1;
1712 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713 int jx = player->jx, jy = player->jy;
1715 player->present = TRUE;
1717 player->block_last_field = (element == EL_SP_MURPHY ?
1718 level.sp_block_last_field :
1719 level.block_last_field);
1721 /* ---------- initialize player's last field block delay --------------- */
1723 /* always start with reliable default value (no adjustment needed) */
1724 player->block_delay_adjustment = 0;
1726 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727 if (player->block_last_field && element == EL_SP_MURPHY)
1728 player->block_delay_adjustment = 1;
1730 /* special case 2: in game engines before 3.1.1, blocking was different */
1731 if (game.use_block_last_field_bug)
1732 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734 if (!network.enabled || player->connected_network)
1736 player->active = TRUE;
1738 /* remove potentially duplicate players */
1739 if (StorePlayer[jx][jy] == Feld[x][y])
1740 StorePlayer[jx][jy] = 0;
1742 StorePlayer[x][y] = Feld[x][y];
1744 #if DEBUG_INIT_PLAYER
1747 printf("- player element %d activated", player->element_nr);
1748 printf(" (local player is %d and currently %s)\n",
1749 local_player->element_nr,
1750 local_player->active ? "active" : "not active");
1755 Feld[x][y] = EL_EMPTY;
1757 player->jx = player->last_jx = x;
1758 player->jy = player->last_jy = y;
1763 int player_nr = GET_PLAYER_NR(element);
1764 struct PlayerInfo *player = &stored_player[player_nr];
1766 if (player->active && player->killed)
1767 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1771 static void InitField(int x, int y, boolean init_game)
1773 int element = Feld[x][y];
1782 InitPlayerField(x, y, element, init_game);
1785 case EL_SOKOBAN_FIELD_PLAYER:
1786 element = Feld[x][y] = EL_PLAYER_1;
1787 InitField(x, y, init_game);
1789 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790 InitField(x, y, init_game);
1793 case EL_SOKOBAN_FIELD_EMPTY:
1794 local_player->sokobanfields_still_needed++;
1798 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1816 case EL_SPACESHIP_RIGHT:
1817 case EL_SPACESHIP_UP:
1818 case EL_SPACESHIP_LEFT:
1819 case EL_SPACESHIP_DOWN:
1820 case EL_BD_BUTTERFLY:
1821 case EL_BD_BUTTERFLY_RIGHT:
1822 case EL_BD_BUTTERFLY_UP:
1823 case EL_BD_BUTTERFLY_LEFT:
1824 case EL_BD_BUTTERFLY_DOWN:
1826 case EL_BD_FIREFLY_RIGHT:
1827 case EL_BD_FIREFLY_UP:
1828 case EL_BD_FIREFLY_LEFT:
1829 case EL_BD_FIREFLY_DOWN:
1830 case EL_PACMAN_RIGHT:
1832 case EL_PACMAN_LEFT:
1833 case EL_PACMAN_DOWN:
1835 case EL_YAMYAM_LEFT:
1836 case EL_YAMYAM_RIGHT:
1838 case EL_YAMYAM_DOWN:
1839 case EL_DARK_YAMYAM:
1842 case EL_SP_SNIKSNAK:
1843 case EL_SP_ELECTRON:
1852 case EL_AMOEBA_FULL:
1857 case EL_AMOEBA_DROP:
1858 if (y == lev_fieldy - 1)
1860 Feld[x][y] = EL_AMOEBA_GROWING;
1861 Store[x][y] = EL_AMOEBA_WET;
1865 case EL_DYNAMITE_ACTIVE:
1866 case EL_SP_DISK_RED_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871 MovDelay[x][y] = 96;
1874 case EL_EM_DYNAMITE_ACTIVE:
1875 MovDelay[x][y] = 32;
1879 local_player->lights_still_needed++;
1883 local_player->friends_still_needed++;
1888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1891 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1905 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1911 game.belt_dir[belt_nr] = belt_dir;
1912 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914 else /* more than one switch -- set it like the first switch */
1916 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1921 case EL_LIGHT_SWITCH_ACTIVE:
1923 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926 case EL_INVISIBLE_STEELWALL:
1927 case EL_INVISIBLE_WALL:
1928 case EL_INVISIBLE_SAND:
1929 if (game.light_time_left > 0 ||
1930 game.lenses_time_left > 0)
1931 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934 case EL_EMC_MAGIC_BALL:
1935 if (game.ball_state)
1936 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939 case EL_EMC_MAGIC_BALL_SWITCH:
1940 if (game.ball_state)
1941 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944 case EL_TRIGGER_PLAYER:
1945 case EL_TRIGGER_ELEMENT:
1946 case EL_TRIGGER_CE_VALUE:
1947 case EL_TRIGGER_CE_SCORE:
1949 case EL_ANY_ELEMENT:
1950 case EL_CURRENT_CE_VALUE:
1951 case EL_CURRENT_CE_SCORE:
1968 /* reference elements should not be used on the playfield */
1969 Feld[x][y] = EL_EMPTY;
1973 if (IS_CUSTOM_ELEMENT(element))
1975 if (CAN_MOVE(element))
1978 if (!element_info[element].use_last_ce_value || init_game)
1979 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981 else if (IS_GROUP_ELEMENT(element))
1983 Feld[x][y] = GetElementFromGroupElement(element);
1985 InitField(x, y, init_game);
1992 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 InitField(x, y, init_game);
1999 /* not needed to call InitMovDir() -- already done by InitField()! */
2000 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001 CAN_MOVE(Feld[x][y]))
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 int old_element = Feld[x][y];
2009 InitField(x, y, init_game);
2011 /* not needed to call InitMovDir() -- already done by InitField()! */
2012 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013 CAN_MOVE(old_element) &&
2014 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2017 /* this case is in fact a combination of not less than three bugs:
2018 first, it calls InitMovDir() for elements that can move, although this is
2019 already done by InitField(); then, it checks the element that was at this
2020 field _before_ the call to InitField() (which can change it); lastly, it
2021 was not called for "mole with direction" elements, which were treated as
2022 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2026 static int get_key_element_from_nr(int key_nr)
2028 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030 EL_EM_KEY_1 : EL_KEY_1);
2032 return key_base_element + key_nr;
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2037 return (player->inventory_size > 0 ?
2038 player->inventory_element[player->inventory_size - 1] :
2039 player->inventory_infinite_element != EL_UNDEFINED ?
2040 player->inventory_infinite_element :
2041 player->dynabombs_left > 0 ?
2042 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 /* pos >= 0: get element from bottom of the stack;
2049 pos < 0: get element from top of the stack */
2053 int min_inventory_size = -pos;
2054 int inventory_pos = player->inventory_size - min_inventory_size;
2055 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057 return (player->inventory_size >= min_inventory_size ?
2058 player->inventory_element[inventory_pos] :
2059 player->inventory_infinite_element != EL_UNDEFINED ?
2060 player->inventory_infinite_element :
2061 player->dynabombs_left >= min_dynabombs_left ?
2062 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 int min_dynabombs_left = pos + 1;
2068 int min_inventory_size = pos + 1 - player->dynabombs_left;
2069 int inventory_pos = pos - player->dynabombs_left;
2071 return (player->inventory_infinite_element != EL_UNDEFINED ?
2072 player->inventory_infinite_element :
2073 player->dynabombs_left >= min_dynabombs_left ?
2074 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075 player->inventory_size >= min_inventory_size ?
2076 player->inventory_element[inventory_pos] :
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2087 if (gpo1->sort_priority != gpo2->sort_priority)
2088 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090 compare_result = gpo1->nr - gpo2->nr;
2092 return compare_result;
2095 int getPlayerInventorySize(int player_nr)
2097 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098 return level.native_em_level->ply[player_nr]->dynamite;
2099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100 return level.native_sp_level->game_sp->red_disk_count;
2102 return stored_player[player_nr].inventory_size;
2105 void InitGameControlValues()
2109 for (i = 0; game_panel_controls[i].nr != -1; i++)
2111 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113 struct TextPosInfo *pos = gpc->pos;
2115 int type = gpc->type;
2119 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120 Error(ERR_EXIT, "this should not happen -- please debug");
2123 /* force update of game controls after initialization */
2124 gpc->value = gpc->last_value = -1;
2125 gpc->frame = gpc->last_frame = -1;
2126 gpc->gfx_frame = -1;
2128 /* determine panel value width for later calculation of alignment */
2129 if (type == TYPE_INTEGER || type == TYPE_STRING)
2131 pos->width = pos->size * getFontWidth(pos->font);
2132 pos->height = getFontHeight(pos->font);
2134 else if (type == TYPE_ELEMENT)
2136 pos->width = pos->size;
2137 pos->height = pos->size;
2140 /* fill structure for game panel draw order */
2142 gpo->sort_priority = pos->sort_priority;
2145 /* sort game panel controls according to sort_priority and control number */
2146 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2150 void UpdatePlayfieldElementCount()
2152 boolean use_element_count = FALSE;
2155 /* first check if it is needed at all to calculate playfield element count */
2156 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158 use_element_count = TRUE;
2160 if (!use_element_count)
2163 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164 element_info[i].element_count = 0;
2166 SCAN_PLAYFIELD(x, y)
2168 element_info[Feld[x][y]].element_count++;
2171 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173 if (IS_IN_GROUP(j, i))
2174 element_info[EL_GROUP_START + i].element_count +=
2175 element_info[j].element_count;
2178 void UpdateGameControlValues()
2181 int time = (local_player->LevelSolved ?
2182 local_player->LevelSolved_CountingTime :
2183 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184 level.native_em_level->lev->time :
2185 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186 level.native_sp_level->game_sp->time_played :
2187 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188 game_mm.energy_left :
2189 game.no_time_limit ? TimePlayed : TimeLeft);
2190 int score = (local_player->LevelSolved ?
2191 local_player->LevelSolved_CountingScore :
2192 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193 level.native_em_level->lev->score :
2194 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195 level.native_sp_level->game_sp->score :
2196 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 local_player->score);
2199 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200 level.native_em_level->lev->required :
2201 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202 level.native_sp_level->game_sp->infotrons_still_needed :
2203 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204 game_mm.kettles_still_needed :
2205 local_player->gems_still_needed);
2206 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required > 0 :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211 game_mm.kettles_still_needed > 0 ||
2212 game_mm.lights_still_needed > 0 :
2213 local_player->gems_still_needed > 0 ||
2214 local_player->sokobanfields_still_needed > 0 ||
2215 local_player->lights_still_needed > 0);
2216 int health = (local_player->LevelSolved ?
2217 local_player->LevelSolved_CountingHealth :
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219 MM_HEALTH(game_mm.laser_overload_value) :
2220 local_player->health);
2222 UpdatePlayfieldElementCount();
2224 /* update game panel control values */
2226 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230 for (i = 0; i < MAX_NUM_KEYS; i++)
2231 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235 if (game.centered_player_nr == -1)
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 /* only one player in Supaplex game engine */
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243 for (k = 0; k < MAX_NUM_KEYS; k++)
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247 if (level.native_em_level->ply[i]->keys & (1 << k))
2248 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249 get_key_element_from_nr(k);
2251 else if (stored_player[i].key[k])
2252 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253 get_key_element_from_nr(k);
2256 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257 getPlayerInventorySize(i);
2259 if (stored_player[i].num_white_keys > 0)
2260 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264 stored_player[i].num_white_keys;
2269 int player_nr = game.centered_player_nr;
2271 for (k = 0; k < MAX_NUM_KEYS; k++)
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2279 else if (stored_player[player_nr].key[k])
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 getPlayerInventorySize(player_nr);
2287 if (stored_player[player_nr].num_white_keys > 0)
2288 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291 stored_player[player_nr].num_white_keys;
2294 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297 get_inventory_element_from_pos(local_player, i);
2298 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299 get_inventory_element_from_pos(local_player, -i - 1);
2302 game_panel_controls[GAME_PANEL_SCORE].value = score;
2303 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305 game_panel_controls[GAME_PANEL_TIME].value = time;
2307 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311 if (level.time == 0)
2312 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325 local_player->shield_normal_time_left;
2326 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330 local_player->shield_deadly_time_left;
2332 game_panel_controls[GAME_PANEL_EXIT].value =
2333 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339 EL_EMC_MAGIC_BALL_SWITCH);
2341 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344 game.light_time_left;
2346 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349 game.timegate_time_left;
2351 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357 game.lenses_time_left;
2359 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362 game.magnify_time_left;
2364 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2366 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2368 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2369 EL_BALLOON_SWITCH_NONE);
2371 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372 local_player->dynabomb_count;
2373 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374 local_player->dynabomb_size;
2375 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378 game_panel_controls[GAME_PANEL_PENGUINS].value =
2379 local_player->friends_still_needed;
2381 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382 local_player->sokobanfields_still_needed;
2383 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389 for (i = 0; i < NUM_BELTS; i++)
2391 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401 game.magic_wall_time_left;
2403 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404 local_player->gravity;
2406 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412 game.panel.element[i].id : EL_UNDEFINED);
2414 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417 element_info[game.panel.element_count[i].id].element_count : 0);
2419 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422 element_info[game.panel.ce_score[i].id].collect_score : 0);
2424 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427 element_info[game.panel.ce_score_element[i].id].collect_score :
2430 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434 /* update game panel control frames */
2436 for (i = 0; game_panel_controls[i].nr != -1; i++)
2438 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440 if (gpc->type == TYPE_ELEMENT)
2442 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444 int last_anim_random_frame = gfx.anim_random_frame;
2445 int element = gpc->value;
2446 int graphic = el2panelimg(element);
2448 if (gpc->value != gpc->last_value)
2451 gpc->gfx_random = INIT_GFX_RANDOM();
2457 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459 gpc->gfx_random = INIT_GFX_RANDOM();
2462 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463 gfx.anim_random_frame = gpc->gfx_random;
2465 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466 gpc->gfx_frame = element_info[element].collect_score;
2468 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472 gfx.anim_random_frame = last_anim_random_frame;
2475 else if (gpc->type == TYPE_GRAPHIC)
2477 if (gpc->graphic != IMG_UNDEFINED)
2479 int last_anim_random_frame = gfx.anim_random_frame;
2480 int graphic = gpc->graphic;
2482 if (gpc->value != gpc->last_value)
2485 gpc->gfx_random = INIT_GFX_RANDOM();
2491 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493 gpc->gfx_random = INIT_GFX_RANDOM();
2496 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497 gfx.anim_random_frame = gpc->gfx_random;
2499 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502 gfx.anim_random_frame = last_anim_random_frame;
2508 void DisplayGameControlValues()
2510 boolean redraw_panel = FALSE;
2513 for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517 if (PANEL_DEACTIVATED(gpc->pos))
2520 if (gpc->value == gpc->last_value &&
2521 gpc->frame == gpc->last_frame)
2524 redraw_panel = TRUE;
2530 /* copy default game door content to main double buffer */
2532 /* !!! CHECK AGAIN !!! */
2533 SetPanelBackground();
2534 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537 /* redraw game control buttons */
2538 RedrawGameButtons();
2540 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544 int nr = game_panel_order[i].nr;
2545 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546 struct TextPosInfo *pos = gpc->pos;
2547 int type = gpc->type;
2548 int value = gpc->value;
2549 int frame = gpc->frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2558 gpc->last_value = value;
2559 gpc->last_frame = frame;
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2581 /* correct text size if "digits" is zero or less */
2583 size = strlen(int2str(value, size));
2585 /* dynamically correct text alignment */
2586 pos->width = size * getFontWidth(font);
2588 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589 int2str(value, size), font, mask_mode);
2591 else if (type == TYPE_ELEMENT)
2593 int element, graphic;
2597 int dst_x = PANEL_XPOS(pos);
2598 int dst_y = PANEL_YPOS(pos);
2600 if (value != EL_UNDEFINED && value != EL_EMPTY)
2603 graphic = el2panelimg(value);
2605 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613 width = graphic_info[graphic].width * size / TILESIZE;
2614 height = graphic_info[graphic].height * size / TILESIZE;
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2624 else if (type == TYPE_GRAPHIC)
2626 int graphic = gpc->graphic;
2627 int graphic_active = gpc->graphic_active;
2631 int dst_x = PANEL_XPOS(pos);
2632 int dst_y = PANEL_YPOS(pos);
2633 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636 if (graphic != IMG_UNDEFINED && !skip)
2638 if (pos->style == STYLE_REVERSE)
2639 value = 100 - value;
2641 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643 if (pos->direction & MV_HORIZONTAL)
2645 width = graphic_info[graphic_active].width * value / 100;
2646 height = graphic_info[graphic_active].height;
2648 if (pos->direction == MV_LEFT)
2650 src_x += graphic_info[graphic_active].width - width;
2651 dst_x += graphic_info[graphic_active].width - width;
2656 width = graphic_info[graphic_active].width;
2657 height = graphic_info[graphic_active].height * value / 100;
2659 if (pos->direction == MV_UP)
2661 src_y += graphic_info[graphic_active].height - height;
2662 dst_y += graphic_info[graphic_active].height - height;
2667 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675 if (pos->direction & MV_HORIZONTAL)
2677 if (pos->direction == MV_RIGHT)
2684 dst_x = PANEL_XPOS(pos);
2687 width = graphic_info[graphic].width - width;
2691 if (pos->direction == MV_DOWN)
2698 dst_y = PANEL_YPOS(pos);
2701 height = graphic_info[graphic].height - height;
2705 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2712 else if (type == TYPE_STRING)
2714 boolean active = (value != 0);
2715 char *state_normal = "off";
2716 char *state_active = "on";
2717 char *state = (active ? state_active : state_normal);
2718 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2720 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2721 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2723 if (nr == GAME_PANEL_GRAVITY_STATE)
2725 int font1 = pos->font; /* (used for normal state) */
2726 int font2 = pos->font_alt; /* (used for active state) */
2728 font = (active ? font2 : font1);
2737 /* don't truncate output if "chars" is zero or less */
2740 /* dynamically correct text alignment */
2741 pos->width = size * getFontWidth(font);
2744 s_cut = getStringCopyN(s, size);
2746 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747 s_cut, font, mask_mode);
2753 redraw_mask |= REDRAW_DOOR_1;
2756 SetGameStatus(GAME_MODE_PLAYING);
2759 void UpdateAndDisplayGameControlValues()
2761 if (tape.deactivate_display)
2764 UpdateGameControlValues();
2765 DisplayGameControlValues();
2768 void UpdateGameDoorValues()
2770 UpdateGameControlValues();
2773 void DrawGameDoorValues()
2775 DisplayGameControlValues();
2780 =============================================================================
2782 -----------------------------------------------------------------------------
2783 initialize game engine due to level / tape version number
2784 =============================================================================
2787 static void InitGameEngine()
2789 int i, j, k, l, x, y;
2791 /* set game engine from tape file when re-playing, else from level file */
2792 game.engine_version = (tape.playing ? tape.engine_version :
2793 level.game_version);
2795 /* set single or multi-player game mode (needed for re-playing tapes) */
2796 game.team_mode = setup.team_mode;
2800 int num_players = 0;
2802 for (i = 0; i < MAX_PLAYERS; i++)
2803 if (tape.player_participates[i])
2806 /* multi-player tapes contain input data for more than one player */
2807 game.team_mode = (num_players > 1);
2810 /* ---------------------------------------------------------------------- */
2811 /* set flags for bugs and changes according to active game engine version */
2812 /* ---------------------------------------------------------------------- */
2815 Summary of bugfix/change:
2816 Fixed handling for custom elements that change when pushed by the player.
2818 Fixed/changed in version:
2822 Before 3.1.0, custom elements that "change when pushing" changed directly
2823 after the player started pushing them (until then handled in "DigField()").
2824 Since 3.1.0, these custom elements are not changed until the "pushing"
2825 move of the element is finished (now handled in "ContinueMoving()").
2827 Affected levels/tapes:
2828 The first condition is generally needed for all levels/tapes before version
2829 3.1.0, which might use the old behaviour before it was changed; known tapes
2830 that are affected are some tapes from the level set "Walpurgis Gardens" by
2832 The second condition is an exception from the above case and is needed for
2833 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834 above (including some development versions of 3.1.0), but before it was
2835 known that this change would break tapes like the above and was fixed in
2836 3.1.1, so that the changed behaviour was active although the engine version
2837 while recording maybe was before 3.1.0. There is at least one tape that is
2838 affected by this exception, which is the tape for the one-level set "Bug
2839 Machine" by Juergen Bonhagen.
2842 game.use_change_when_pushing_bug =
2843 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version < VERSION_IDENT(3,1,1,0)));
2849 Summary of bugfix/change:
2850 Fixed handling for blocking the field the player leaves when moving.
2852 Fixed/changed in version:
2856 Before 3.1.1, when "block last field when moving" was enabled, the field
2857 the player is leaving when moving was blocked for the time of the move,
2858 and was directly unblocked afterwards. This resulted in the last field
2859 being blocked for exactly one less than the number of frames of one player
2860 move. Additionally, even when blocking was disabled, the last field was
2861 blocked for exactly one frame.
2862 Since 3.1.1, due to changes in player movement handling, the last field
2863 is not blocked at all when blocking is disabled. When blocking is enabled,
2864 the last field is blocked for exactly the number of frames of one player
2865 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866 last field is blocked for exactly one more than the number of frames of
2869 Affected levels/tapes:
2870 (!!! yet to be determined -- probably many !!!)
2873 game.use_block_last_field_bug =
2874 (game.engine_version < VERSION_IDENT(3,1,1,0));
2876 game_em.use_single_button =
2877 (game.engine_version > VERSION_IDENT(4,0,0,2));
2879 game_em.use_snap_key_bug =
2880 (game.engine_version < VERSION_IDENT(4,0,1,0));
2882 /* ---------------------------------------------------------------------- */
2884 /* set maximal allowed number of custom element changes per game frame */
2885 game.max_num_changes_per_frame = 1;
2887 /* default scan direction: scan playfield from top/left to bottom/right */
2888 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890 /* dynamically adjust element properties according to game engine version */
2891 InitElementPropertiesEngine(game.engine_version);
2894 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895 printf(" tape version == %06d [%s] [file: %06d]\n",
2896 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898 printf(" => game.engine_version == %06d\n", game.engine_version);
2901 /* ---------- initialize player's initial move delay --------------------- */
2903 /* dynamically adjust player properties according to level information */
2904 for (i = 0; i < MAX_PLAYERS; i++)
2905 game.initial_move_delay_value[i] =
2906 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908 /* dynamically adjust player properties according to game engine version */
2909 for (i = 0; i < MAX_PLAYERS; i++)
2910 game.initial_move_delay[i] =
2911 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912 game.initial_move_delay_value[i] : 0);
2914 /* ---------- initialize player's initial push delay --------------------- */
2916 /* dynamically adjust player properties according to game engine version */
2917 game.initial_push_delay_value =
2918 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920 /* ---------- initialize changing elements ------------------------------- */
2922 /* initialize changing elements information */
2923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925 struct ElementInfo *ei = &element_info[i];
2927 /* this pointer might have been changed in the level editor */
2928 ei->change = &ei->change_page[0];
2930 if (!IS_CUSTOM_ELEMENT(i))
2932 ei->change->target_element = EL_EMPTY_SPACE;
2933 ei->change->delay_fixed = 0;
2934 ei->change->delay_random = 0;
2935 ei->change->delay_frames = 1;
2938 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940 ei->has_change_event[j] = FALSE;
2942 ei->event_page_nr[j] = 0;
2943 ei->event_page[j] = &ei->change_page[0];
2947 /* add changing elements from pre-defined list */
2948 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951 struct ElementInfo *ei = &element_info[ch_delay->element];
2953 ei->change->target_element = ch_delay->target_element;
2954 ei->change->delay_fixed = ch_delay->change_delay;
2956 ei->change->pre_change_function = ch_delay->pre_change_function;
2957 ei->change->change_function = ch_delay->change_function;
2958 ei->change->post_change_function = ch_delay->post_change_function;
2960 ei->change->can_change = TRUE;
2961 ei->change->can_change_or_has_action = TRUE;
2963 ei->has_change_event[CE_DELAY] = TRUE;
2965 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969 /* ---------- initialize internal run-time variables --------------------- */
2971 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975 for (j = 0; j < ei->num_change_pages; j++)
2977 ei->change_page[j].can_change_or_has_action =
2978 (ei->change_page[j].can_change |
2979 ei->change_page[j].has_action);
2983 /* add change events from custom element configuration */
2984 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988 for (j = 0; j < ei->num_change_pages; j++)
2990 if (!ei->change_page[j].can_change_or_has_action)
2993 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995 /* only add event page for the first page found with this event */
2996 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998 ei->has_change_event[k] = TRUE;
3000 ei->event_page_nr[k] = j;
3001 ei->event_page[k] = &ei->change_page[j];
3007 /* ---------- initialize reference elements in change conditions --------- */
3009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011 int element = EL_CUSTOM_START + i;
3012 struct ElementInfo *ei = &element_info[element];
3014 for (j = 0; j < ei->num_change_pages; j++)
3016 int trigger_element = ei->change_page[j].initial_trigger_element;
3018 if (trigger_element >= EL_PREV_CE_8 &&
3019 trigger_element <= EL_NEXT_CE_8)
3020 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022 ei->change_page[j].trigger_element = trigger_element;
3026 /* ---------- initialize run-time trigger player and element ------------- */
3028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032 for (j = 0; j < ei->num_change_pages; j++)
3034 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038 ei->change_page[j].actual_trigger_ce_value = 0;
3039 ei->change_page[j].actual_trigger_ce_score = 0;
3043 /* ---------- initialize trigger events ---------------------------------- */
3045 /* initialize trigger events information */
3046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048 trigger_events[i][j] = FALSE;
3050 /* add trigger events from element change event properties */
3051 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053 struct ElementInfo *ei = &element_info[i];
3055 for (j = 0; j < ei->num_change_pages; j++)
3057 if (!ei->change_page[j].can_change_or_has_action)
3060 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062 int trigger_element = ei->change_page[j].trigger_element;
3064 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066 if (ei->change_page[j].has_event[k])
3068 if (IS_GROUP_ELEMENT(trigger_element))
3070 struct ElementGroupInfo *group =
3071 element_info[trigger_element].group;
3073 for (l = 0; l < group->num_elements_resolved; l++)
3074 trigger_events[group->element_resolved[l]][k] = TRUE;
3076 else if (trigger_element == EL_ANY_ELEMENT)
3077 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078 trigger_events[l][k] = TRUE;
3080 trigger_events[trigger_element][k] = TRUE;
3087 /* ---------- initialize push delay -------------------------------------- */
3089 /* initialize push delay values to default */
3090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092 if (!IS_CUSTOM_ELEMENT(i))
3094 /* set default push delay values (corrected since version 3.0.7-1) */
3095 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097 element_info[i].push_delay_fixed = 2;
3098 element_info[i].push_delay_random = 8;
3102 element_info[i].push_delay_fixed = 8;
3103 element_info[i].push_delay_random = 8;
3108 /* set push delay value for certain elements from pre-defined list */
3109 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111 int e = push_delay_list[i].element;
3113 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3114 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117 /* set push delay value for Supaplex elements for newer engine versions */
3118 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122 if (IS_SP_ELEMENT(i))
3124 /* set SP push delay to just enough to push under a falling zonk */
3125 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127 element_info[i].push_delay_fixed = delay;
3128 element_info[i].push_delay_random = 0;
3133 /* ---------- initialize move stepsize ----------------------------------- */
3135 /* initialize move stepsize values to default */
3136 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137 if (!IS_CUSTOM_ELEMENT(i))
3138 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140 /* set move stepsize value for certain elements from pre-defined list */
3141 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143 int e = move_stepsize_list[i].element;
3145 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148 /* ---------- initialize collect score ----------------------------------- */
3150 /* initialize collect score values for custom elements from initial value */
3151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152 if (IS_CUSTOM_ELEMENT(i))
3153 element_info[i].collect_score = element_info[i].collect_score_initial;
3155 /* ---------- initialize collect count ----------------------------------- */
3157 /* initialize collect count values for non-custom elements */
3158 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159 if (!IS_CUSTOM_ELEMENT(i))
3160 element_info[i].collect_count_initial = 0;
3162 /* add collect count values for all elements from pre-defined list */
3163 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164 element_info[collect_count_list[i].element].collect_count_initial =
3165 collect_count_list[i].count;
3167 /* ---------- initialize access direction -------------------------------- */
3169 /* initialize access direction values to default (access from every side) */
3170 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (!IS_CUSTOM_ELEMENT(i))
3172 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174 /* set access direction value for certain elements from pre-defined list */
3175 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176 element_info[access_direction_list[i].element].access_direction =
3177 access_direction_list[i].direction;
3179 /* ---------- initialize explosion content ------------------------------- */
3180 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182 if (IS_CUSTOM_ELEMENT(i))
3185 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189 element_info[i].content.e[x][y] =
3190 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192 i == EL_PLAYER_3 ? EL_EMERALD :
3193 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194 i == EL_MOLE ? EL_EMERALD_RED :
3195 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200 i == EL_WALL_EMERALD ? EL_EMERALD :
3201 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206 i == EL_WALL_PEARL ? EL_PEARL :
3207 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212 /* ---------- initialize recursion detection ------------------------------ */
3213 recursion_loop_depth = 0;
3214 recursion_loop_detected = FALSE;
3215 recursion_loop_element = EL_UNDEFINED;
3217 /* ---------- initialize graphics engine ---------------------------------- */
3218 game.scroll_delay_value =
3219 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220 setup.scroll_delay ? setup.scroll_delay_value : 0);
3221 game.scroll_delay_value =
3222 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224 /* ---------- initialize game engine snapshots ---------------------------- */
3225 for (i = 0; i < MAX_PLAYERS; i++)
3226 game.snapshot.last_action[i] = 0;
3227 game.snapshot.changed_action = FALSE;
3228 game.snapshot.collected_item = FALSE;
3229 game.snapshot.mode =
3230 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231 SNAPSHOT_MODE_EVERY_STEP :
3232 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233 SNAPSHOT_MODE_EVERY_MOVE :
3234 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236 game.snapshot.save_snapshot = FALSE;
3238 /* ---------- initialize level time for Supaplex engine ------------------- */
3239 /* Supaplex levels with time limit currently unsupported -- should be added */
3240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3244 int get_num_special_action(int element, int action_first, int action_last)
3246 int num_special_action = 0;
3249 for (i = action_first; i <= action_last; i++)
3251 boolean found = FALSE;
3253 for (j = 0; j < NUM_DIRECTIONS; j++)
3254 if (el_act_dir2img(element, i, j) !=
3255 el_act_dir2img(element, ACTION_DEFAULT, j))
3259 num_special_action++;
3264 return num_special_action;
3269 =============================================================================
3271 -----------------------------------------------------------------------------
3272 initialize and start new game
3273 =============================================================================
3278 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280 int fade_mask = REDRAW_FIELD;
3282 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3283 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3284 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3285 int initial_move_dir = MV_DOWN;
3288 // required here to update video display before fading (FIX THIS)
3289 DrawMaskedBorder(REDRAW_DOOR_2);
3291 if (!game.restart_level)
3292 CloseDoor(DOOR_CLOSE_1);
3294 SetGameStatus(GAME_MODE_PLAYING);
3296 if (level_editor_test_game)
3297 FadeSkipNextFadeIn();
3299 FadeSetEnterScreen();
3301 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302 fade_mask = REDRAW_ALL;
3304 FadeLevelSoundsAndMusic();
3306 ExpireSoundLoops(TRUE);
3308 if (!level_editor_test_game)
3311 /* needed if different viewport properties defined for playing */
3312 ChangeViewportPropertiesIfNeeded();
3316 DrawCompleteVideoDisplay();
3318 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3321 InitGameControlValues();
3323 /* don't play tapes over network */
3324 network_playing = (network.enabled && !tape.playing);
3326 for (i = 0; i < MAX_PLAYERS; i++)
3328 struct PlayerInfo *player = &stored_player[i];
3330 player->index_nr = i;
3331 player->index_bit = (1 << i);
3332 player->element_nr = EL_PLAYER_1 + i;
3334 player->present = FALSE;
3335 player->active = FALSE;
3336 player->mapped = FALSE;
3338 player->killed = FALSE;
3339 player->reanimated = FALSE;
3342 player->effective_action = 0;
3343 player->programmed_action = 0;
3345 player->mouse_action.lx = 0;
3346 player->mouse_action.ly = 0;
3347 player->mouse_action.button = 0;
3348 player->mouse_action.button_hint = 0;
3350 player->effective_mouse_action.lx = 0;
3351 player->effective_mouse_action.ly = 0;
3352 player->effective_mouse_action.button = 0;
3353 player->effective_mouse_action.button_hint = 0;
3356 player->score_final = 0;
3358 player->health = MAX_HEALTH;
3359 player->health_final = MAX_HEALTH;
3361 player->gems_still_needed = level.gems_needed;
3362 player->sokobanfields_still_needed = 0;
3363 player->lights_still_needed = 0;
3364 player->friends_still_needed = 0;
3366 for (j = 0; j < MAX_NUM_KEYS; j++)
3367 player->key[j] = FALSE;
3369 player->num_white_keys = 0;
3371 player->dynabomb_count = 0;
3372 player->dynabomb_size = 1;
3373 player->dynabombs_left = 0;
3374 player->dynabomb_xl = FALSE;
3376 player->MovDir = initial_move_dir;
3379 player->GfxDir = initial_move_dir;
3380 player->GfxAction = ACTION_DEFAULT;
3382 player->StepFrame = 0;
3384 player->initial_element = player->element_nr;
3385 player->artwork_element =
3386 (level.use_artwork_element[i] ? level.artwork_element[i] :
3387 player->element_nr);
3388 player->use_murphy = FALSE;
3390 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3391 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3393 player->gravity = level.initial_player_gravity[i];
3395 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3397 player->actual_frame_counter = 0;
3399 player->step_counter = 0;
3401 player->last_move_dir = initial_move_dir;
3403 player->is_active = FALSE;
3405 player->is_waiting = FALSE;
3406 player->is_moving = FALSE;
3407 player->is_auto_moving = FALSE;
3408 player->is_digging = FALSE;
3409 player->is_snapping = FALSE;
3410 player->is_collecting = FALSE;
3411 player->is_pushing = FALSE;
3412 player->is_switching = FALSE;
3413 player->is_dropping = FALSE;
3414 player->is_dropping_pressed = FALSE;
3416 player->is_bored = FALSE;
3417 player->is_sleeping = FALSE;
3419 player->was_waiting = TRUE;
3420 player->was_moving = FALSE;
3421 player->was_snapping = FALSE;
3422 player->was_dropping = FALSE;
3424 player->force_dropping = FALSE;
3426 player->frame_counter_bored = -1;
3427 player->frame_counter_sleeping = -1;
3429 player->anim_delay_counter = 0;
3430 player->post_delay_counter = 0;
3432 player->dir_waiting = initial_move_dir;
3433 player->action_waiting = ACTION_DEFAULT;
3434 player->last_action_waiting = ACTION_DEFAULT;
3435 player->special_action_bored = ACTION_DEFAULT;
3436 player->special_action_sleeping = ACTION_DEFAULT;
3438 player->switch_x = -1;
3439 player->switch_y = -1;
3441 player->drop_x = -1;
3442 player->drop_y = -1;
3444 player->show_envelope = 0;
3446 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3448 player->push_delay = -1; /* initialized when pushing starts */
3449 player->push_delay_value = game.initial_push_delay_value;
3451 player->drop_delay = 0;
3452 player->drop_pressed_delay = 0;
3454 player->last_jx = -1;
3455 player->last_jy = -1;
3459 player->shield_normal_time_left = 0;
3460 player->shield_deadly_time_left = 0;
3462 player->inventory_infinite_element = EL_UNDEFINED;
3463 player->inventory_size = 0;
3465 if (level.use_initial_inventory[i])
3467 for (j = 0; j < level.initial_inventory_size[i]; j++)
3469 int element = level.initial_inventory_content[i][j];
3470 int collect_count = element_info[element].collect_count_initial;
3473 if (!IS_CUSTOM_ELEMENT(element))
3476 if (collect_count == 0)
3477 player->inventory_infinite_element = element;
3479 for (k = 0; k < collect_count; k++)
3480 if (player->inventory_size < MAX_INVENTORY_SIZE)
3481 player->inventory_element[player->inventory_size++] = element;
3485 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486 SnapField(player, 0, 0);
3488 player->LevelSolved = FALSE;
3489 player->GameOver = FALSE;
3491 player->LevelSolved_GameWon = FALSE;
3492 player->LevelSolved_GameEnd = FALSE;
3493 player->LevelSolved_PanelOff = FALSE;
3494 player->LevelSolved_SaveTape = FALSE;
3495 player->LevelSolved_SaveScore = FALSE;
3497 player->LevelSolved_CountingTime = 0;
3498 player->LevelSolved_CountingScore = 0;
3499 player->LevelSolved_CountingHealth = 0;
3501 map_player_action[i] = i;
3504 network_player_action_received = FALSE;
3506 /* initial null action */
3507 if (network_playing)
3508 SendToServer_MovePlayer(MV_NONE);
3516 TimeLeft = level.time;
3519 ScreenMovDir = MV_NONE;
3523 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3525 AllPlayersGone = FALSE;
3527 game.no_time_limit = (level.time == 0);
3529 game.yamyam_content_nr = 0;
3530 game.robot_wheel_active = FALSE;
3531 game.magic_wall_active = FALSE;
3532 game.magic_wall_time_left = 0;
3533 game.light_time_left = 0;
3534 game.timegate_time_left = 0;
3535 game.switchgate_pos = 0;
3536 game.wind_direction = level.wind_direction_initial;
3538 game.lenses_time_left = 0;
3539 game.magnify_time_left = 0;
3541 game.ball_state = level.ball_state_initial;
3542 game.ball_content_nr = 0;
3544 game.envelope_active = FALSE;
3546 for (i = 0; i < NUM_BELTS; i++)
3548 game.belt_dir[i] = MV_NONE;
3549 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3552 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3553 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3555 #if DEBUG_INIT_PLAYER
3558 printf("Player status at level initialization:\n");
3560 for (i = 0; i < MAX_PLAYERS; i++)
3562 struct PlayerInfo *player = &stored_player[i];
3564 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3568 player->connected_locally,
3569 player->connected_network,
3572 if (local_player == player)
3573 printf(" (local player)");
3580 SCAN_PLAYFIELD(x, y)
3582 Feld[x][y] = level.field[x][y];
3583 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3584 ChangeDelay[x][y] = 0;
3585 ChangePage[x][y] = -1;
3586 CustomValue[x][y] = 0; /* initialized in InitField() */
3587 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3589 WasJustMoving[x][y] = 0;
3590 WasJustFalling[x][y] = 0;
3591 CheckCollision[x][y] = 0;
3592 CheckImpact[x][y] = 0;
3594 Pushed[x][y] = FALSE;
3596 ChangeCount[x][y] = 0;
3597 ChangeEvent[x][y] = -1;
3599 ExplodePhase[x][y] = 0;
3600 ExplodeDelay[x][y] = 0;
3601 ExplodeField[x][y] = EX_TYPE_NONE;
3603 RunnerVisit[x][y] = 0;
3604 PlayerVisit[x][y] = 0;
3607 GfxRandom[x][y] = INIT_GFX_RANDOM();
3608 GfxElement[x][y] = EL_UNDEFINED;
3609 GfxAction[x][y] = ACTION_DEFAULT;
3610 GfxDir[x][y] = MV_NONE;
3611 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3614 SCAN_PLAYFIELD(x, y)
3616 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3618 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3620 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3623 InitField(x, y, TRUE);
3625 ResetGfxAnimation(x, y);
3630 for (i = 0; i < MAX_PLAYERS; i++)
3632 struct PlayerInfo *player = &stored_player[i];
3634 /* set number of special actions for bored and sleeping animation */
3635 player->num_special_action_bored =
3636 get_num_special_action(player->artwork_element,
3637 ACTION_BORING_1, ACTION_BORING_LAST);
3638 player->num_special_action_sleeping =
3639 get_num_special_action(player->artwork_element,
3640 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3643 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3644 emulate_sb ? EMU_SOKOBAN :
3645 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3647 /* initialize type of slippery elements */
3648 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3650 if (!IS_CUSTOM_ELEMENT(i))
3652 /* default: elements slip down either to the left or right randomly */
3653 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3655 /* SP style elements prefer to slip down on the left side */
3656 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3657 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3659 /* BD style elements prefer to slip down on the left side */
3660 if (game.emulation == EMU_BOULDERDASH)
3661 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3665 /* initialize explosion and ignition delay */
3666 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3668 if (!IS_CUSTOM_ELEMENT(i))
3671 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3672 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3673 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3674 int last_phase = (num_phase + 1) * delay;
3675 int half_phase = (num_phase / 2) * delay;
3677 element_info[i].explosion_delay = last_phase - 1;
3678 element_info[i].ignition_delay = half_phase;
3680 if (i == EL_BLACK_ORB)
3681 element_info[i].ignition_delay = 1;
3685 /* correct non-moving belts to start moving left */
3686 for (i = 0; i < NUM_BELTS; i++)
3687 if (game.belt_dir[i] == MV_NONE)
3688 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3690 #if USE_NEW_PLAYER_ASSIGNMENTS
3691 for (i = 0; i < MAX_PLAYERS; i++)
3693 stored_player[i].connected = FALSE;
3695 /* in network game mode, the local player might not be the first player */
3696 if (stored_player[i].connected_locally)
3697 local_player = &stored_player[i];
3700 if (!network.enabled)
3701 local_player->connected = TRUE;
3705 for (i = 0; i < MAX_PLAYERS; i++)
3706 stored_player[i].connected = tape.player_participates[i];
3708 else if (network.enabled)
3710 /* add team mode players connected over the network (needed for correct
3711 assignment of player figures from level to locally playing players) */
3713 for (i = 0; i < MAX_PLAYERS; i++)
3714 if (stored_player[i].connected_network)
3715 stored_player[i].connected = TRUE;
3717 else if (game.team_mode)
3719 /* try to guess locally connected team mode players (needed for correct
3720 assignment of player figures from level to locally playing players) */
3722 for (i = 0; i < MAX_PLAYERS; i++)
3723 if (setup.input[i].use_joystick ||
3724 setup.input[i].key.left != KSYM_UNDEFINED)
3725 stored_player[i].connected = TRUE;
3728 #if DEBUG_INIT_PLAYER
3731 printf("Player status after level initialization:\n");
3733 for (i = 0; i < MAX_PLAYERS; i++)
3735 struct PlayerInfo *player = &stored_player[i];
3737 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3741 player->connected_locally,
3742 player->connected_network,
3745 if (local_player == player)
3746 printf(" (local player)");
3753 #if DEBUG_INIT_PLAYER
3755 printf("Reassigning players ...\n");
3758 /* check if any connected player was not found in playfield */
3759 for (i = 0; i < MAX_PLAYERS; i++)
3761 struct PlayerInfo *player = &stored_player[i];
3763 if (player->connected && !player->present)
3765 struct PlayerInfo *field_player = NULL;
3767 #if DEBUG_INIT_PLAYER
3769 printf("- looking for field player for player %d ...\n", i + 1);
3772 /* assign first free player found that is present in the playfield */
3774 /* first try: look for unmapped playfield player that is not connected */
3775 for (j = 0; j < MAX_PLAYERS; j++)
3776 if (field_player == NULL &&
3777 stored_player[j].present &&
3778 !stored_player[j].mapped &&
3779 !stored_player[j].connected)
3780 field_player = &stored_player[j];
3782 /* second try: look for *any* unmapped playfield player */
3783 for (j = 0; j < MAX_PLAYERS; j++)
3784 if (field_player == NULL &&
3785 stored_player[j].present &&
3786 !stored_player[j].mapped)
3787 field_player = &stored_player[j];
3789 if (field_player != NULL)
3791 int jx = field_player->jx, jy = field_player->jy;
3793 #if DEBUG_INIT_PLAYER
3795 printf("- found player %d\n", field_player->index_nr + 1);
3798 player->present = FALSE;
3799 player->active = FALSE;
3801 field_player->present = TRUE;
3802 field_player->active = TRUE;
3805 player->initial_element = field_player->initial_element;
3806 player->artwork_element = field_player->artwork_element;
3808 player->block_last_field = field_player->block_last_field;
3809 player->block_delay_adjustment = field_player->block_delay_adjustment;
3812 StorePlayer[jx][jy] = field_player->element_nr;
3814 field_player->jx = field_player->last_jx = jx;
3815 field_player->jy = field_player->last_jy = jy;
3817 if (local_player == player)
3818 local_player = field_player;
3820 map_player_action[field_player->index_nr] = i;
3822 field_player->mapped = TRUE;
3824 #if DEBUG_INIT_PLAYER
3826 printf("- map_player_action[%d] == %d\n",
3827 field_player->index_nr + 1, i + 1);
3832 if (player->connected && player->present)
3833 player->mapped = TRUE;
3836 #if DEBUG_INIT_PLAYER
3839 printf("Player status after player assignment (first stage):\n");
3841 for (i = 0; i < MAX_PLAYERS; i++)
3843 struct PlayerInfo *player = &stored_player[i];
3845 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3849 player->connected_locally,
3850 player->connected_network,
3853 if (local_player == player)
3854 printf(" (local player)");
3863 /* check if any connected player was not found in playfield */
3864 for (i = 0; i < MAX_PLAYERS; i++)
3866 struct PlayerInfo *player = &stored_player[i];
3868 if (player->connected && !player->present)
3870 for (j = 0; j < MAX_PLAYERS; j++)
3872 struct PlayerInfo *field_player = &stored_player[j];
3873 int jx = field_player->jx, jy = field_player->jy;
3875 /* assign first free player found that is present in the playfield */
3876 if (field_player->present && !field_player->connected)
3878 player->present = TRUE;
3879 player->active = TRUE;
3881 field_player->present = FALSE;
3882 field_player->active = FALSE;
3884 player->initial_element = field_player->initial_element;
3885 player->artwork_element = field_player->artwork_element;
3887 player->block_last_field = field_player->block_last_field;
3888 player->block_delay_adjustment = field_player->block_delay_adjustment;
3890 StorePlayer[jx][jy] = player->element_nr;
3892 player->jx = player->last_jx = jx;
3893 player->jy = player->last_jy = jy;
3903 printf("::: local_player->present == %d\n", local_player->present);
3906 /* set focus to local player for network games, else to all players */
3907 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908 game.centered_player_nr_next = game.centered_player_nr;
3909 game.set_centered_player = FALSE;
3911 if (network_playing && tape.recording)
3913 /* store client dependent player focus when recording network games */
3914 tape.centered_player_nr_next = game.centered_player_nr_next;
3915 tape.set_centered_player = TRUE;
3920 /* when playing a tape, eliminate all players who do not participate */
3922 #if USE_NEW_PLAYER_ASSIGNMENTS
3924 if (!game.team_mode)
3926 for (i = 0; i < MAX_PLAYERS; i++)
3928 if (stored_player[i].active &&
3929 !tape.player_participates[map_player_action[i]])
3931 struct PlayerInfo *player = &stored_player[i];
3932 int jx = player->jx, jy = player->jy;
3934 #if DEBUG_INIT_PLAYER
3936 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3939 player->active = FALSE;
3940 StorePlayer[jx][jy] = 0;
3941 Feld[jx][jy] = EL_EMPTY;
3948 for (i = 0; i < MAX_PLAYERS; i++)
3950 if (stored_player[i].active &&
3951 !tape.player_participates[i])
3953 struct PlayerInfo *player = &stored_player[i];
3954 int jx = player->jx, jy = player->jy;
3956 player->active = FALSE;
3957 StorePlayer[jx][jy] = 0;
3958 Feld[jx][jy] = EL_EMPTY;
3963 else if (!network.enabled && !game.team_mode) /* && !tape.playing */
3965 /* when in single player mode, eliminate all but the first active player */
3967 for (i = 0; i < MAX_PLAYERS; i++)
3969 if (stored_player[i].active)
3971 for (j = i + 1; j < MAX_PLAYERS; j++)
3973 if (stored_player[j].active)
3975 struct PlayerInfo *player = &stored_player[j];
3976 int jx = player->jx, jy = player->jy;
3978 player->active = FALSE;
3979 player->present = FALSE;
3981 StorePlayer[jx][jy] = 0;
3982 Feld[jx][jy] = EL_EMPTY;
3989 /* when recording the game, store which players take part in the game */
3992 #if USE_NEW_PLAYER_ASSIGNMENTS
3993 for (i = 0; i < MAX_PLAYERS; i++)
3994 if (stored_player[i].connected)
3995 tape.player_participates[i] = TRUE;
3997 for (i = 0; i < MAX_PLAYERS; i++)
3998 if (stored_player[i].active)
3999 tape.player_participates[i] = TRUE;
4003 #if DEBUG_INIT_PLAYER
4006 printf("Player status after player assignment (final stage):\n");
4008 for (i = 0; i < MAX_PLAYERS; i++)
4010 struct PlayerInfo *player = &stored_player[i];
4012 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4016 player->connected_locally,
4017 player->connected_network,
4020 if (local_player == player)
4021 printf(" (local player)");
4028 if (BorderElement == EL_EMPTY)
4031 SBX_Right = lev_fieldx - SCR_FIELDX;
4033 SBY_Lower = lev_fieldy - SCR_FIELDY;
4038 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4040 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4043 if (full_lev_fieldx <= SCR_FIELDX)
4044 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4045 if (full_lev_fieldy <= SCR_FIELDY)
4046 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4048 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4050 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4053 /* if local player not found, look for custom element that might create
4054 the player (make some assumptions about the right custom element) */
4055 if (!local_player->present)
4057 int start_x = 0, start_y = 0;
4058 int found_rating = 0;
4059 int found_element = EL_UNDEFINED;
4060 int player_nr = local_player->index_nr;
4062 SCAN_PLAYFIELD(x, y)
4064 int element = Feld[x][y];
4069 if (level.use_start_element[player_nr] &&
4070 level.start_element[player_nr] == element &&
4077 found_element = element;
4080 if (!IS_CUSTOM_ELEMENT(element))
4083 if (CAN_CHANGE(element))
4085 for (i = 0; i < element_info[element].num_change_pages; i++)
4087 /* check for player created from custom element as single target */
4088 content = element_info[element].change_page[i].target_element;
4089 is_player = ELEM_IS_PLAYER(content);
4091 if (is_player && (found_rating < 3 ||
4092 (found_rating == 3 && element < found_element)))
4098 found_element = element;
4103 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4105 /* check for player created from custom element as explosion content */
4106 content = element_info[element].content.e[xx][yy];
4107 is_player = ELEM_IS_PLAYER(content);
4109 if (is_player && (found_rating < 2 ||
4110 (found_rating == 2 && element < found_element)))
4112 start_x = x + xx - 1;
4113 start_y = y + yy - 1;
4116 found_element = element;
4119 if (!CAN_CHANGE(element))
4122 for (i = 0; i < element_info[element].num_change_pages; i++)
4124 /* check for player created from custom element as extended target */
4126 element_info[element].change_page[i].target_content.e[xx][yy];
4128 is_player = ELEM_IS_PLAYER(content);
4130 if (is_player && (found_rating < 1 ||
4131 (found_rating == 1 && element < found_element)))
4133 start_x = x + xx - 1;
4134 start_y = y + yy - 1;
4137 found_element = element;
4143 scroll_x = SCROLL_POSITION_X(start_x);
4144 scroll_y = SCROLL_POSITION_Y(start_y);
4148 scroll_x = SCROLL_POSITION_X(local_player->jx);
4149 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4152 /* !!! FIX THIS (START) !!! */
4153 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4155 InitGameEngine_EM();
4157 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4159 InitGameEngine_SP();
4161 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4163 InitGameEngine_MM();
4167 DrawLevel(REDRAW_FIELD);
4170 /* after drawing the level, correct some elements */
4171 if (game.timegate_time_left == 0)
4172 CloseAllOpenTimegates();
4175 /* blit playfield from scroll buffer to normal back buffer for fading in */
4176 BlitScreenToBitmap(backbuffer);
4177 /* !!! FIX THIS (END) !!! */
4179 DrawMaskedBorder(fade_mask);
4184 // full screen redraw is required at this point in the following cases:
4185 // - special editor door undrawn when game was started from level editor
4186 // - drawing area (playfield) was changed and has to be removed completely
4187 redraw_mask = REDRAW_ALL;
4191 if (!game.restart_level)
4193 /* copy default game door content to main double buffer */
4195 /* !!! CHECK AGAIN !!! */
4196 SetPanelBackground();
4197 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4198 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4201 SetPanelBackground();
4202 SetDrawBackgroundMask(REDRAW_DOOR_1);
4204 UpdateAndDisplayGameControlValues();
4206 if (!game.restart_level)
4212 CreateGameButtons();
4217 /* copy actual game door content to door double buffer for OpenDoor() */
4218 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4220 OpenDoor(DOOR_OPEN_ALL);
4222 KeyboardAutoRepeatOffUnlessAutoplay();
4224 #if DEBUG_INIT_PLAYER
4227 printf("Player status (final):\n");
4229 for (i = 0; i < MAX_PLAYERS; i++)
4231 struct PlayerInfo *player = &stored_player[i];
4233 printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4237 player->connected_locally,
4238 player->connected_network,
4241 if (local_player == player)
4242 printf(" (local player)");
4255 if (!game.restart_level && !tape.playing)
4257 LevelStats_incPlayed(level_nr);
4259 SaveLevelSetup_SeriesInfo();
4262 game.restart_level = FALSE;
4263 game.restart_game_message = NULL;
4265 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4266 InitGameActions_MM();
4268 SaveEngineSnapshotToListInitial();
4270 if (!game.restart_level)
4272 PlaySound(SND_GAME_STARTING);
4274 if (setup.sound_music)
4279 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4280 int actual_player_x, int actual_player_y)
4282 /* this is used for non-R'n'D game engines to update certain engine values */
4284 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4286 actual_player_x = correctLevelPosX_EM(actual_player_x);
4287 actual_player_y = correctLevelPosY_EM(actual_player_y);
4290 /* needed to determine if sounds are played within the visible screen area */
4291 scroll_x = actual_scroll_x;
4292 scroll_y = actual_scroll_y;
4294 /* needed to get player position for "follow finger" playing input method */
4295 local_player->jx = actual_player_x;
4296 local_player->jy = actual_player_y;
4299 void InitMovDir(int x, int y)
4301 int i, element = Feld[x][y];
4302 static int xy[4][2] =
4309 static int direction[3][4] =
4311 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4312 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4313 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4322 Feld[x][y] = EL_BUG;
4323 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4326 case EL_SPACESHIP_RIGHT:
4327 case EL_SPACESHIP_UP:
4328 case EL_SPACESHIP_LEFT:
4329 case EL_SPACESHIP_DOWN:
4330 Feld[x][y] = EL_SPACESHIP;
4331 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4334 case EL_BD_BUTTERFLY_RIGHT:
4335 case EL_BD_BUTTERFLY_UP:
4336 case EL_BD_BUTTERFLY_LEFT:
4337 case EL_BD_BUTTERFLY_DOWN:
4338 Feld[x][y] = EL_BD_BUTTERFLY;
4339 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4342 case EL_BD_FIREFLY_RIGHT:
4343 case EL_BD_FIREFLY_UP:
4344 case EL_BD_FIREFLY_LEFT:
4345 case EL_BD_FIREFLY_DOWN:
4346 Feld[x][y] = EL_BD_FIREFLY;
4347 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4350 case EL_PACMAN_RIGHT:
4352 case EL_PACMAN_LEFT:
4353 case EL_PACMAN_DOWN:
4354 Feld[x][y] = EL_PACMAN;
4355 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4358 case EL_YAMYAM_LEFT:
4359 case EL_YAMYAM_RIGHT:
4361 case EL_YAMYAM_DOWN:
4362 Feld[x][y] = EL_YAMYAM;
4363 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4366 case EL_SP_SNIKSNAK:
4367 MovDir[x][y] = MV_UP;
4370 case EL_SP_ELECTRON:
4371 MovDir[x][y] = MV_LEFT;
4378 Feld[x][y] = EL_MOLE;
4379 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4383 if (IS_CUSTOM_ELEMENT(element))
4385 struct ElementInfo *ei = &element_info[element];
4386 int move_direction_initial = ei->move_direction_initial;
4387 int move_pattern = ei->move_pattern;
4389 if (move_direction_initial == MV_START_PREVIOUS)
4391 if (MovDir[x][y] != MV_NONE)
4394 move_direction_initial = MV_START_AUTOMATIC;
4397 if (move_direction_initial == MV_START_RANDOM)
4398 MovDir[x][y] = 1 << RND(4);
4399 else if (move_direction_initial & MV_ANY_DIRECTION)
4400 MovDir[x][y] = move_direction_initial;
4401 else if (move_pattern == MV_ALL_DIRECTIONS ||
4402 move_pattern == MV_TURNING_LEFT ||
4403 move_pattern == MV_TURNING_RIGHT ||
4404 move_pattern == MV_TURNING_LEFT_RIGHT ||
4405 move_pattern == MV_TURNING_RIGHT_LEFT ||
4406 move_pattern == MV_TURNING_RANDOM)
4407 MovDir[x][y] = 1 << RND(4);
4408 else if (move_pattern == MV_HORIZONTAL)
4409 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4410 else if (move_pattern == MV_VERTICAL)
4411 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4412 else if (move_pattern & MV_ANY_DIRECTION)
4413 MovDir[x][y] = element_info[element].move_pattern;
4414 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4415 move_pattern == MV_ALONG_RIGHT_SIDE)
4417 /* use random direction as default start direction */
4418 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4419 MovDir[x][y] = 1 << RND(4);
4421 for (i = 0; i < NUM_DIRECTIONS; i++)
4423 int x1 = x + xy[i][0];
4424 int y1 = y + xy[i][1];
4426 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4428 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4429 MovDir[x][y] = direction[0][i];
4431 MovDir[x][y] = direction[1][i];
4440 MovDir[x][y] = 1 << RND(4);
4442 if (element != EL_BUG &&
4443 element != EL_SPACESHIP &&
4444 element != EL_BD_BUTTERFLY &&
4445 element != EL_BD_FIREFLY)
4448 for (i = 0; i < NUM_DIRECTIONS; i++)
4450 int x1 = x + xy[i][0];
4451 int y1 = y + xy[i][1];
4453 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4455 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4457 MovDir[x][y] = direction[0][i];
4460 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4461 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4463 MovDir[x][y] = direction[1][i];
4472 GfxDir[x][y] = MovDir[x][y];
4475 void InitAmoebaNr(int x, int y)
4478 int group_nr = AmoebeNachbarNr(x, y);
4482 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4484 if (AmoebaCnt[i] == 0)
4492 AmoebaNr[x][y] = group_nr;
4493 AmoebaCnt[group_nr]++;
4494 AmoebaCnt2[group_nr]++;
4497 static void PlayerWins(struct PlayerInfo *player)
4499 player->LevelSolved = TRUE;
4500 player->GameOver = TRUE;
4502 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4503 level.native_em_level->lev->score :
4504 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4507 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4508 MM_HEALTH(game_mm.laser_overload_value) :
4511 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4513 player->LevelSolved_CountingScore = player->score_final;
4514 player->LevelSolved_CountingHealth = player->health_final;
4519 static int time_count_steps;
4520 static int time, time_final;
4521 static int score, score_final;
4522 static int health, health_final;
4523 static int game_over_delay_1 = 0;
4524 static int game_over_delay_2 = 0;
4525 static int game_over_delay_3 = 0;
4526 int game_over_delay_value_1 = 50;
4527 int game_over_delay_value_2 = 25;
4528 int game_over_delay_value_3 = 50;
4530 if (!local_player->LevelSolved_GameWon)
4534 /* do not start end game actions before the player stops moving (to exit) */
4535 if (local_player->MovPos)
4538 local_player->LevelSolved_GameWon = TRUE;
4539 local_player->LevelSolved_SaveTape = tape.recording;
4540 local_player->LevelSolved_SaveScore = !tape.playing;
4544 LevelStats_incSolved(level_nr);
4546 SaveLevelSetup_SeriesInfo();
4549 if (tape.auto_play) /* tape might already be stopped here */
4550 tape.auto_play_level_solved = TRUE;
4554 game_over_delay_1 = 0;
4555 game_over_delay_2 = 0;
4556 game_over_delay_3 = game_over_delay_value_3;
4558 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4559 score = score_final = local_player->score_final;
4560 health = health_final = local_player->health_final;
4562 if (level.score[SC_TIME_BONUS] > 0)
4567 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4569 else if (game.no_time_limit && TimePlayed < 999)
4572 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4575 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4577 game_over_delay_1 = game_over_delay_value_1;
4579 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4582 score_final += health * level.score[SC_TIME_BONUS];
4584 game_over_delay_2 = game_over_delay_value_2;
4587 local_player->score_final = score_final;
4588 local_player->health_final = health_final;
4591 if (level_editor_test_game)
4594 score = score_final;
4596 local_player->LevelSolved_CountingTime = time;
4597 local_player->LevelSolved_CountingScore = score;
4599 game_panel_controls[GAME_PANEL_TIME].value = time;
4600 game_panel_controls[GAME_PANEL_SCORE].value = score;
4602 DisplayGameControlValues();
4605 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4607 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4609 /* close exit door after last player */
4610 if ((AllPlayersGone &&
4611 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4612 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4613 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4614 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4615 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4617 int element = Feld[ExitX][ExitY];
4619 Feld[ExitX][ExitY] =
4620 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4621 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4622 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4623 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4624 EL_EM_STEEL_EXIT_CLOSING);
4626 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4629 /* player disappears */
4630 DrawLevelField(ExitX, ExitY);
4633 for (i = 0; i < MAX_PLAYERS; i++)
4635 struct PlayerInfo *player = &stored_player[i];
4637 if (player->present)
4639 RemovePlayer(player);
4641 /* player disappears */
4642 DrawLevelField(player->jx, player->jy);
4647 PlaySound(SND_GAME_WINNING);
4650 if (game_over_delay_1 > 0)
4652 game_over_delay_1--;
4657 if (time != time_final)
4659 int time_to_go = ABS(time_final - time);
4660 int time_count_dir = (time < time_final ? +1 : -1);
4662 if (time_to_go < time_count_steps)
4663 time_count_steps = 1;
4665 time += time_count_steps * time_count_dir;
4666 score += time_count_steps * level.score[SC_TIME_BONUS];
4668 local_player->LevelSolved_CountingTime = time;
4669 local_player->LevelSolved_CountingScore = score;
4671 game_panel_controls[GAME_PANEL_TIME].value = time;
4672 game_panel_controls[GAME_PANEL_SCORE].value = score;
4674 DisplayGameControlValues();
4676 if (time == time_final)
4677 StopSound(SND_GAME_LEVELTIME_BONUS);
4678 else if (setup.sound_loops)
4679 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4681 PlaySound(SND_GAME_LEVELTIME_BONUS);
4686 if (game_over_delay_2 > 0)
4688 game_over_delay_2--;
4693 if (health != health_final)
4695 int health_count_dir = (health < health_final ? +1 : -1);
4697 health += health_count_dir;
4698 score += level.score[SC_TIME_BONUS];
4700 local_player->LevelSolved_CountingHealth = health;
4701 local_player->LevelSolved_CountingScore = score;
4703 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4704 game_panel_controls[GAME_PANEL_SCORE].value = score;
4706 DisplayGameControlValues();
4708 if (health == health_final)
4709 StopSound(SND_GAME_LEVELTIME_BONUS);
4710 else if (setup.sound_loops)
4711 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4713 PlaySound(SND_GAME_LEVELTIME_BONUS);
4718 local_player->LevelSolved_PanelOff = TRUE;
4720 if (game_over_delay_3 > 0)
4722 game_over_delay_3--;
4733 boolean raise_level = FALSE;
4735 local_player->LevelSolved_GameEnd = TRUE;
4737 if (local_player->LevelSolved_SaveTape)
4739 /* make sure that request dialog to save tape does not open door again */
4740 if (!global.use_envelope_request)
4741 CloseDoor(DOOR_CLOSE_1);
4743 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4746 /* if no tape is to be saved, close both doors simultaneously */
4747 CloseDoor(DOOR_CLOSE_ALL);
4749 if (level_editor_test_game)
4751 SetGameStatus(GAME_MODE_MAIN);
4758 if (!local_player->LevelSolved_SaveScore)
4760 SetGameStatus(GAME_MODE_MAIN);
4767 if (level_nr == leveldir_current->handicap_level)
4769 leveldir_current->handicap_level++;
4771 SaveLevelSetup_SeriesInfo();
4774 if (setup.increment_levels &&
4775 level_nr < leveldir_current->last_level)
4776 raise_level = TRUE; /* advance to next level */
4778 if ((hi_pos = NewHiScore()) >= 0)
4780 SetGameStatus(GAME_MODE_SCORES);
4782 DrawHallOfFame(hi_pos);
4792 SetGameStatus(GAME_MODE_MAIN);
4808 boolean one_score_entry_per_name = !program.many_scores_per_name;
4810 LoadScore(level_nr);
4812 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4813 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4816 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4818 if (local_player->score_final > highscore[k].Score)
4820 /* player has made it to the hall of fame */
4822 if (k < MAX_SCORE_ENTRIES - 1)
4824 int m = MAX_SCORE_ENTRIES - 1;
4826 if (one_score_entry_per_name)
4828 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4829 if (strEqual(setup.player_name, highscore[l].Name))
4832 if (m == k) /* player's new highscore overwrites his old one */
4836 for (l = m; l > k; l--)
4838 strcpy(highscore[l].Name, highscore[l - 1].Name);
4839 highscore[l].Score = highscore[l - 1].Score;
4845 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4846 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4847 highscore[k].Score = local_player->score_final;
4852 else if (one_score_entry_per_name &&
4853 !strncmp(setup.player_name, highscore[k].Name,
4854 MAX_PLAYER_NAME_LEN))
4855 break; /* player already there with a higher score */
4859 SaveScore(level_nr);
4864 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4866 int element = Feld[x][y];
4867 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4868 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4869 int horiz_move = (dx != 0);
4870 int sign = (horiz_move ? dx : dy);
4871 int step = sign * element_info[element].move_stepsize;
4873 /* special values for move stepsize for spring and things on conveyor belt */
4876 if (CAN_FALL(element) &&
4877 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4878 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4879 else if (element == EL_SPRING)
4880 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4886 inline static int getElementMoveStepsize(int x, int y)
4888 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4891 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4893 if (player->GfxAction != action || player->GfxDir != dir)
4895 player->GfxAction = action;
4896 player->GfxDir = dir;
4898 player->StepFrame = 0;
4902 static void ResetGfxFrame(int x, int y)
4904 // profiling showed that "autotest" spends 10~20% of its time in this function
4905 if (DrawingDeactivatedField())
4908 int element = Feld[x][y];
4909 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4911 if (graphic_info[graphic].anim_global_sync)
4912 GfxFrame[x][y] = FrameCounter;
4913 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4914 GfxFrame[x][y] = CustomValue[x][y];
4915 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4916 GfxFrame[x][y] = element_info[element].collect_score;
4917 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4918 GfxFrame[x][y] = ChangeDelay[x][y];
4921 static void ResetGfxAnimation(int x, int y)
4923 GfxAction[x][y] = ACTION_DEFAULT;
4924 GfxDir[x][y] = MovDir[x][y];
4927 ResetGfxFrame(x, y);
4930 static void ResetRandomAnimationValue(int x, int y)
4932 GfxRandom[x][y] = INIT_GFX_RANDOM();
4935 void InitMovingField(int x, int y, int direction)
4937 int element = Feld[x][y];
4938 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4939 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4942 boolean is_moving_before, is_moving_after;
4944 /* check if element was/is moving or being moved before/after mode change */
4945 is_moving_before = (WasJustMoving[x][y] != 0);
4946 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4948 /* reset animation only for moving elements which change direction of moving
4949 or which just started or stopped moving
4950 (else CEs with property "can move" / "not moving" are reset each frame) */
4951 if (is_moving_before != is_moving_after ||
4952 direction != MovDir[x][y])
4953 ResetGfxAnimation(x, y);
4955 MovDir[x][y] = direction;
4956 GfxDir[x][y] = direction;
4958 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4959 direction == MV_DOWN && CAN_FALL(element) ?
4960 ACTION_FALLING : ACTION_MOVING);
4962 /* this is needed for CEs with property "can move" / "not moving" */
4964 if (is_moving_after)
4966 if (Feld[newx][newy] == EL_EMPTY)
4967 Feld[newx][newy] = EL_BLOCKED;
4969 MovDir[newx][newy] = MovDir[x][y];
4971 CustomValue[newx][newy] = CustomValue[x][y];
4973 GfxFrame[newx][newy] = GfxFrame[x][y];
4974 GfxRandom[newx][newy] = GfxRandom[x][y];
4975 GfxAction[newx][newy] = GfxAction[x][y];
4976 GfxDir[newx][newy] = GfxDir[x][y];
4980 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4982 int direction = MovDir[x][y];
4983 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4984 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4990 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4992 int oldx = x, oldy = y;
4993 int direction = MovDir[x][y];
4995 if (direction == MV_LEFT)
4997 else if (direction == MV_RIGHT)
4999 else if (direction == MV_UP)
5001 else if (direction == MV_DOWN)
5004 *comes_from_x = oldx;
5005 *comes_from_y = oldy;
5008 int MovingOrBlocked2Element(int x, int y)
5010 int element = Feld[x][y];
5012 if (element == EL_BLOCKED)
5016 Blocked2Moving(x, y, &oldx, &oldy);
5017 return Feld[oldx][oldy];
5023 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5025 /* like MovingOrBlocked2Element(), but if element is moving
5026 and (x,y) is the field the moving element is just leaving,
5027 return EL_BLOCKED instead of the element value */
5028 int element = Feld[x][y];
5030 if (IS_MOVING(x, y))
5032 if (element == EL_BLOCKED)
5036 Blocked2Moving(x, y, &oldx, &oldy);
5037 return Feld[oldx][oldy];
5046 static void RemoveField(int x, int y)
5048 Feld[x][y] = EL_EMPTY;
5054 CustomValue[x][y] = 0;
5057 ChangeDelay[x][y] = 0;
5058 ChangePage[x][y] = -1;
5059 Pushed[x][y] = FALSE;
5061 GfxElement[x][y] = EL_UNDEFINED;
5062 GfxAction[x][y] = ACTION_DEFAULT;
5063 GfxDir[x][y] = MV_NONE;
5066 void RemoveMovingField(int x, int y)
5068 int oldx = x, oldy = y, newx = x, newy = y;
5069 int element = Feld[x][y];
5070 int next_element = EL_UNDEFINED;
5072 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5075 if (IS_MOVING(x, y))
5077 Moving2Blocked(x, y, &newx, &newy);
5079 if (Feld[newx][newy] != EL_BLOCKED)
5081 /* element is moving, but target field is not free (blocked), but
5082 already occupied by something different (example: acid pool);
5083 in this case, only remove the moving field, but not the target */
5085 RemoveField(oldx, oldy);
5087 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5089 TEST_DrawLevelField(oldx, oldy);
5094 else if (element == EL_BLOCKED)
5096 Blocked2Moving(x, y, &oldx, &oldy);
5097 if (!IS_MOVING(oldx, oldy))
5101 if (element == EL_BLOCKED &&
5102 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5103 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5104 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5105 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5106 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5107 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5108 next_element = get_next_element(Feld[oldx][oldy]);
5110 RemoveField(oldx, oldy);
5111 RemoveField(newx, newy);
5113 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5115 if (next_element != EL_UNDEFINED)
5116 Feld[oldx][oldy] = next_element;
5118 TEST_DrawLevelField(oldx, oldy);
5119 TEST_DrawLevelField(newx, newy);
5122 void DrawDynamite(int x, int y)
5124 int sx = SCREENX(x), sy = SCREENY(y);
5125 int graphic = el2img(Feld[x][y]);
5128 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5131 if (IS_WALKABLE_INSIDE(Back[x][y]))
5135 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5136 else if (Store[x][y])
5137 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5139 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5141 if (Back[x][y] || Store[x][y])
5142 DrawGraphicThruMask(sx, sy, graphic, frame);
5144 DrawGraphic(sx, sy, graphic, frame);
5147 void CheckDynamite(int x, int y)
5149 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5153 if (MovDelay[x][y] != 0)
5156 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5162 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5167 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5169 boolean num_checked_players = 0;
5172 for (i = 0; i < MAX_PLAYERS; i++)
5174 if (stored_player[i].active)
5176 int sx = stored_player[i].jx;
5177 int sy = stored_player[i].jy;
5179 if (num_checked_players == 0)
5186 *sx1 = MIN(*sx1, sx);
5187 *sy1 = MIN(*sy1, sy);
5188 *sx2 = MAX(*sx2, sx);
5189 *sy2 = MAX(*sy2, sy);
5192 num_checked_players++;
5197 static boolean checkIfAllPlayersFitToScreen_RND()
5199 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5201 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5203 return (sx2 - sx1 < SCR_FIELDX &&
5204 sy2 - sy1 < SCR_FIELDY);
5207 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5209 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5211 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5213 *sx = (sx1 + sx2) / 2;
5214 *sy = (sy1 + sy2) / 2;
5217 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5218 boolean center_screen, boolean quick_relocation)
5220 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5221 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5222 boolean no_delay = (tape.warp_forward);
5223 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5224 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5225 int new_scroll_x, new_scroll_y;
5227 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5229 /* case 1: quick relocation inside visible screen (without scrolling) */
5236 if (!level.shifted_relocation || center_screen)
5238 /* relocation _with_ centering of screen */
5240 new_scroll_x = SCROLL_POSITION_X(x);
5241 new_scroll_y = SCROLL_POSITION_Y(y);
5245 /* relocation _without_ centering of screen */
5247 int center_scroll_x = SCROLL_POSITION_X(old_x);
5248 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5249 int offset_x = x + (scroll_x - center_scroll_x);
5250 int offset_y = y + (scroll_y - center_scroll_y);
5252 /* for new screen position, apply previous offset to center position */
5253 new_scroll_x = SCROLL_POSITION_X(offset_x);
5254 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5257 if (quick_relocation)
5259 /* case 2: quick relocation (redraw without visible scrolling) */
5261 scroll_x = new_scroll_x;
5262 scroll_y = new_scroll_y;
5269 /* case 3: visible relocation (with scrolling to new position) */
5271 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5273 SetVideoFrameDelay(wait_delay_value);
5275 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5278 int fx = FX, fy = FY;
5280 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5281 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5283 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5289 fx += dx * TILEX / 2;
5290 fy += dy * TILEY / 2;
5292 ScrollLevel(dx, dy);
5295 /* scroll in two steps of half tile size to make things smoother */
5296 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5298 /* scroll second step to align at full tile size */
5299 BlitScreenToBitmap(window);
5305 SetVideoFrameDelay(frame_delay_value_old);
5308 void RelocatePlayer(int jx, int jy, int el_player_raw)
5310 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5311 int player_nr = GET_PLAYER_NR(el_player);
5312 struct PlayerInfo *player = &stored_player[player_nr];
5313 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5314 boolean no_delay = (tape.warp_forward);
5315 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5316 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5317 int old_jx = player->jx;
5318 int old_jy = player->jy;
5319 int old_element = Feld[old_jx][old_jy];
5320 int element = Feld[jx][jy];
5321 boolean player_relocated = (old_jx != jx || old_jy != jy);
5323 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5324 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5325 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5326 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5327 int leave_side_horiz = move_dir_horiz;
5328 int leave_side_vert = move_dir_vert;
5329 int enter_side = enter_side_horiz | enter_side_vert;
5330 int leave_side = leave_side_horiz | leave_side_vert;
5332 if (player->GameOver) /* do not reanimate dead player */
5335 if (!player_relocated) /* no need to relocate the player */
5338 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5340 RemoveField(jx, jy); /* temporarily remove newly placed player */
5341 DrawLevelField(jx, jy);
5344 if (player->present)
5346 while (player->MovPos)
5348 ScrollPlayer(player, SCROLL_GO_ON);
5349 ScrollScreen(NULL, SCROLL_GO_ON);
5351 AdvanceFrameAndPlayerCounters(player->index_nr);
5355 BackToFront_WithFrameDelay(wait_delay_value);
5358 DrawPlayer(player); /* needed here only to cleanup last field */
5359 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5361 player->is_moving = FALSE;
5364 if (IS_CUSTOM_ELEMENT(old_element))
5365 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5367 player->index_bit, leave_side);
5369 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5371 player->index_bit, leave_side);
5373 Feld[jx][jy] = el_player;
5374 InitPlayerField(jx, jy, el_player, TRUE);
5376 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5377 possible that the relocation target field did not contain a player element,
5378 but a walkable element, to which the new player was relocated -- in this
5379 case, restore that (already initialized!) element on the player field */
5380 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5382 Feld[jx][jy] = element; /* restore previously existing element */
5385 /* only visually relocate centered player */
5386 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5387 FALSE, level.instant_relocation);
5389 TestIfPlayerTouchesBadThing(jx, jy);
5390 TestIfPlayerTouchesCustomElement(jx, jy);
5392 if (IS_CUSTOM_ELEMENT(element))
5393 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5394 player->index_bit, enter_side);
5396 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5397 player->index_bit, enter_side);
5399 if (player->is_switching)
5401 /* ensure that relocation while still switching an element does not cause
5402 a new element to be treated as also switched directly after relocation
5403 (this is important for teleporter switches that teleport the player to
5404 a place where another teleporter switch is in the same direction, which
5405 would then incorrectly be treated as immediately switched before the
5406 direction key that caused the switch was released) */
5408 player->switch_x += jx - old_jx;
5409 player->switch_y += jy - old_jy;
5413 void Explode(int ex, int ey, int phase, int mode)
5419 /* !!! eliminate this variable !!! */
5420 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5422 if (game.explosions_delayed)
5424 ExplodeField[ex][ey] = mode;
5428 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5430 int center_element = Feld[ex][ey];
5431 int artwork_element, explosion_element; /* set these values later */
5433 /* remove things displayed in background while burning dynamite */
5434 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5437 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5439 /* put moving element to center field (and let it explode there) */
5440 center_element = MovingOrBlocked2Element(ex, ey);
5441 RemoveMovingField(ex, ey);
5442 Feld[ex][ey] = center_element;
5445 /* now "center_element" is finally determined -- set related values now */
5446 artwork_element = center_element; /* for custom player artwork */
5447 explosion_element = center_element; /* for custom player artwork */
5449 if (IS_PLAYER(ex, ey))
5451 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5453 artwork_element = stored_player[player_nr].artwork_element;
5455 if (level.use_explosion_element[player_nr])
5457 explosion_element = level.explosion_element[player_nr];
5458 artwork_element = explosion_element;
5462 if (mode == EX_TYPE_NORMAL ||
5463 mode == EX_TYPE_CENTER ||
5464 mode == EX_TYPE_CROSS)
5465 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5467 last_phase = element_info[explosion_element].explosion_delay + 1;
5469 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5471 int xx = x - ex + 1;
5472 int yy = y - ey + 1;
5475 if (!IN_LEV_FIELD(x, y) ||
5476 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5477 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5480 element = Feld[x][y];
5482 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5484 element = MovingOrBlocked2Element(x, y);
5486 if (!IS_EXPLOSION_PROOF(element))
5487 RemoveMovingField(x, y);
5490 /* indestructible elements can only explode in center (but not flames) */
5491 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5492 mode == EX_TYPE_BORDER)) ||
5493 element == EL_FLAMES)
5496 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5497 behaviour, for example when touching a yamyam that explodes to rocks
5498 with active deadly shield, a rock is created under the player !!! */
5499 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5501 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5502 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5503 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5505 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5508 if (IS_ACTIVE_BOMB(element))
5510 /* re-activate things under the bomb like gate or penguin */
5511 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5518 /* save walkable background elements while explosion on same tile */
5519 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5520 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5521 Back[x][y] = element;
5523 /* ignite explodable elements reached by other explosion */
5524 if (element == EL_EXPLOSION)
5525 element = Store2[x][y];
5527 if (AmoebaNr[x][y] &&
5528 (element == EL_AMOEBA_FULL ||
5529 element == EL_BD_AMOEBA ||
5530 element == EL_AMOEBA_GROWING))
5532 AmoebaCnt[AmoebaNr[x][y]]--;
5533 AmoebaCnt2[AmoebaNr[x][y]]--;
5538 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5540 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5542 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5544 if (PLAYERINFO(ex, ey)->use_murphy)
5545 Store[x][y] = EL_EMPTY;
5548 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5549 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5550 else if (ELEM_IS_PLAYER(center_element))
5551 Store[x][y] = EL_EMPTY;
5552 else if (center_element == EL_YAMYAM)
5553 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5554 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5555 Store[x][y] = element_info[center_element].content.e[xx][yy];
5557 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5558 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5559 otherwise) -- FIX THIS !!! */
5560 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5561 Store[x][y] = element_info[element].content.e[1][1];
5563 else if (!CAN_EXPLODE(element))
5564 Store[x][y] = element_info[element].content.e[1][1];
5567 Store[x][y] = EL_EMPTY;
5569 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5570 center_element == EL_AMOEBA_TO_DIAMOND)
5571 Store2[x][y] = element;
5573 Feld[x][y] = EL_EXPLOSION;
5574 GfxElement[x][y] = artwork_element;
5576 ExplodePhase[x][y] = 1;
5577 ExplodeDelay[x][y] = last_phase;
5582 if (center_element == EL_YAMYAM)
5583 game.yamyam_content_nr =
5584 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5596 GfxFrame[x][y] = 0; /* restart explosion animation */
5598 last_phase = ExplodeDelay[x][y];
5600 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5602 /* this can happen if the player leaves an explosion just in time */
5603 if (GfxElement[x][y] == EL_UNDEFINED)
5604 GfxElement[x][y] = EL_EMPTY;
5606 border_element = Store2[x][y];
5607 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5608 border_element = StorePlayer[x][y];
5610 if (phase == element_info[border_element].ignition_delay ||
5611 phase == last_phase)
5613 boolean border_explosion = FALSE;
5615 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5616 !PLAYER_EXPLOSION_PROTECTED(x, y))
5618 KillPlayerUnlessExplosionProtected(x, y);
5619 border_explosion = TRUE;
5621 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5623 Feld[x][y] = Store2[x][y];
5626 border_explosion = TRUE;
5628 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5630 AmoebeUmwandeln(x, y);
5632 border_explosion = TRUE;
5635 /* if an element just explodes due to another explosion (chain-reaction),
5636 do not immediately end the new explosion when it was the last frame of
5637 the explosion (as it would be done in the following "if"-statement!) */
5638 if (border_explosion && phase == last_phase)
5642 if (phase == last_phase)
5646 element = Feld[x][y] = Store[x][y];
5647 Store[x][y] = Store2[x][y] = 0;
5648 GfxElement[x][y] = EL_UNDEFINED;
5650 /* player can escape from explosions and might therefore be still alive */
5651 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5652 element <= EL_PLAYER_IS_EXPLODING_4)
5654 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5655 int explosion_element = EL_PLAYER_1 + player_nr;
5656 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5657 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5659 if (level.use_explosion_element[player_nr])
5660 explosion_element = level.explosion_element[player_nr];
5662 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5663 element_info[explosion_element].content.e[xx][yy]);
5666 /* restore probably existing indestructible background element */
5667 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5668 element = Feld[x][y] = Back[x][y];
5671 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5672 GfxDir[x][y] = MV_NONE;
5673 ChangeDelay[x][y] = 0;
5674 ChangePage[x][y] = -1;
5676 CustomValue[x][y] = 0;
5678 InitField_WithBug2(x, y, FALSE);
5680 TEST_DrawLevelField(x, y);
5682 TestIfElementTouchesCustomElement(x, y);
5684 if (GFX_CRUMBLED(element))
5685 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5687 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5688 StorePlayer[x][y] = 0;
5690 if (ELEM_IS_PLAYER(element))
5691 RelocatePlayer(x, y, element);
5693 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5695 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5696 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5699 TEST_DrawLevelFieldCrumbled(x, y);
5701 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5703 DrawLevelElement(x, y, Back[x][y]);
5704 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5706 else if (IS_WALKABLE_UNDER(Back[x][y]))
5708 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5709 DrawLevelElementThruMask(x, y, Back[x][y]);
5711 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5712 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5716 void DynaExplode(int ex, int ey)
5719 int dynabomb_element = Feld[ex][ey];
5720 int dynabomb_size = 1;
5721 boolean dynabomb_xl = FALSE;
5722 struct PlayerInfo *player;
5723 static int xy[4][2] =
5731 if (IS_ACTIVE_BOMB(dynabomb_element))
5733 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5734 dynabomb_size = player->dynabomb_size;
5735 dynabomb_xl = player->dynabomb_xl;
5736 player->dynabombs_left++;
5739 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5741 for (i = 0; i < NUM_DIRECTIONS; i++)
5743 for (j = 1; j <= dynabomb_size; j++)
5745 int x = ex + j * xy[i][0];
5746 int y = ey + j * xy[i][1];
5749 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5752 element = Feld[x][y];
5754 /* do not restart explosions of fields with active bombs */
5755 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5758 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5760 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5761 !IS_DIGGABLE(element) && !dynabomb_xl)
5767 void Bang(int x, int y)
5769 int element = MovingOrBlocked2Element(x, y);
5770 int explosion_type = EX_TYPE_NORMAL;
5772 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5774 struct PlayerInfo *player = PLAYERINFO(x, y);
5776 element = Feld[x][y] = player->initial_element;
5778 if (level.use_explosion_element[player->index_nr])
5780 int explosion_element = level.explosion_element[player->index_nr];
5782 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5783 explosion_type = EX_TYPE_CROSS;
5784 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5785 explosion_type = EX_TYPE_CENTER;
5793 case EL_BD_BUTTERFLY:
5796 case EL_DARK_YAMYAM:
5800 RaiseScoreElement(element);
5803 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5804 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5805 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5806 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5807 case EL_DYNABOMB_INCREASE_NUMBER:
5808 case EL_DYNABOMB_INCREASE_SIZE:
5809 case EL_DYNABOMB_INCREASE_POWER:
5810 explosion_type = EX_TYPE_DYNA;
5813 case EL_DC_LANDMINE:
5814 explosion_type = EX_TYPE_CENTER;
5819 case EL_LAMP_ACTIVE:
5820 case EL_AMOEBA_TO_DIAMOND:
5821 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5822 explosion_type = EX_TYPE_CENTER;
5826 if (element_info[element].explosion_type == EXPLODES_CROSS)
5827 explosion_type = EX_TYPE_CROSS;
5828 else if (element_info[element].explosion_type == EXPLODES_1X1)
5829 explosion_type = EX_TYPE_CENTER;
5833 if (explosion_type == EX_TYPE_DYNA)
5836 Explode(x, y, EX_PHASE_START, explosion_type);
5838 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5841 void SplashAcid(int x, int y)
5843 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5844 (!IN_LEV_FIELD(x - 1, y - 2) ||
5845 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5846 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5848 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5849 (!IN_LEV_FIELD(x + 1, y - 2) ||
5850 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5851 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5853 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5856 static void InitBeltMovement()
5858 static int belt_base_element[4] =
5860 EL_CONVEYOR_BELT_1_LEFT,
5861 EL_CONVEYOR_BELT_2_LEFT,
5862 EL_CONVEYOR_BELT_3_LEFT,
5863 EL_CONVEYOR_BELT_4_LEFT
5865 static int belt_base_active_element[4] =
5867 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5868 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5869 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5870 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5875 /* set frame order for belt animation graphic according to belt direction */
5876 for (i = 0; i < NUM_BELTS; i++)
5880 for (j = 0; j < NUM_BELT_PARTS; j++)
5882 int element = belt_base_active_element[belt_nr] + j;
5883 int graphic_1 = el2img(element);
5884 int graphic_2 = el2panelimg(element);
5886 if (game.belt_dir[i] == MV_LEFT)
5888 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5889 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5893 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5894 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5899 SCAN_PLAYFIELD(x, y)
5901 int element = Feld[x][y];
5903 for (i = 0; i < NUM_BELTS; i++)
5905 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5907 int e_belt_nr = getBeltNrFromBeltElement(element);
5910 if (e_belt_nr == belt_nr)
5912 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5914 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5921 static void ToggleBeltSwitch(int x, int y)
5923 static int belt_base_element[4] =
5925 EL_CONVEYOR_BELT_1_LEFT,
5926 EL_CONVEYOR_BELT_2_LEFT,
5927 EL_CONVEYOR_BELT_3_LEFT,
5928 EL_CONVEYOR_BELT_4_LEFT
5930 static int belt_base_active_element[4] =
5932 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5933 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5934 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5935 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5937 static int belt_base_switch_element[4] =
5939 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5940 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5941 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5942 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5944 static int belt_move_dir[4] =
5952 int element = Feld[x][y];
5953 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5954 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5955 int belt_dir = belt_move_dir[belt_dir_nr];
5958 if (!IS_BELT_SWITCH(element))
5961 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5962 game.belt_dir[belt_nr] = belt_dir;
5964 if (belt_dir_nr == 3)
5967 /* set frame order for belt animation graphic according to belt direction */
5968 for (i = 0; i < NUM_BELT_PARTS; i++)
5970 int element = belt_base_active_element[belt_nr] + i;
5971 int graphic_1 = el2img(element);
5972 int graphic_2 = el2panelimg(element);
5974 if (belt_dir == MV_LEFT)
5976 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5977 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5981 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5982 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5986 SCAN_PLAYFIELD(xx, yy)
5988 int element = Feld[xx][yy];
5990 if (IS_BELT_SWITCH(element))
5992 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5994 if (e_belt_nr == belt_nr)
5996 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5997 TEST_DrawLevelField(xx, yy);
6000 else if (IS_BELT(element) && belt_dir != MV_NONE)
6002 int e_belt_nr = getBeltNrFromBeltElement(element);
6004 if (e_belt_nr == belt_nr)
6006 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6008 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6009 TEST_DrawLevelField(xx, yy);
6012 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6014 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6016 if (e_belt_nr == belt_nr)
6018 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6020 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6021 TEST_DrawLevelField(xx, yy);
6027 static void ToggleSwitchgateSwitch(int x, int y)
6031 game.switchgate_pos = !game.switchgate_pos;
6033 SCAN_PLAYFIELD(xx, yy)
6035 int element = Feld[xx][yy];
6037 if (element == EL_SWITCHGATE_SWITCH_UP)
6039 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6040 TEST_DrawLevelField(xx, yy);
6042 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6044 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6045 TEST_DrawLevelField(xx, yy);
6047 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6049 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6050 TEST_DrawLevelField(xx, yy);
6052 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6054 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6055 TEST_DrawLevelField(xx, yy);
6057 else if (element == EL_SWITCHGATE_OPEN ||
6058 element == EL_SWITCHGATE_OPENING)
6060 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6062 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6064 else if (element == EL_SWITCHGATE_CLOSED ||
6065 element == EL_SWITCHGATE_CLOSING)
6067 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6069 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6074 static int getInvisibleActiveFromInvisibleElement(int element)
6076 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6077 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6078 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6082 static int getInvisibleFromInvisibleActiveElement(int element)
6084 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6085 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6086 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6090 static void RedrawAllLightSwitchesAndInvisibleElements()
6094 SCAN_PLAYFIELD(x, y)
6096 int element = Feld[x][y];
6098 if (element == EL_LIGHT_SWITCH &&
6099 game.light_time_left > 0)
6101 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6102 TEST_DrawLevelField(x, y);
6104 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6105 game.light_time_left == 0)
6107 Feld[x][y] = EL_LIGHT_SWITCH;
6108 TEST_DrawLevelField(x, y);
6110 else if (element == EL_EMC_DRIPPER &&
6111 game.light_time_left > 0)
6113 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6114 TEST_DrawLevelField(x, y);
6116 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6117 game.light_time_left == 0)
6119 Feld[x][y] = EL_EMC_DRIPPER;
6120 TEST_DrawLevelField(x, y);
6122 else if (element == EL_INVISIBLE_STEELWALL ||
6123 element == EL_INVISIBLE_WALL ||
6124 element == EL_INVISIBLE_SAND)
6126 if (game.light_time_left > 0)
6127 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6129 TEST_DrawLevelField(x, y);
6131 /* uncrumble neighbour fields, if needed */
6132 if (element == EL_INVISIBLE_SAND)
6133 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6135 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6136 element == EL_INVISIBLE_WALL_ACTIVE ||
6137 element == EL_INVISIBLE_SAND_ACTIVE)
6139 if (game.light_time_left == 0)
6140 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6142 TEST_DrawLevelField(x, y);
6144 /* re-crumble neighbour fields, if needed */
6145 if (element == EL_INVISIBLE_SAND)
6146 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6151 static void RedrawAllInvisibleElementsForLenses()
6155 SCAN_PLAYFIELD(x, y)
6157 int element = Feld[x][y];
6159 if (element == EL_EMC_DRIPPER &&
6160 game.lenses_time_left > 0)
6162 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6163 TEST_DrawLevelField(x, y);
6165 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6166 game.lenses_time_left == 0)
6168 Feld[x][y] = EL_EMC_DRIPPER;
6169 TEST_DrawLevelField(x, y);
6171 else if (element == EL_INVISIBLE_STEELWALL ||
6172 element == EL_INVISIBLE_WALL ||
6173 element == EL_INVISIBLE_SAND)
6175 if (game.lenses_time_left > 0)
6176 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6178 TEST_DrawLevelField(x, y);
6180 /* uncrumble neighbour fields, if needed */
6181 if (element == EL_INVISIBLE_SAND)
6182 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6184 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6185 element == EL_INVISIBLE_WALL_ACTIVE ||
6186 element == EL_INVISIBLE_SAND_ACTIVE)
6188 if (game.lenses_time_left == 0)
6189 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6191 TEST_DrawLevelField(x, y);
6193 /* re-crumble neighbour fields, if needed */
6194 if (element == EL_INVISIBLE_SAND)
6195 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6200 static void RedrawAllInvisibleElementsForMagnifier()
6204 SCAN_PLAYFIELD(x, y)
6206 int element = Feld[x][y];
6208 if (element == EL_EMC_FAKE_GRASS &&
6209 game.magnify_time_left > 0)
6211 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6212 TEST_DrawLevelField(x, y);
6214 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6215 game.magnify_time_left == 0)
6217 Feld[x][y] = EL_EMC_FAKE_GRASS;
6218 TEST_DrawLevelField(x, y);
6220 else if (IS_GATE_GRAY(element) &&
6221 game.magnify_time_left > 0)
6223 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6224 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6225 IS_EM_GATE_GRAY(element) ?
6226 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6227 IS_EMC_GATE_GRAY(element) ?
6228 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6229 IS_DC_GATE_GRAY(element) ?
6230 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6232 TEST_DrawLevelField(x, y);
6234 else if (IS_GATE_GRAY_ACTIVE(element) &&
6235 game.magnify_time_left == 0)
6237 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6238 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6239 IS_EM_GATE_GRAY_ACTIVE(element) ?
6240 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6241 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6242 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6243 IS_DC_GATE_GRAY_ACTIVE(element) ?
6244 EL_DC_GATE_WHITE_GRAY :
6246 TEST_DrawLevelField(x, y);
6251 static void ToggleLightSwitch(int x, int y)
6253 int element = Feld[x][y];
6255 game.light_time_left =
6256 (element == EL_LIGHT_SWITCH ?
6257 level.time_light * FRAMES_PER_SECOND : 0);
6259 RedrawAllLightSwitchesAndInvisibleElements();
6262 static void ActivateTimegateSwitch(int x, int y)
6266 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6268 SCAN_PLAYFIELD(xx, yy)
6270 int element = Feld[xx][yy];
6272 if (element == EL_TIMEGATE_CLOSED ||
6273 element == EL_TIMEGATE_CLOSING)
6275 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6276 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6280 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6282 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6283 TEST_DrawLevelField(xx, yy);
6289 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6290 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6293 void Impact(int x, int y)
6295 boolean last_line = (y == lev_fieldy - 1);
6296 boolean object_hit = FALSE;
6297 boolean impact = (last_line || object_hit);
6298 int element = Feld[x][y];
6299 int smashed = EL_STEELWALL;
6301 if (!last_line) /* check if element below was hit */
6303 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6306 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6307 MovDir[x][y + 1] != MV_DOWN ||
6308 MovPos[x][y + 1] <= TILEY / 2));
6310 /* do not smash moving elements that left the smashed field in time */
6311 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6312 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6315 #if USE_QUICKSAND_IMPACT_BUGFIX
6316 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6318 RemoveMovingField(x, y + 1);
6319 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6320 Feld[x][y + 2] = EL_ROCK;
6321 TEST_DrawLevelField(x, y + 2);
6326 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6328 RemoveMovingField(x, y + 1);
6329 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6330 Feld[x][y + 2] = EL_ROCK;
6331 TEST_DrawLevelField(x, y + 2);
6338 smashed = MovingOrBlocked2Element(x, y + 1);
6340 impact = (last_line || object_hit);
6343 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6345 SplashAcid(x, y + 1);
6349 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6350 /* only reset graphic animation if graphic really changes after impact */
6352 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6354 ResetGfxAnimation(x, y);
6355 TEST_DrawLevelField(x, y);
6358 if (impact && CAN_EXPLODE_IMPACT(element))
6363 else if (impact && element == EL_PEARL &&
6364 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6366 ResetGfxAnimation(x, y);
6368 Feld[x][y] = EL_PEARL_BREAKING;
6369 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6372 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6374 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6379 if (impact && element == EL_AMOEBA_DROP)
6381 if (object_hit && IS_PLAYER(x, y + 1))
6382 KillPlayerUnlessEnemyProtected(x, y + 1);
6383 else if (object_hit && smashed == EL_PENGUIN)
6387 Feld[x][y] = EL_AMOEBA_GROWING;
6388 Store[x][y] = EL_AMOEBA_WET;
6390 ResetRandomAnimationValue(x, y);
6395 if (object_hit) /* check which object was hit */
6397 if ((CAN_PASS_MAGIC_WALL(element) &&
6398 (smashed == EL_MAGIC_WALL ||
6399 smashed == EL_BD_MAGIC_WALL)) ||
6400 (CAN_PASS_DC_MAGIC_WALL(element) &&
6401 smashed == EL_DC_MAGIC_WALL))
6404 int activated_magic_wall =
6405 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6406 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6407 EL_DC_MAGIC_WALL_ACTIVE);
6409 /* activate magic wall / mill */
6410 SCAN_PLAYFIELD(xx, yy)
6412 if (Feld[xx][yy] == smashed)
6413 Feld[xx][yy] = activated_magic_wall;
6416 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6417 game.magic_wall_active = TRUE;
6419 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6420 SND_MAGIC_WALL_ACTIVATING :
6421 smashed == EL_BD_MAGIC_WALL ?
6422 SND_BD_MAGIC_WALL_ACTIVATING :
6423 SND_DC_MAGIC_WALL_ACTIVATING));
6426 if (IS_PLAYER(x, y + 1))
6428 if (CAN_SMASH_PLAYER(element))
6430 KillPlayerUnlessEnemyProtected(x, y + 1);
6434 else if (smashed == EL_PENGUIN)
6436 if (CAN_SMASH_PLAYER(element))
6442 else if (element == EL_BD_DIAMOND)
6444 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6450 else if (((element == EL_SP_INFOTRON ||
6451 element == EL_SP_ZONK) &&
6452 (smashed == EL_SP_SNIKSNAK ||
6453 smashed == EL_SP_ELECTRON ||
6454 smashed == EL_SP_DISK_ORANGE)) ||
6455 (element == EL_SP_INFOTRON &&
6456 smashed == EL_SP_DISK_YELLOW))
6461 else if (CAN_SMASH_EVERYTHING(element))
6463 if (IS_CLASSIC_ENEMY(smashed) ||
6464 CAN_EXPLODE_SMASHED(smashed))
6469 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6471 if (smashed == EL_LAMP ||
6472 smashed == EL_LAMP_ACTIVE)
6477 else if (smashed == EL_NUT)
6479 Feld[x][y + 1] = EL_NUT_BREAKING;
6480 PlayLevelSound(x, y, SND_NUT_BREAKING);
6481 RaiseScoreElement(EL_NUT);
6484 else if (smashed == EL_PEARL)
6486 ResetGfxAnimation(x, y);
6488 Feld[x][y + 1] = EL_PEARL_BREAKING;
6489 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6492 else if (smashed == EL_DIAMOND)
6494 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6495 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6498 else if (IS_BELT_SWITCH(smashed))
6500 ToggleBeltSwitch(x, y + 1);
6502 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6503 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6504 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6505 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6507 ToggleSwitchgateSwitch(x, y + 1);
6509 else if (smashed == EL_LIGHT_SWITCH ||
6510 smashed == EL_LIGHT_SWITCH_ACTIVE)
6512 ToggleLightSwitch(x, y + 1);
6516 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6518 CheckElementChangeBySide(x, y + 1, smashed, element,
6519 CE_SWITCHED, CH_SIDE_TOP);
6520 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6526 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6531 /* play sound of magic wall / mill */
6533 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6534 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6535 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6537 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6538 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6539 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6540 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6541 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6542 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6547 /* play sound of object that hits the ground */
6548 if (last_line || object_hit)
6549 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6552 inline static void TurnRoundExt(int x, int y)
6564 { 0, 0 }, { 0, 0 }, { 0, 0 },
6569 int left, right, back;
6573 { MV_DOWN, MV_UP, MV_RIGHT },
6574 { MV_UP, MV_DOWN, MV_LEFT },
6576 { MV_LEFT, MV_RIGHT, MV_DOWN },
6580 { MV_RIGHT, MV_LEFT, MV_UP }
6583 int element = Feld[x][y];
6584 int move_pattern = element_info[element].move_pattern;
6586 int old_move_dir = MovDir[x][y];
6587 int left_dir = turn[old_move_dir].left;
6588 int right_dir = turn[old_move_dir].right;
6589 int back_dir = turn[old_move_dir].back;
6591 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6592 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6593 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6594 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6596 int left_x = x + left_dx, left_y = y + left_dy;
6597 int right_x = x + right_dx, right_y = y + right_dy;
6598 int move_x = x + move_dx, move_y = y + move_dy;
6602 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6604 TestIfBadThingTouchesOtherBadThing(x, y);
6606 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6607 MovDir[x][y] = right_dir;
6608 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6609 MovDir[x][y] = left_dir;
6611 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6613 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6616 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6618 TestIfBadThingTouchesOtherBadThing(x, y);
6620 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6621 MovDir[x][y] = left_dir;
6622 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6623 MovDir[x][y] = right_dir;
6625 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6627 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6630 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6632 TestIfBadThingTouchesOtherBadThing(x, y);
6634 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6635 MovDir[x][y] = left_dir;
6636 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6637 MovDir[x][y] = right_dir;
6639 if (MovDir[x][y] != old_move_dir)
6642 else if (element == EL_YAMYAM)
6644 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6645 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6647 if (can_turn_left && can_turn_right)
6648 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6649 else if (can_turn_left)
6650 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6651 else if (can_turn_right)
6652 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6654 MovDir[x][y] = back_dir;
6656 MovDelay[x][y] = 16 + 16 * RND(3);
6658 else if (element == EL_DARK_YAMYAM)
6660 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6662 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6665 if (can_turn_left && can_turn_right)
6666 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6667 else if (can_turn_left)
6668 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6669 else if (can_turn_right)
6670 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6672 MovDir[x][y] = back_dir;
6674 MovDelay[x][y] = 16 + 16 * RND(3);
6676 else if (element == EL_PACMAN)
6678 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6679 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6681 if (can_turn_left && can_turn_right)
6682 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6683 else if (can_turn_left)
6684 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6685 else if (can_turn_right)
6686 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6688 MovDir[x][y] = back_dir;
6690 MovDelay[x][y] = 6 + RND(40);
6692 else if (element == EL_PIG)
6694 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6695 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6696 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6697 boolean should_turn_left, should_turn_right, should_move_on;
6699 int rnd = RND(rnd_value);
6701 should_turn_left = (can_turn_left &&
6703 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6704 y + back_dy + left_dy)));
6705 should_turn_right = (can_turn_right &&
6707 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6708 y + back_dy + right_dy)));
6709 should_move_on = (can_move_on &&
6712 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6713 y + move_dy + left_dy) ||
6714 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6715 y + move_dy + right_dy)));
6717 if (should_turn_left || should_turn_right || should_move_on)
6719 if (should_turn_left && should_turn_right && should_move_on)
6720 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6721 rnd < 2 * rnd_value / 3 ? right_dir :
6723 else if (should_turn_left && should_turn_right)
6724 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6725 else if (should_turn_left && should_move_on)
6726 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6727 else if (should_turn_right && should_move_on)
6728 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6729 else if (should_turn_left)
6730 MovDir[x][y] = left_dir;
6731 else if (should_turn_right)
6732 MovDir[x][y] = right_dir;
6733 else if (should_move_on)
6734 MovDir[x][y] = old_move_dir;
6736 else if (can_move_on && rnd > rnd_value / 8)
6737 MovDir[x][y] = old_move_dir;
6738 else if (can_turn_left && can_turn_right)
6739 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6740 else if (can_turn_left && rnd > rnd_value / 8)
6741 MovDir[x][y] = left_dir;
6742 else if (can_turn_right && rnd > rnd_value/8)
6743 MovDir[x][y] = right_dir;
6745 MovDir[x][y] = back_dir;
6747 xx = x + move_xy[MovDir[x][y]].dx;
6748 yy = y + move_xy[MovDir[x][y]].dy;
6750 if (!IN_LEV_FIELD(xx, yy) ||
6751 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6752 MovDir[x][y] = old_move_dir;
6756 else if (element == EL_DRAGON)
6758 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6759 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6760 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6762 int rnd = RND(rnd_value);
6764 if (can_move_on && rnd > rnd_value / 8)
6765 MovDir[x][y] = old_move_dir;
6766 else if (can_turn_left && can_turn_right)
6767 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6768 else if (can_turn_left && rnd > rnd_value / 8)
6769 MovDir[x][y] = left_dir;
6770 else if (can_turn_right && rnd > rnd_value / 8)
6771 MovDir[x][y] = right_dir;
6773 MovDir[x][y] = back_dir;
6775 xx = x + move_xy[MovDir[x][y]].dx;
6776 yy = y + move_xy[MovDir[x][y]].dy;
6778 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6779 MovDir[x][y] = old_move_dir;
6783 else if (element == EL_MOLE)
6785 boolean can_move_on =
6786 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6787 IS_AMOEBOID(Feld[move_x][move_y]) ||
6788 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6791 boolean can_turn_left =
6792 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6793 IS_AMOEBOID(Feld[left_x][left_y])));
6795 boolean can_turn_right =
6796 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6797 IS_AMOEBOID(Feld[right_x][right_y])));
6799 if (can_turn_left && can_turn_right)
6800 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6801 else if (can_turn_left)
6802 MovDir[x][y] = left_dir;
6804 MovDir[x][y] = right_dir;
6807 if (MovDir[x][y] != old_move_dir)
6810 else if (element == EL_BALLOON)
6812 MovDir[x][y] = game.wind_direction;
6815 else if (element == EL_SPRING)
6817 if (MovDir[x][y] & MV_HORIZONTAL)
6819 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6820 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6822 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6823 ResetGfxAnimation(move_x, move_y);
6824 TEST_DrawLevelField(move_x, move_y);
6826 MovDir[x][y] = back_dir;
6828 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6829 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6830 MovDir[x][y] = MV_NONE;
6835 else if (element == EL_ROBOT ||
6836 element == EL_SATELLITE ||
6837 element == EL_PENGUIN ||
6838 element == EL_EMC_ANDROID)
6840 int attr_x = -1, attr_y = -1;
6851 for (i = 0; i < MAX_PLAYERS; i++)
6853 struct PlayerInfo *player = &stored_player[i];
6854 int jx = player->jx, jy = player->jy;
6856 if (!player->active)
6860 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6868 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6869 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6870 game.engine_version < VERSION_IDENT(3,1,0,0)))
6876 if (element == EL_PENGUIN)
6879 static int xy[4][2] =
6887 for (i = 0; i < NUM_DIRECTIONS; i++)
6889 int ex = x + xy[i][0];
6890 int ey = y + xy[i][1];
6892 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6893 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6894 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6895 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6904 MovDir[x][y] = MV_NONE;
6906 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6907 else if (attr_x > x)
6908 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6910 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6911 else if (attr_y > y)
6912 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6914 if (element == EL_ROBOT)
6918 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6919 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6920 Moving2Blocked(x, y, &newx, &newy);
6922 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6923 MovDelay[x][y] = 8 + 8 * !RND(3);
6925 MovDelay[x][y] = 16;
6927 else if (element == EL_PENGUIN)
6933 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6935 boolean first_horiz = RND(2);
6936 int new_move_dir = MovDir[x][y];
6939 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6940 Moving2Blocked(x, y, &newx, &newy);
6942 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6946 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6947 Moving2Blocked(x, y, &newx, &newy);
6949 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6952 MovDir[x][y] = old_move_dir;
6956 else if (element == EL_SATELLITE)
6962 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6964 boolean first_horiz = RND(2);
6965 int new_move_dir = MovDir[x][y];
6968 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6969 Moving2Blocked(x, y, &newx, &newy);
6971 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6975 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6976 Moving2Blocked(x, y, &newx, &newy);
6978 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6981 MovDir[x][y] = old_move_dir;
6985 else if (element == EL_EMC_ANDROID)
6987 static int check_pos[16] =
6989 -1, /* 0 => (invalid) */
6990 7, /* 1 => MV_LEFT */
6991 3, /* 2 => MV_RIGHT */
6992 -1, /* 3 => (invalid) */
6994 0, /* 5 => MV_LEFT | MV_UP */
6995 2, /* 6 => MV_RIGHT | MV_UP */
6996 -1, /* 7 => (invalid) */
6997 5, /* 8 => MV_DOWN */
6998 6, /* 9 => MV_LEFT | MV_DOWN */
6999 4, /* 10 => MV_RIGHT | MV_DOWN */
7000 -1, /* 11 => (invalid) */
7001 -1, /* 12 => (invalid) */
7002 -1, /* 13 => (invalid) */
7003 -1, /* 14 => (invalid) */
7004 -1, /* 15 => (invalid) */
7012 { -1, -1, MV_LEFT | MV_UP },
7014 { +1, -1, MV_RIGHT | MV_UP },
7015 { +1, 0, MV_RIGHT },
7016 { +1, +1, MV_RIGHT | MV_DOWN },
7018 { -1, +1, MV_LEFT | MV_DOWN },
7021 int start_pos, check_order;
7022 boolean can_clone = FALSE;
7025 /* check if there is any free field around current position */
7026 for (i = 0; i < 8; i++)
7028 int newx = x + check_xy[i].dx;
7029 int newy = y + check_xy[i].dy;
7031 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7039 if (can_clone) /* randomly find an element to clone */
7043 start_pos = check_pos[RND(8)];
7044 check_order = (RND(2) ? -1 : +1);
7046 for (i = 0; i < 8; i++)
7048 int pos_raw = start_pos + i * check_order;
7049 int pos = (pos_raw + 8) % 8;
7050 int newx = x + check_xy[pos].dx;
7051 int newy = y + check_xy[pos].dy;
7053 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7055 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7056 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7058 Store[x][y] = Feld[newx][newy];
7067 if (can_clone) /* randomly find a direction to move */
7071 start_pos = check_pos[RND(8)];
7072 check_order = (RND(2) ? -1 : +1);
7074 for (i = 0; i < 8; i++)
7076 int pos_raw = start_pos + i * check_order;
7077 int pos = (pos_raw + 8) % 8;
7078 int newx = x + check_xy[pos].dx;
7079 int newy = y + check_xy[pos].dy;
7080 int new_move_dir = check_xy[pos].dir;
7082 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7084 MovDir[x][y] = new_move_dir;
7085 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7094 if (can_clone) /* cloning and moving successful */
7097 /* cannot clone -- try to move towards player */
7099 start_pos = check_pos[MovDir[x][y] & 0x0f];
7100 check_order = (RND(2) ? -1 : +1);
7102 for (i = 0; i < 3; i++)
7104 /* first check start_pos, then previous/next or (next/previous) pos */
7105 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7106 int pos = (pos_raw + 8) % 8;
7107 int newx = x + check_xy[pos].dx;
7108 int newy = y + check_xy[pos].dy;
7109 int new_move_dir = check_xy[pos].dir;
7111 if (IS_PLAYER(newx, newy))
7114 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7116 MovDir[x][y] = new_move_dir;
7117 MovDelay[x][y] = level.android_move_time * 8 + 1;
7124 else if (move_pattern == MV_TURNING_LEFT ||
7125 move_pattern == MV_TURNING_RIGHT ||
7126 move_pattern == MV_TURNING_LEFT_RIGHT ||
7127 move_pattern == MV_TURNING_RIGHT_LEFT ||
7128 move_pattern == MV_TURNING_RANDOM ||
7129 move_pattern == MV_ALL_DIRECTIONS)
7131 boolean can_turn_left =
7132 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7133 boolean can_turn_right =
7134 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7136 if (element_info[element].move_stepsize == 0) /* "not moving" */
7139 if (move_pattern == MV_TURNING_LEFT)
7140 MovDir[x][y] = left_dir;
7141 else if (move_pattern == MV_TURNING_RIGHT)
7142 MovDir[x][y] = right_dir;
7143 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7144 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7145 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7146 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7147 else if (move_pattern == MV_TURNING_RANDOM)
7148 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7149 can_turn_right && !can_turn_left ? right_dir :
7150 RND(2) ? left_dir : right_dir);
7151 else if (can_turn_left && can_turn_right)
7152 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7153 else if (can_turn_left)
7154 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7155 else if (can_turn_right)
7156 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7158 MovDir[x][y] = back_dir;
7160 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7162 else if (move_pattern == MV_HORIZONTAL ||
7163 move_pattern == MV_VERTICAL)
7165 if (move_pattern & old_move_dir)
7166 MovDir[x][y] = back_dir;
7167 else if (move_pattern == MV_HORIZONTAL)
7168 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7169 else if (move_pattern == MV_VERTICAL)
7170 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7172 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174 else if (move_pattern & MV_ANY_DIRECTION)
7176 MovDir[x][y] = move_pattern;
7177 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7179 else if (move_pattern & MV_WIND_DIRECTION)
7181 MovDir[x][y] = game.wind_direction;
7182 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7184 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7186 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7187 MovDir[x][y] = left_dir;
7188 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7189 MovDir[x][y] = right_dir;
7191 if (MovDir[x][y] != old_move_dir)
7192 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7194 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7196 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7197 MovDir[x][y] = right_dir;
7198 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7199 MovDir[x][y] = left_dir;
7201 if (MovDir[x][y] != old_move_dir)
7202 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7204 else if (move_pattern == MV_TOWARDS_PLAYER ||
7205 move_pattern == MV_AWAY_FROM_PLAYER)
7207 int attr_x = -1, attr_y = -1;
7209 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7220 for (i = 0; i < MAX_PLAYERS; i++)
7222 struct PlayerInfo *player = &stored_player[i];
7223 int jx = player->jx, jy = player->jy;
7225 if (!player->active)
7229 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7237 MovDir[x][y] = MV_NONE;
7239 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7240 else if (attr_x > x)
7241 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7243 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7244 else if (attr_y > y)
7245 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7247 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7249 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7251 boolean first_horiz = RND(2);
7252 int new_move_dir = MovDir[x][y];
7254 if (element_info[element].move_stepsize == 0) /* "not moving" */
7256 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7257 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7263 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7264 Moving2Blocked(x, y, &newx, &newy);
7266 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7270 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7271 Moving2Blocked(x, y, &newx, &newy);
7273 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7276 MovDir[x][y] = old_move_dir;
7279 else if (move_pattern == MV_WHEN_PUSHED ||
7280 move_pattern == MV_WHEN_DROPPED)
7282 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7283 MovDir[x][y] = MV_NONE;
7287 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7289 static int test_xy[7][2] =
7299 static int test_dir[7] =
7309 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7310 int move_preference = -1000000; /* start with very low preference */
7311 int new_move_dir = MV_NONE;
7312 int start_test = RND(4);
7315 for (i = 0; i < NUM_DIRECTIONS; i++)
7317 int move_dir = test_dir[start_test + i];
7318 int move_dir_preference;
7320 xx = x + test_xy[start_test + i][0];
7321 yy = y + test_xy[start_test + i][1];
7323 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7324 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7326 new_move_dir = move_dir;
7331 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7334 move_dir_preference = -1 * RunnerVisit[xx][yy];
7335 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7336 move_dir_preference = PlayerVisit[xx][yy];
7338 if (move_dir_preference > move_preference)
7340 /* prefer field that has not been visited for the longest time */
7341 move_preference = move_dir_preference;
7342 new_move_dir = move_dir;
7344 else if (move_dir_preference == move_preference &&
7345 move_dir == old_move_dir)
7347 /* prefer last direction when all directions are preferred equally */
7348 move_preference = move_dir_preference;
7349 new_move_dir = move_dir;
7353 MovDir[x][y] = new_move_dir;
7354 if (old_move_dir != new_move_dir)
7355 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7359 static void TurnRound(int x, int y)
7361 int direction = MovDir[x][y];
7365 GfxDir[x][y] = MovDir[x][y];
7367 if (direction != MovDir[x][y])
7371 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7373 ResetGfxFrame(x, y);
7376 static boolean JustBeingPushed(int x, int y)
7380 for (i = 0; i < MAX_PLAYERS; i++)
7382 struct PlayerInfo *player = &stored_player[i];
7384 if (player->active && player->is_pushing && player->MovPos)
7386 int next_jx = player->jx + (player->jx - player->last_jx);
7387 int next_jy = player->jy + (player->jy - player->last_jy);
7389 if (x == next_jx && y == next_jy)
7397 void StartMoving(int x, int y)
7399 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7400 int element = Feld[x][y];
7405 if (MovDelay[x][y] == 0)
7406 GfxAction[x][y] = ACTION_DEFAULT;
7408 if (CAN_FALL(element) && y < lev_fieldy - 1)
7410 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7411 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7412 if (JustBeingPushed(x, y))
7415 if (element == EL_QUICKSAND_FULL)
7417 if (IS_FREE(x, y + 1))
7419 InitMovingField(x, y, MV_DOWN);
7420 started_moving = TRUE;
7422 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7423 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7424 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7425 Store[x][y] = EL_ROCK;
7427 Store[x][y] = EL_ROCK;
7430 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7432 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7434 if (!MovDelay[x][y])
7436 MovDelay[x][y] = TILEY + 1;
7438 ResetGfxAnimation(x, y);
7439 ResetGfxAnimation(x, y + 1);
7444 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7445 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7452 Feld[x][y] = EL_QUICKSAND_EMPTY;
7453 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7454 Store[x][y + 1] = Store[x][y];
7457 PlayLevelSoundAction(x, y, ACTION_FILLING);
7459 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7461 if (!MovDelay[x][y])
7463 MovDelay[x][y] = TILEY + 1;
7465 ResetGfxAnimation(x, y);
7466 ResetGfxAnimation(x, y + 1);
7471 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7472 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7479 Feld[x][y] = EL_QUICKSAND_EMPTY;
7480 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7481 Store[x][y + 1] = Store[x][y];
7484 PlayLevelSoundAction(x, y, ACTION_FILLING);
7487 else if (element == EL_QUICKSAND_FAST_FULL)
7489 if (IS_FREE(x, y + 1))
7491 InitMovingField(x, y, MV_DOWN);
7492 started_moving = TRUE;
7494 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7495 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7496 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7497 Store[x][y] = EL_ROCK;
7499 Store[x][y] = EL_ROCK;
7502 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7504 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7506 if (!MovDelay[x][y])
7508 MovDelay[x][y] = TILEY + 1;
7510 ResetGfxAnimation(x, y);
7511 ResetGfxAnimation(x, y + 1);
7516 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7517 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7524 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7525 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7526 Store[x][y + 1] = Store[x][y];
7529 PlayLevelSoundAction(x, y, ACTION_FILLING);
7531 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7533 if (!MovDelay[x][y])
7535 MovDelay[x][y] = TILEY + 1;
7537 ResetGfxAnimation(x, y);
7538 ResetGfxAnimation(x, y + 1);
7543 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7544 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7551 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7552 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7553 Store[x][y + 1] = Store[x][y];
7556 PlayLevelSoundAction(x, y, ACTION_FILLING);
7559 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7560 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7562 InitMovingField(x, y, MV_DOWN);
7563 started_moving = TRUE;
7565 Feld[x][y] = EL_QUICKSAND_FILLING;
7566 Store[x][y] = element;
7568 PlayLevelSoundAction(x, y, ACTION_FILLING);
7570 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7571 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7573 InitMovingField(x, y, MV_DOWN);
7574 started_moving = TRUE;
7576 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7577 Store[x][y] = element;
7579 PlayLevelSoundAction(x, y, ACTION_FILLING);
7581 else if (element == EL_MAGIC_WALL_FULL)
7583 if (IS_FREE(x, y + 1))
7585 InitMovingField(x, y, MV_DOWN);
7586 started_moving = TRUE;
7588 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7589 Store[x][y] = EL_CHANGED(Store[x][y]);
7591 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7593 if (!MovDelay[x][y])
7594 MovDelay[x][y] = TILEY / 4 + 1;
7603 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7604 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7605 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7609 else if (element == EL_BD_MAGIC_WALL_FULL)
7611 if (IS_FREE(x, y + 1))
7613 InitMovingField(x, y, MV_DOWN);
7614 started_moving = TRUE;
7616 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7617 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7619 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7621 if (!MovDelay[x][y])
7622 MovDelay[x][y] = TILEY / 4 + 1;
7631 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7632 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7633 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7637 else if (element == EL_DC_MAGIC_WALL_FULL)
7639 if (IS_FREE(x, y + 1))
7641 InitMovingField(x, y, MV_DOWN);
7642 started_moving = TRUE;
7644 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7645 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7647 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7649 if (!MovDelay[x][y])
7650 MovDelay[x][y] = TILEY / 4 + 1;
7659 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7660 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7661 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7665 else if ((CAN_PASS_MAGIC_WALL(element) &&
7666 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7667 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7668 (CAN_PASS_DC_MAGIC_WALL(element) &&
7669 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7672 InitMovingField(x, y, MV_DOWN);
7673 started_moving = TRUE;
7676 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7677 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7678 EL_DC_MAGIC_WALL_FILLING);
7679 Store[x][y] = element;
7681 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7683 SplashAcid(x, y + 1);
7685 InitMovingField(x, y, MV_DOWN);
7686 started_moving = TRUE;
7688 Store[x][y] = EL_ACID;
7691 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7692 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7693 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7694 CAN_FALL(element) && WasJustFalling[x][y] &&
7695 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7697 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7698 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7699 (Feld[x][y + 1] == EL_BLOCKED)))
7701 /* this is needed for a special case not covered by calling "Impact()"
7702 from "ContinueMoving()": if an element moves to a tile directly below
7703 another element which was just falling on that tile (which was empty
7704 in the previous frame), the falling element above would just stop
7705 instead of smashing the element below (in previous version, the above
7706 element was just checked for "moving" instead of "falling", resulting
7707 in incorrect smashes caused by horizontal movement of the above
7708 element; also, the case of the player being the element to smash was
7709 simply not covered here... :-/ ) */
7711 CheckCollision[x][y] = 0;
7712 CheckImpact[x][y] = 0;
7716 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7718 if (MovDir[x][y] == MV_NONE)
7720 InitMovingField(x, y, MV_DOWN);
7721 started_moving = TRUE;
7724 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7726 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7727 MovDir[x][y] = MV_DOWN;
7729 InitMovingField(x, y, MV_DOWN);
7730 started_moving = TRUE;
7732 else if (element == EL_AMOEBA_DROP)
7734 Feld[x][y] = EL_AMOEBA_GROWING;
7735 Store[x][y] = EL_AMOEBA_WET;
7737 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7738 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7739 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7740 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7742 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7743 (IS_FREE(x - 1, y + 1) ||
7744 Feld[x - 1][y + 1] == EL_ACID));
7745 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7746 (IS_FREE(x + 1, y + 1) ||
7747 Feld[x + 1][y + 1] == EL_ACID));
7748 boolean can_fall_any = (can_fall_left || can_fall_right);
7749 boolean can_fall_both = (can_fall_left && can_fall_right);
7750 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7752 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7754 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7755 can_fall_right = FALSE;
7756 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7757 can_fall_left = FALSE;
7758 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7759 can_fall_right = FALSE;
7760 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7761 can_fall_left = FALSE;
7763 can_fall_any = (can_fall_left || can_fall_right);
7764 can_fall_both = FALSE;
7769 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7770 can_fall_right = FALSE; /* slip down on left side */
7772 can_fall_left = !(can_fall_right = RND(2));
7774 can_fall_both = FALSE;
7779 /* if not determined otherwise, prefer left side for slipping down */
7780 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7781 started_moving = TRUE;
7784 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7786 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7787 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7788 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7789 int belt_dir = game.belt_dir[belt_nr];
7791 if ((belt_dir == MV_LEFT && left_is_free) ||
7792 (belt_dir == MV_RIGHT && right_is_free))
7794 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7796 InitMovingField(x, y, belt_dir);
7797 started_moving = TRUE;
7799 Pushed[x][y] = TRUE;
7800 Pushed[nextx][y] = TRUE;
7802 GfxAction[x][y] = ACTION_DEFAULT;
7806 MovDir[x][y] = 0; /* if element was moving, stop it */
7811 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7812 if (CAN_MOVE(element) && !started_moving)
7814 int move_pattern = element_info[element].move_pattern;
7817 Moving2Blocked(x, y, &newx, &newy);
7819 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7822 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7823 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7825 WasJustMoving[x][y] = 0;
7826 CheckCollision[x][y] = 0;
7828 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7830 if (Feld[x][y] != element) /* element has changed */
7834 if (!MovDelay[x][y]) /* start new movement phase */
7836 /* all objects that can change their move direction after each step
7837 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7839 if (element != EL_YAMYAM &&
7840 element != EL_DARK_YAMYAM &&
7841 element != EL_PACMAN &&
7842 !(move_pattern & MV_ANY_DIRECTION) &&
7843 move_pattern != MV_TURNING_LEFT &&
7844 move_pattern != MV_TURNING_RIGHT &&
7845 move_pattern != MV_TURNING_LEFT_RIGHT &&
7846 move_pattern != MV_TURNING_RIGHT_LEFT &&
7847 move_pattern != MV_TURNING_RANDOM)
7851 if (MovDelay[x][y] && (element == EL_BUG ||
7852 element == EL_SPACESHIP ||
7853 element == EL_SP_SNIKSNAK ||
7854 element == EL_SP_ELECTRON ||
7855 element == EL_MOLE))
7856 TEST_DrawLevelField(x, y);
7860 if (MovDelay[x][y]) /* wait some time before next movement */
7864 if (element == EL_ROBOT ||
7865 element == EL_YAMYAM ||
7866 element == EL_DARK_YAMYAM)
7868 DrawLevelElementAnimationIfNeeded(x, y, element);
7869 PlayLevelSoundAction(x, y, ACTION_WAITING);
7871 else if (element == EL_SP_ELECTRON)
7872 DrawLevelElementAnimationIfNeeded(x, y, element);
7873 else if (element == EL_DRAGON)
7876 int dir = MovDir[x][y];
7877 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7878 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7879 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7880 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7881 dir == MV_UP ? IMG_FLAMES_1_UP :
7882 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7883 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7885 GfxAction[x][y] = ACTION_ATTACKING;
7887 if (IS_PLAYER(x, y))
7888 DrawPlayerField(x, y);
7890 TEST_DrawLevelField(x, y);
7892 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7894 for (i = 1; i <= 3; i++)
7896 int xx = x + i * dx;
7897 int yy = y + i * dy;
7898 int sx = SCREENX(xx);
7899 int sy = SCREENY(yy);
7900 int flame_graphic = graphic + (i - 1);
7902 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7907 int flamed = MovingOrBlocked2Element(xx, yy);
7909 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7912 RemoveMovingField(xx, yy);
7914 ChangeDelay[xx][yy] = 0;
7916 Feld[xx][yy] = EL_FLAMES;
7918 if (IN_SCR_FIELD(sx, sy))
7920 TEST_DrawLevelFieldCrumbled(xx, yy);
7921 DrawGraphic(sx, sy, flame_graphic, frame);
7926 if (Feld[xx][yy] == EL_FLAMES)
7927 Feld[xx][yy] = EL_EMPTY;
7928 TEST_DrawLevelField(xx, yy);
7933 if (MovDelay[x][y]) /* element still has to wait some time */
7935 PlayLevelSoundAction(x, y, ACTION_WAITING);
7941 /* now make next step */
7943 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7945 if (DONT_COLLIDE_WITH(element) &&
7946 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7947 !PLAYER_ENEMY_PROTECTED(newx, newy))
7949 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7954 else if (CAN_MOVE_INTO_ACID(element) &&
7955 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7956 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7957 (MovDir[x][y] == MV_DOWN ||
7958 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7960 SplashAcid(newx, newy);
7961 Store[x][y] = EL_ACID;
7963 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7965 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7966 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7967 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7968 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7971 TEST_DrawLevelField(x, y);
7973 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7974 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7975 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7977 local_player->friends_still_needed--;
7978 if (!local_player->friends_still_needed &&
7979 !local_player->GameOver && AllPlayersGone)
7980 PlayerWins(local_player);
7984 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7986 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7987 TEST_DrawLevelField(newx, newy);
7989 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7991 else if (!IS_FREE(newx, newy))
7993 GfxAction[x][y] = ACTION_WAITING;
7995 if (IS_PLAYER(x, y))
7996 DrawPlayerField(x, y);
7998 TEST_DrawLevelField(x, y);
8003 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8005 if (IS_FOOD_PIG(Feld[newx][newy]))
8007 if (IS_MOVING(newx, newy))
8008 RemoveMovingField(newx, newy);
8011 Feld[newx][newy] = EL_EMPTY;
8012 TEST_DrawLevelField(newx, newy);
8015 PlayLevelSound(x, y, SND_PIG_DIGGING);
8017 else if (!IS_FREE(newx, newy))
8019 if (IS_PLAYER(x, y))
8020 DrawPlayerField(x, y);
8022 TEST_DrawLevelField(x, y);
8027 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8029 if (Store[x][y] != EL_EMPTY)
8031 boolean can_clone = FALSE;
8034 /* check if element to clone is still there */
8035 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8037 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8045 /* cannot clone or target field not free anymore -- do not clone */
8046 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8047 Store[x][y] = EL_EMPTY;
8050 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8052 if (IS_MV_DIAGONAL(MovDir[x][y]))
8054 int diagonal_move_dir = MovDir[x][y];
8055 int stored = Store[x][y];
8056 int change_delay = 8;
8059 /* android is moving diagonally */
8061 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8063 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8064 GfxElement[x][y] = EL_EMC_ANDROID;
8065 GfxAction[x][y] = ACTION_SHRINKING;
8066 GfxDir[x][y] = diagonal_move_dir;
8067 ChangeDelay[x][y] = change_delay;
8069 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8072 DrawLevelGraphicAnimation(x, y, graphic);
8073 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8075 if (Feld[newx][newy] == EL_ACID)
8077 SplashAcid(newx, newy);
8082 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8084 Store[newx][newy] = EL_EMC_ANDROID;
8085 GfxElement[newx][newy] = EL_EMC_ANDROID;
8086 GfxAction[newx][newy] = ACTION_GROWING;
8087 GfxDir[newx][newy] = diagonal_move_dir;
8088 ChangeDelay[newx][newy] = change_delay;
8090 graphic = el_act_dir2img(GfxElement[newx][newy],
8091 GfxAction[newx][newy], GfxDir[newx][newy]);
8093 DrawLevelGraphicAnimation(newx, newy, graphic);
8094 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8100 Feld[newx][newy] = EL_EMPTY;
8101 TEST_DrawLevelField(newx, newy);
8103 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8106 else if (!IS_FREE(newx, newy))
8111 else if (IS_CUSTOM_ELEMENT(element) &&
8112 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8114 if (!DigFieldByCE(newx, newy, element))
8117 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8119 RunnerVisit[x][y] = FrameCounter;
8120 PlayerVisit[x][y] /= 8; /* expire player visit path */
8123 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8125 if (!IS_FREE(newx, newy))
8127 if (IS_PLAYER(x, y))
8128 DrawPlayerField(x, y);
8130 TEST_DrawLevelField(x, y);
8136 boolean wanna_flame = !RND(10);
8137 int dx = newx - x, dy = newy - y;
8138 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8139 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8140 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8141 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8142 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8143 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8146 IS_CLASSIC_ENEMY(element1) ||
8147 IS_CLASSIC_ENEMY(element2)) &&
8148 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8149 element1 != EL_FLAMES && element2 != EL_FLAMES)
8151 ResetGfxAnimation(x, y);
8152 GfxAction[x][y] = ACTION_ATTACKING;
8154 if (IS_PLAYER(x, y))
8155 DrawPlayerField(x, y);
8157 TEST_DrawLevelField(x, y);
8159 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8161 MovDelay[x][y] = 50;
8163 Feld[newx][newy] = EL_FLAMES;
8164 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8165 Feld[newx1][newy1] = EL_FLAMES;
8166 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8167 Feld[newx2][newy2] = EL_FLAMES;
8173 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8174 Feld[newx][newy] == EL_DIAMOND)
8176 if (IS_MOVING(newx, newy))
8177 RemoveMovingField(newx, newy);
8180 Feld[newx][newy] = EL_EMPTY;
8181 TEST_DrawLevelField(newx, newy);
8184 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8186 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8187 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8189 if (AmoebaNr[newx][newy])
8191 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8192 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8193 Feld[newx][newy] == EL_BD_AMOEBA)
8194 AmoebaCnt[AmoebaNr[newx][newy]]--;
8197 if (IS_MOVING(newx, newy))
8199 RemoveMovingField(newx, newy);
8203 Feld[newx][newy] = EL_EMPTY;
8204 TEST_DrawLevelField(newx, newy);
8207 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8209 else if ((element == EL_PACMAN || element == EL_MOLE)
8210 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8212 if (AmoebaNr[newx][newy])
8214 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8215 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8216 Feld[newx][newy] == EL_BD_AMOEBA)
8217 AmoebaCnt[AmoebaNr[newx][newy]]--;
8220 if (element == EL_MOLE)
8222 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8223 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8225 ResetGfxAnimation(x, y);
8226 GfxAction[x][y] = ACTION_DIGGING;
8227 TEST_DrawLevelField(x, y);
8229 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8231 return; /* wait for shrinking amoeba */
8233 else /* element == EL_PACMAN */
8235 Feld[newx][newy] = EL_EMPTY;
8236 TEST_DrawLevelField(newx, newy);
8237 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8240 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8241 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8242 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8244 /* wait for shrinking amoeba to completely disappear */
8247 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8249 /* object was running against a wall */
8253 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8254 DrawLevelElementAnimation(x, y, element);
8256 if (DONT_TOUCH(element))
8257 TestIfBadThingTouchesPlayer(x, y);
8262 InitMovingField(x, y, MovDir[x][y]);
8264 PlayLevelSoundAction(x, y, ACTION_MOVING);
8268 ContinueMoving(x, y);
8271 void ContinueMoving(int x, int y)
8273 int element = Feld[x][y];
8274 struct ElementInfo *ei = &element_info[element];
8275 int direction = MovDir[x][y];
8276 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8277 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8278 int newx = x + dx, newy = y + dy;
8279 int stored = Store[x][y];
8280 int stored_new = Store[newx][newy];
8281 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8282 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8283 boolean last_line = (newy == lev_fieldy - 1);
8285 MovPos[x][y] += getElementMoveStepsize(x, y);
8287 if (pushed_by_player) /* special case: moving object pushed by player */
8288 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8290 if (ABS(MovPos[x][y]) < TILEX)
8292 TEST_DrawLevelField(x, y);
8294 return; /* element is still moving */
8297 /* element reached destination field */
8299 Feld[x][y] = EL_EMPTY;
8300 Feld[newx][newy] = element;
8301 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8303 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8305 element = Feld[newx][newy] = EL_ACID;
8307 else if (element == EL_MOLE)
8309 Feld[x][y] = EL_SAND;
8311 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8313 else if (element == EL_QUICKSAND_FILLING)
8315 element = Feld[newx][newy] = get_next_element(element);
8316 Store[newx][newy] = Store[x][y];
8318 else if (element == EL_QUICKSAND_EMPTYING)
8320 Feld[x][y] = get_next_element(element);
8321 element = Feld[newx][newy] = Store[x][y];
8323 else if (element == EL_QUICKSAND_FAST_FILLING)
8325 element = Feld[newx][newy] = get_next_element(element);
8326 Store[newx][newy] = Store[x][y];
8328 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8330 Feld[x][y] = get_next_element(element);
8331 element = Feld[newx][newy] = Store[x][y];
8333 else if (element == EL_MAGIC_WALL_FILLING)
8335 element = Feld[newx][newy] = get_next_element(element);
8336 if (!game.magic_wall_active)
8337 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8338 Store[newx][newy] = Store[x][y];
8340 else if (element == EL_MAGIC_WALL_EMPTYING)
8342 Feld[x][y] = get_next_element(element);
8343 if (!game.magic_wall_active)
8344 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8345 element = Feld[newx][newy] = Store[x][y];
8347 InitField(newx, newy, FALSE);
8349 else if (element == EL_BD_MAGIC_WALL_FILLING)
8351 element = Feld[newx][newy] = get_next_element(element);
8352 if (!game.magic_wall_active)
8353 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8354 Store[newx][newy] = Store[x][y];
8356 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8358 Feld[x][y] = get_next_element(element);
8359 if (!game.magic_wall_active)
8360 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8361 element = Feld[newx][newy] = Store[x][y];
8363 InitField(newx, newy, FALSE);
8365 else if (element == EL_DC_MAGIC_WALL_FILLING)
8367 element = Feld[newx][newy] = get_next_element(element);
8368 if (!game.magic_wall_active)
8369 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8370 Store[newx][newy] = Store[x][y];
8372 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8374 Feld[x][y] = get_next_element(element);
8375 if (!game.magic_wall_active)
8376 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8377 element = Feld[newx][newy] = Store[x][y];
8379 InitField(newx, newy, FALSE);
8381 else if (element == EL_AMOEBA_DROPPING)
8383 Feld[x][y] = get_next_element(element);
8384 element = Feld[newx][newy] = Store[x][y];
8386 else if (element == EL_SOKOBAN_OBJECT)
8389 Feld[x][y] = Back[x][y];
8391 if (Back[newx][newy])
8392 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8394 Back[x][y] = Back[newx][newy] = 0;
8397 Store[x][y] = EL_EMPTY;
8402 MovDelay[newx][newy] = 0;
8404 if (CAN_CHANGE_OR_HAS_ACTION(element))
8406 /* copy element change control values to new field */
8407 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8408 ChangePage[newx][newy] = ChangePage[x][y];
8409 ChangeCount[newx][newy] = ChangeCount[x][y];
8410 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8413 CustomValue[newx][newy] = CustomValue[x][y];
8415 ChangeDelay[x][y] = 0;
8416 ChangePage[x][y] = -1;
8417 ChangeCount[x][y] = 0;
8418 ChangeEvent[x][y] = -1;
8420 CustomValue[x][y] = 0;
8422 /* copy animation control values to new field */
8423 GfxFrame[newx][newy] = GfxFrame[x][y];
8424 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8425 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8426 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8428 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8430 /* some elements can leave other elements behind after moving */
8431 if (ei->move_leave_element != EL_EMPTY &&
8432 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8433 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8435 int move_leave_element = ei->move_leave_element;
8437 /* this makes it possible to leave the removed element again */
8438 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8439 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8441 Feld[x][y] = move_leave_element;
8443 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8444 MovDir[x][y] = direction;
8446 InitField(x, y, FALSE);
8448 if (GFX_CRUMBLED(Feld[x][y]))
8449 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8451 if (ELEM_IS_PLAYER(move_leave_element))
8452 RelocatePlayer(x, y, move_leave_element);
8455 /* do this after checking for left-behind element */
8456 ResetGfxAnimation(x, y); /* reset animation values for old field */
8458 if (!CAN_MOVE(element) ||
8459 (CAN_FALL(element) && direction == MV_DOWN &&
8460 (element == EL_SPRING ||
8461 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8462 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8463 GfxDir[x][y] = MovDir[newx][newy] = 0;
8465 TEST_DrawLevelField(x, y);
8466 TEST_DrawLevelField(newx, newy);
8468 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8470 /* prevent pushed element from moving on in pushed direction */
8471 if (pushed_by_player && CAN_MOVE(element) &&
8472 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8473 !(element_info[element].move_pattern & direction))
8474 TurnRound(newx, newy);
8476 /* prevent elements on conveyor belt from moving on in last direction */
8477 if (pushed_by_conveyor && CAN_FALL(element) &&
8478 direction & MV_HORIZONTAL)
8479 MovDir[newx][newy] = 0;
8481 if (!pushed_by_player)
8483 int nextx = newx + dx, nexty = newy + dy;
8484 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8486 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8488 if (CAN_FALL(element) && direction == MV_DOWN)
8489 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8491 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8492 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8494 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8495 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8498 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8500 TestIfBadThingTouchesPlayer(newx, newy);
8501 TestIfBadThingTouchesFriend(newx, newy);
8503 if (!IS_CUSTOM_ELEMENT(element))
8504 TestIfBadThingTouchesOtherBadThing(newx, newy);
8506 else if (element == EL_PENGUIN)
8507 TestIfFriendTouchesBadThing(newx, newy);
8509 if (DONT_GET_HIT_BY(element))
8511 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8514 /* give the player one last chance (one more frame) to move away */
8515 if (CAN_FALL(element) && direction == MV_DOWN &&
8516 (last_line || (!IS_FREE(x, newy + 1) &&
8517 (!IS_PLAYER(x, newy + 1) ||
8518 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8521 if (pushed_by_player && !game.use_change_when_pushing_bug)
8523 int push_side = MV_DIR_OPPOSITE(direction);
8524 struct PlayerInfo *player = PLAYERINFO(x, y);
8526 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8527 player->index_bit, push_side);
8528 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8529 player->index_bit, push_side);
8532 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8533 MovDelay[newx][newy] = 1;
8535 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8537 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8538 TestIfElementHitsCustomElement(newx, newy, direction);
8539 TestIfPlayerTouchesCustomElement(newx, newy);
8540 TestIfElementTouchesCustomElement(newx, newy);
8542 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8543 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8544 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8545 MV_DIR_OPPOSITE(direction));
8548 int AmoebeNachbarNr(int ax, int ay)
8551 int element = Feld[ax][ay];
8553 static int xy[4][2] =
8561 for (i = 0; i < NUM_DIRECTIONS; i++)
8563 int x = ax + xy[i][0];
8564 int y = ay + xy[i][1];
8566 if (!IN_LEV_FIELD(x, y))
8569 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8570 group_nr = AmoebaNr[x][y];
8576 void AmoebenVereinigen(int ax, int ay)
8578 int i, x, y, xx, yy;
8579 int new_group_nr = AmoebaNr[ax][ay];
8580 static int xy[4][2] =
8588 if (new_group_nr == 0)
8591 for (i = 0; i < NUM_DIRECTIONS; i++)
8596 if (!IN_LEV_FIELD(x, y))
8599 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8600 Feld[x][y] == EL_BD_AMOEBA ||
8601 Feld[x][y] == EL_AMOEBA_DEAD) &&
8602 AmoebaNr[x][y] != new_group_nr)
8604 int old_group_nr = AmoebaNr[x][y];
8606 if (old_group_nr == 0)
8609 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8610 AmoebaCnt[old_group_nr] = 0;
8611 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8612 AmoebaCnt2[old_group_nr] = 0;
8614 SCAN_PLAYFIELD(xx, yy)
8616 if (AmoebaNr[xx][yy] == old_group_nr)
8617 AmoebaNr[xx][yy] = new_group_nr;
8623 void AmoebeUmwandeln(int ax, int ay)
8627 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8629 int group_nr = AmoebaNr[ax][ay];
8634 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8635 printf("AmoebeUmwandeln(): This should never happen!\n");
8640 SCAN_PLAYFIELD(x, y)
8642 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8645 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8649 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8650 SND_AMOEBA_TURNING_TO_GEM :
8651 SND_AMOEBA_TURNING_TO_ROCK));
8656 static int xy[4][2] =
8664 for (i = 0; i < NUM_DIRECTIONS; i++)
8669 if (!IN_LEV_FIELD(x, y))
8672 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8674 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8675 SND_AMOEBA_TURNING_TO_GEM :
8676 SND_AMOEBA_TURNING_TO_ROCK));
8683 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8686 int group_nr = AmoebaNr[ax][ay];
8687 boolean done = FALSE;
8692 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8693 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8698 SCAN_PLAYFIELD(x, y)
8700 if (AmoebaNr[x][y] == group_nr &&
8701 (Feld[x][y] == EL_AMOEBA_DEAD ||
8702 Feld[x][y] == EL_BD_AMOEBA ||
8703 Feld[x][y] == EL_AMOEBA_GROWING))
8706 Feld[x][y] = new_element;
8707 InitField(x, y, FALSE);
8708 TEST_DrawLevelField(x, y);
8714 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8715 SND_BD_AMOEBA_TURNING_TO_ROCK :
8716 SND_BD_AMOEBA_TURNING_TO_GEM));
8719 void AmoebeWaechst(int x, int y)
8721 static unsigned int sound_delay = 0;
8722 static unsigned int sound_delay_value = 0;
8724 if (!MovDelay[x][y]) /* start new growing cycle */
8728 if (DelayReached(&sound_delay, sound_delay_value))
8730 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8731 sound_delay_value = 30;
8735 if (MovDelay[x][y]) /* wait some time before growing bigger */
8738 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8740 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8741 6 - MovDelay[x][y]);
8743 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8746 if (!MovDelay[x][y])
8748 Feld[x][y] = Store[x][y];
8750 TEST_DrawLevelField(x, y);
8755 void AmoebaDisappearing(int x, int y)
8757 static unsigned int sound_delay = 0;
8758 static unsigned int sound_delay_value = 0;
8760 if (!MovDelay[x][y]) /* start new shrinking cycle */
8764 if (DelayReached(&sound_delay, sound_delay_value))
8765 sound_delay_value = 30;
8768 if (MovDelay[x][y]) /* wait some time before shrinking */
8771 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8773 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8774 6 - MovDelay[x][y]);
8776 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8779 if (!MovDelay[x][y])
8781 Feld[x][y] = EL_EMPTY;
8782 TEST_DrawLevelField(x, y);
8784 /* don't let mole enter this field in this cycle;
8785 (give priority to objects falling to this field from above) */
8791 void AmoebeAbleger(int ax, int ay)
8794 int element = Feld[ax][ay];
8795 int graphic = el2img(element);
8796 int newax = ax, neway = ay;
8797 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8798 static int xy[4][2] =
8806 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8808 Feld[ax][ay] = EL_AMOEBA_DEAD;
8809 TEST_DrawLevelField(ax, ay);
8813 if (IS_ANIMATED(graphic))
8814 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8816 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8817 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8819 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8822 if (MovDelay[ax][ay])
8826 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8829 int x = ax + xy[start][0];
8830 int y = ay + xy[start][1];
8832 if (!IN_LEV_FIELD(x, y))
8835 if (IS_FREE(x, y) ||
8836 CAN_GROW_INTO(Feld[x][y]) ||
8837 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8838 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8844 if (newax == ax && neway == ay)
8847 else /* normal or "filled" (BD style) amoeba */
8850 boolean waiting_for_player = FALSE;
8852 for (i = 0; i < NUM_DIRECTIONS; i++)
8854 int j = (start + i) % 4;
8855 int x = ax + xy[j][0];
8856 int y = ay + xy[j][1];
8858 if (!IN_LEV_FIELD(x, y))
8861 if (IS_FREE(x, y) ||
8862 CAN_GROW_INTO(Feld[x][y]) ||
8863 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8864 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8870 else if (IS_PLAYER(x, y))
8871 waiting_for_player = TRUE;
8874 if (newax == ax && neway == ay) /* amoeba cannot grow */
8876 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8878 Feld[ax][ay] = EL_AMOEBA_DEAD;
8879 TEST_DrawLevelField(ax, ay);
8880 AmoebaCnt[AmoebaNr[ax][ay]]--;
8882 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8884 if (element == EL_AMOEBA_FULL)
8885 AmoebeUmwandeln(ax, ay);
8886 else if (element == EL_BD_AMOEBA)
8887 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8892 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8894 /* amoeba gets larger by growing in some direction */
8896 int new_group_nr = AmoebaNr[ax][ay];
8899 if (new_group_nr == 0)
8901 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8902 printf("AmoebeAbleger(): This should never happen!\n");
8907 AmoebaNr[newax][neway] = new_group_nr;
8908 AmoebaCnt[new_group_nr]++;
8909 AmoebaCnt2[new_group_nr]++;
8911 /* if amoeba touches other amoeba(s) after growing, unify them */
8912 AmoebenVereinigen(newax, neway);
8914 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8916 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8922 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8923 (neway == lev_fieldy - 1 && newax != ax))
8925 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8926 Store[newax][neway] = element;
8928 else if (neway == ay || element == EL_EMC_DRIPPER)
8930 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8932 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8936 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8937 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8938 Store[ax][ay] = EL_AMOEBA_DROP;
8939 ContinueMoving(ax, ay);
8943 TEST_DrawLevelField(newax, neway);
8946 void Life(int ax, int ay)
8950 int element = Feld[ax][ay];
8951 int graphic = el2img(element);
8952 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8954 boolean changed = FALSE;
8956 if (IS_ANIMATED(graphic))
8957 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8962 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8963 MovDelay[ax][ay] = life_time;
8965 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8968 if (MovDelay[ax][ay])
8972 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8974 int xx = ax+x1, yy = ay+y1;
8977 if (!IN_LEV_FIELD(xx, yy))
8980 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8982 int x = xx+x2, y = yy+y2;
8984 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8987 if (((Feld[x][y] == element ||
8988 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8990 (IS_FREE(x, y) && Stop[x][y]))
8994 if (xx == ax && yy == ay) /* field in the middle */
8996 if (nachbarn < life_parameter[0] ||
8997 nachbarn > life_parameter[1])
8999 Feld[xx][yy] = EL_EMPTY;
9001 TEST_DrawLevelField(xx, yy);
9002 Stop[xx][yy] = TRUE;
9006 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9007 { /* free border field */
9008 if (nachbarn >= life_parameter[2] &&
9009 nachbarn <= life_parameter[3])
9011 Feld[xx][yy] = element;
9012 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9014 TEST_DrawLevelField(xx, yy);
9015 Stop[xx][yy] = TRUE;
9022 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9023 SND_GAME_OF_LIFE_GROWING);
9026 static void InitRobotWheel(int x, int y)
9028 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9031 static void RunRobotWheel(int x, int y)
9033 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9036 static void StopRobotWheel(int x, int y)
9038 if (ZX == x && ZY == y)
9042 game.robot_wheel_active = FALSE;
9046 static void InitTimegateWheel(int x, int y)
9048 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9051 static void RunTimegateWheel(int x, int y)
9053 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9056 static void InitMagicBallDelay(int x, int y)
9058 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9061 static void ActivateMagicBall(int bx, int by)
9065 if (level.ball_random)
9067 int pos_border = RND(8); /* select one of the eight border elements */
9068 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9069 int xx = pos_content % 3;
9070 int yy = pos_content / 3;
9075 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9076 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9080 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9082 int xx = x - bx + 1;
9083 int yy = y - by + 1;
9085 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9086 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9090 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9093 void CheckExit(int x, int y)
9095 if (local_player->gems_still_needed > 0 ||
9096 local_player->sokobanfields_still_needed > 0 ||
9097 local_player->lights_still_needed > 0)
9099 int element = Feld[x][y];
9100 int graphic = el2img(element);
9102 if (IS_ANIMATED(graphic))
9103 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9108 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9111 Feld[x][y] = EL_EXIT_OPENING;
9113 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9116 void CheckExitEM(int x, int y)
9118 if (local_player->gems_still_needed > 0 ||
9119 local_player->sokobanfields_still_needed > 0 ||
9120 local_player->lights_still_needed > 0)
9122 int element = Feld[x][y];
9123 int graphic = el2img(element);
9125 if (IS_ANIMATED(graphic))
9126 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9131 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9134 Feld[x][y] = EL_EM_EXIT_OPENING;
9136 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9139 void CheckExitSteel(int x, int y)
9141 if (local_player->gems_still_needed > 0 ||
9142 local_player->sokobanfields_still_needed > 0 ||
9143 local_player->lights_still_needed > 0)
9145 int element = Feld[x][y];
9146 int graphic = el2img(element);
9148 if (IS_ANIMATED(graphic))
9149 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9154 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9157 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9159 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9162 void CheckExitSteelEM(int x, int y)
9164 if (local_player->gems_still_needed > 0 ||
9165 local_player->sokobanfields_still_needed > 0 ||
9166 local_player->lights_still_needed > 0)
9168 int element = Feld[x][y];
9169 int graphic = el2img(element);
9171 if (IS_ANIMATED(graphic))
9172 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9177 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9180 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9182 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9185 void CheckExitSP(int x, int y)
9187 if (local_player->gems_still_needed > 0)
9189 int element = Feld[x][y];
9190 int graphic = el2img(element);
9192 if (IS_ANIMATED(graphic))
9193 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9198 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9201 Feld[x][y] = EL_SP_EXIT_OPENING;
9203 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9206 static void CloseAllOpenTimegates()
9210 SCAN_PLAYFIELD(x, y)
9212 int element = Feld[x][y];
9214 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9216 Feld[x][y] = EL_TIMEGATE_CLOSING;
9218 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9223 void DrawTwinkleOnField(int x, int y)
9225 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9228 if (Feld[x][y] == EL_BD_DIAMOND)
9231 if (MovDelay[x][y] == 0) /* next animation frame */
9232 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9234 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9238 DrawLevelElementAnimation(x, y, Feld[x][y]);
9240 if (MovDelay[x][y] != 0)
9242 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9243 10 - MovDelay[x][y]);
9245 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9250 void MauerWaechst(int x, int y)
9254 if (!MovDelay[x][y]) /* next animation frame */
9255 MovDelay[x][y] = 3 * delay;
9257 if (MovDelay[x][y]) /* wait some time before next frame */
9261 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9263 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9264 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9266 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9269 if (!MovDelay[x][y])
9271 if (MovDir[x][y] == MV_LEFT)
9273 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9274 TEST_DrawLevelField(x - 1, y);
9276 else if (MovDir[x][y] == MV_RIGHT)
9278 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9279 TEST_DrawLevelField(x + 1, y);
9281 else if (MovDir[x][y] == MV_UP)
9283 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9284 TEST_DrawLevelField(x, y - 1);
9288 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9289 TEST_DrawLevelField(x, y + 1);
9292 Feld[x][y] = Store[x][y];
9294 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9295 TEST_DrawLevelField(x, y);
9300 void MauerAbleger(int ax, int ay)
9302 int element = Feld[ax][ay];
9303 int graphic = el2img(element);
9304 boolean oben_frei = FALSE, unten_frei = FALSE;
9305 boolean links_frei = FALSE, rechts_frei = FALSE;
9306 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9307 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9308 boolean new_wall = FALSE;
9310 if (IS_ANIMATED(graphic))
9311 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9313 if (!MovDelay[ax][ay]) /* start building new wall */
9314 MovDelay[ax][ay] = 6;
9316 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9319 if (MovDelay[ax][ay])
9323 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9325 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9327 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9329 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9332 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9333 element == EL_EXPANDABLE_WALL_ANY)
9337 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9338 Store[ax][ay-1] = element;
9339 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9340 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9341 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9342 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9347 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9348 Store[ax][ay+1] = element;
9349 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9350 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9351 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9352 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9357 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9358 element == EL_EXPANDABLE_WALL_ANY ||
9359 element == EL_EXPANDABLE_WALL ||
9360 element == EL_BD_EXPANDABLE_WALL)
9364 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9365 Store[ax-1][ay] = element;
9366 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9367 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9368 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9369 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9375 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9376 Store[ax+1][ay] = element;
9377 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9378 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9379 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9380 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9385 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9386 TEST_DrawLevelField(ax, ay);
9388 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9390 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9391 unten_massiv = TRUE;
9392 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9393 links_massiv = TRUE;
9394 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9395 rechts_massiv = TRUE;
9397 if (((oben_massiv && unten_massiv) ||
9398 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9399 element == EL_EXPANDABLE_WALL) &&
9400 ((links_massiv && rechts_massiv) ||
9401 element == EL_EXPANDABLE_WALL_VERTICAL))
9402 Feld[ax][ay] = EL_WALL;
9405 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9408 void MauerAblegerStahl(int ax, int ay)
9410 int element = Feld[ax][ay];
9411 int graphic = el2img(element);
9412 boolean oben_frei = FALSE, unten_frei = FALSE;
9413 boolean links_frei = FALSE, rechts_frei = FALSE;
9414 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9415 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9416 boolean new_wall = FALSE;
9418 if (IS_ANIMATED(graphic))
9419 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9421 if (!MovDelay[ax][ay]) /* start building new wall */
9422 MovDelay[ax][ay] = 6;
9424 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9427 if (MovDelay[ax][ay])
9431 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9433 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9435 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9437 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9440 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9441 element == EL_EXPANDABLE_STEELWALL_ANY)
9445 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9446 Store[ax][ay-1] = element;
9447 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9448 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9449 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9450 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9455 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9456 Store[ax][ay+1] = element;
9457 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9458 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9459 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9460 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9465 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9466 element == EL_EXPANDABLE_STEELWALL_ANY)
9470 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9471 Store[ax-1][ay] = element;
9472 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9473 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9474 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9475 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9481 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9482 Store[ax+1][ay] = element;
9483 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9484 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9485 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9486 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9491 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9493 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9494 unten_massiv = TRUE;
9495 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9496 links_massiv = TRUE;
9497 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9498 rechts_massiv = TRUE;
9500 if (((oben_massiv && unten_massiv) ||
9501 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9502 ((links_massiv && rechts_massiv) ||
9503 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9504 Feld[ax][ay] = EL_STEELWALL;
9507 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9510 void CheckForDragon(int x, int y)
9513 boolean dragon_found = FALSE;
9514 static int xy[4][2] =
9522 for (i = 0; i < NUM_DIRECTIONS; i++)
9524 for (j = 0; j < 4; j++)
9526 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9528 if (IN_LEV_FIELD(xx, yy) &&
9529 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9531 if (Feld[xx][yy] == EL_DRAGON)
9532 dragon_found = TRUE;
9541 for (i = 0; i < NUM_DIRECTIONS; i++)
9543 for (j = 0; j < 3; j++)
9545 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9547 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9549 Feld[xx][yy] = EL_EMPTY;
9550 TEST_DrawLevelField(xx, yy);
9559 static void InitBuggyBase(int x, int y)
9561 int element = Feld[x][y];
9562 int activating_delay = FRAMES_PER_SECOND / 4;
9565 (element == EL_SP_BUGGY_BASE ?
9566 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9567 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9569 element == EL_SP_BUGGY_BASE_ACTIVE ?
9570 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9573 static void WarnBuggyBase(int x, int y)
9576 static int xy[4][2] =
9584 for (i = 0; i < NUM_DIRECTIONS; i++)
9586 int xx = x + xy[i][0];
9587 int yy = y + xy[i][1];
9589 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9591 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9598 static void InitTrap(int x, int y)
9600 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9603 static void ActivateTrap(int x, int y)
9605 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9608 static void ChangeActiveTrap(int x, int y)
9610 int graphic = IMG_TRAP_ACTIVE;
9612 /* if new animation frame was drawn, correct crumbled sand border */
9613 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9614 TEST_DrawLevelFieldCrumbled(x, y);
9617 static int getSpecialActionElement(int element, int number, int base_element)
9619 return (element != EL_EMPTY ? element :
9620 number != -1 ? base_element + number - 1 :
9624 static int getModifiedActionNumber(int value_old, int operator, int operand,
9625 int value_min, int value_max)
9627 int value_new = (operator == CA_MODE_SET ? operand :
9628 operator == CA_MODE_ADD ? value_old + operand :
9629 operator == CA_MODE_SUBTRACT ? value_old - operand :
9630 operator == CA_MODE_MULTIPLY ? value_old * operand :
9631 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9632 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9635 return (value_new < value_min ? value_min :
9636 value_new > value_max ? value_max :
9640 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9642 struct ElementInfo *ei = &element_info[element];
9643 struct ElementChangeInfo *change = &ei->change_page[page];
9644 int target_element = change->target_element;
9645 int action_type = change->action_type;
9646 int action_mode = change->action_mode;
9647 int action_arg = change->action_arg;
9648 int action_element = change->action_element;
9651 if (!change->has_action)
9654 /* ---------- determine action paramater values -------------------------- */
9656 int level_time_value =
9657 (level.time > 0 ? TimeLeft :
9660 int action_arg_element_raw =
9661 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9662 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9663 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9664 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9665 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9666 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9667 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9669 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9671 int action_arg_direction =
9672 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9673 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9674 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9675 change->actual_trigger_side :
9676 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9677 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9680 int action_arg_number_min =
9681 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9684 int action_arg_number_max =
9685 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9686 action_type == CA_SET_LEVEL_GEMS ? 999 :
9687 action_type == CA_SET_LEVEL_TIME ? 9999 :
9688 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9689 action_type == CA_SET_CE_VALUE ? 9999 :
9690 action_type == CA_SET_CE_SCORE ? 9999 :
9693 int action_arg_number_reset =
9694 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9695 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9696 action_type == CA_SET_LEVEL_TIME ? level.time :
9697 action_type == CA_SET_LEVEL_SCORE ? 0 :
9698 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9699 action_type == CA_SET_CE_SCORE ? 0 :
9702 int action_arg_number =
9703 (action_arg <= CA_ARG_MAX ? action_arg :
9704 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9705 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9706 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9707 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9708 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9709 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9710 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9711 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9712 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9713 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9714 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9715 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9716 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9717 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9718 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9719 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9720 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9721 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9722 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9723 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9724 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9727 int action_arg_number_old =
9728 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9729 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9730 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9731 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9732 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9735 int action_arg_number_new =
9736 getModifiedActionNumber(action_arg_number_old,
9737 action_mode, action_arg_number,
9738 action_arg_number_min, action_arg_number_max);
9740 int trigger_player_bits =
9741 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9742 change->actual_trigger_player_bits : change->trigger_player);
9744 int action_arg_player_bits =
9745 (action_arg >= CA_ARG_PLAYER_1 &&
9746 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9747 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9748 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9751 /* ---------- execute action -------------------------------------------- */
9753 switch (action_type)
9760 /* ---------- level actions ------------------------------------------- */
9762 case CA_RESTART_LEVEL:
9764 game.restart_level = TRUE;
9769 case CA_SHOW_ENVELOPE:
9771 int element = getSpecialActionElement(action_arg_element,
9772 action_arg_number, EL_ENVELOPE_1);
9774 if (IS_ENVELOPE(element))
9775 local_player->show_envelope = element;
9780 case CA_SET_LEVEL_TIME:
9782 if (level.time > 0) /* only modify limited time value */
9784 TimeLeft = action_arg_number_new;
9786 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9788 DisplayGameControlValues();
9790 if (!TimeLeft && setup.time_limit)
9791 for (i = 0; i < MAX_PLAYERS; i++)
9792 KillPlayer(&stored_player[i]);
9798 case CA_SET_LEVEL_SCORE:
9800 local_player->score = action_arg_number_new;
9802 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9804 DisplayGameControlValues();
9809 case CA_SET_LEVEL_GEMS:
9811 local_player->gems_still_needed = action_arg_number_new;
9813 game.snapshot.collected_item = TRUE;
9815 game_panel_controls[GAME_PANEL_GEMS].value =
9816 local_player->gems_still_needed;
9818 DisplayGameControlValues();
9823 case CA_SET_LEVEL_WIND:
9825 game.wind_direction = action_arg_direction;
9830 case CA_SET_LEVEL_RANDOM_SEED:
9832 /* ensure that setting a new random seed while playing is predictable */
9833 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9838 /* ---------- player actions ------------------------------------------ */
9840 case CA_MOVE_PLAYER:
9842 /* automatically move to the next field in specified direction */
9843 for (i = 0; i < MAX_PLAYERS; i++)
9844 if (trigger_player_bits & (1 << i))
9845 stored_player[i].programmed_action = action_arg_direction;
9850 case CA_EXIT_PLAYER:
9852 for (i = 0; i < MAX_PLAYERS; i++)
9853 if (action_arg_player_bits & (1 << i))
9854 PlayerWins(&stored_player[i]);
9859 case CA_KILL_PLAYER:
9861 for (i = 0; i < MAX_PLAYERS; i++)
9862 if (action_arg_player_bits & (1 << i))
9863 KillPlayer(&stored_player[i]);
9868 case CA_SET_PLAYER_KEYS:
9870 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9871 int element = getSpecialActionElement(action_arg_element,
9872 action_arg_number, EL_KEY_1);
9874 if (IS_KEY(element))
9876 for (i = 0; i < MAX_PLAYERS; i++)
9878 if (trigger_player_bits & (1 << i))
9880 stored_player[i].key[KEY_NR(element)] = key_state;
9882 DrawGameDoorValues();
9890 case CA_SET_PLAYER_SPEED:
9892 for (i = 0; i < MAX_PLAYERS; i++)
9894 if (trigger_player_bits & (1 << i))
9896 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9898 if (action_arg == CA_ARG_SPEED_FASTER &&
9899 stored_player[i].cannot_move)
9901 action_arg_number = STEPSIZE_VERY_SLOW;
9903 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9904 action_arg == CA_ARG_SPEED_FASTER)
9906 action_arg_number = 2;
9907 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9910 else if (action_arg == CA_ARG_NUMBER_RESET)
9912 action_arg_number = level.initial_player_stepsize[i];
9916 getModifiedActionNumber(move_stepsize,
9919 action_arg_number_min,
9920 action_arg_number_max);
9922 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9929 case CA_SET_PLAYER_SHIELD:
9931 for (i = 0; i < MAX_PLAYERS; i++)
9933 if (trigger_player_bits & (1 << i))
9935 if (action_arg == CA_ARG_SHIELD_OFF)
9937 stored_player[i].shield_normal_time_left = 0;
9938 stored_player[i].shield_deadly_time_left = 0;
9940 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9942 stored_player[i].shield_normal_time_left = 999999;
9944 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9946 stored_player[i].shield_normal_time_left = 999999;
9947 stored_player[i].shield_deadly_time_left = 999999;
9955 case CA_SET_PLAYER_GRAVITY:
9957 for (i = 0; i < MAX_PLAYERS; i++)
9959 if (trigger_player_bits & (1 << i))
9961 stored_player[i].gravity =
9962 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9963 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9964 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9965 stored_player[i].gravity);
9972 case CA_SET_PLAYER_ARTWORK:
9974 for (i = 0; i < MAX_PLAYERS; i++)
9976 if (trigger_player_bits & (1 << i))
9978 int artwork_element = action_arg_element;
9980 if (action_arg == CA_ARG_ELEMENT_RESET)
9982 (level.use_artwork_element[i] ? level.artwork_element[i] :
9983 stored_player[i].element_nr);
9985 if (stored_player[i].artwork_element != artwork_element)
9986 stored_player[i].Frame = 0;
9988 stored_player[i].artwork_element = artwork_element;
9990 SetPlayerWaiting(&stored_player[i], FALSE);
9992 /* set number of special actions for bored and sleeping animation */
9993 stored_player[i].num_special_action_bored =
9994 get_num_special_action(artwork_element,
9995 ACTION_BORING_1, ACTION_BORING_LAST);
9996 stored_player[i].num_special_action_sleeping =
9997 get_num_special_action(artwork_element,
9998 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10005 case CA_SET_PLAYER_INVENTORY:
10007 for (i = 0; i < MAX_PLAYERS; i++)
10009 struct PlayerInfo *player = &stored_player[i];
10012 if (trigger_player_bits & (1 << i))
10014 int inventory_element = action_arg_element;
10016 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10017 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10018 action_arg == CA_ARG_ELEMENT_ACTION)
10020 int element = inventory_element;
10021 int collect_count = element_info[element].collect_count_initial;
10023 if (!IS_CUSTOM_ELEMENT(element))
10026 if (collect_count == 0)
10027 player->inventory_infinite_element = element;
10029 for (k = 0; k < collect_count; k++)
10030 if (player->inventory_size < MAX_INVENTORY_SIZE)
10031 player->inventory_element[player->inventory_size++] =
10034 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10035 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10036 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10038 if (player->inventory_infinite_element != EL_UNDEFINED &&
10039 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10040 action_arg_element_raw))
10041 player->inventory_infinite_element = EL_UNDEFINED;
10043 for (k = 0, j = 0; j < player->inventory_size; j++)
10045 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10046 action_arg_element_raw))
10047 player->inventory_element[k++] = player->inventory_element[j];
10050 player->inventory_size = k;
10052 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10054 if (player->inventory_size > 0)
10056 for (j = 0; j < player->inventory_size - 1; j++)
10057 player->inventory_element[j] = player->inventory_element[j + 1];
10059 player->inventory_size--;
10062 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10064 if (player->inventory_size > 0)
10065 player->inventory_size--;
10067 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10069 player->inventory_infinite_element = EL_UNDEFINED;
10070 player->inventory_size = 0;
10072 else if (action_arg == CA_ARG_INVENTORY_RESET)
10074 player->inventory_infinite_element = EL_UNDEFINED;
10075 player->inventory_size = 0;
10077 if (level.use_initial_inventory[i])
10079 for (j = 0; j < level.initial_inventory_size[i]; j++)
10081 int element = level.initial_inventory_content[i][j];
10082 int collect_count = element_info[element].collect_count_initial;
10084 if (!IS_CUSTOM_ELEMENT(element))
10087 if (collect_count == 0)
10088 player->inventory_infinite_element = element;
10090 for (k = 0; k < collect_count; k++)
10091 if (player->inventory_size < MAX_INVENTORY_SIZE)
10092 player->inventory_element[player->inventory_size++] =
10103 /* ---------- CE actions ---------------------------------------------- */
10105 case CA_SET_CE_VALUE:
10107 int last_ce_value = CustomValue[x][y];
10109 CustomValue[x][y] = action_arg_number_new;
10111 if (CustomValue[x][y] != last_ce_value)
10113 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10114 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10116 if (CustomValue[x][y] == 0)
10118 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10119 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10126 case CA_SET_CE_SCORE:
10128 int last_ce_score = ei->collect_score;
10130 ei->collect_score = action_arg_number_new;
10132 if (ei->collect_score != last_ce_score)
10134 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10135 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10137 if (ei->collect_score == 0)
10141 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10142 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10145 This is a very special case that seems to be a mixture between
10146 CheckElementChange() and CheckTriggeredElementChange(): while
10147 the first one only affects single elements that are triggered
10148 directly, the second one affects multiple elements in the playfield
10149 that are triggered indirectly by another element. This is a third
10150 case: Changing the CE score always affects multiple identical CEs,
10151 so every affected CE must be checked, not only the single CE for
10152 which the CE score was changed in the first place (as every instance
10153 of that CE shares the same CE score, and therefore also can change)!
10155 SCAN_PLAYFIELD(xx, yy)
10157 if (Feld[xx][yy] == element)
10158 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10159 CE_SCORE_GETS_ZERO);
10167 case CA_SET_CE_ARTWORK:
10169 int artwork_element = action_arg_element;
10170 boolean reset_frame = FALSE;
10173 if (action_arg == CA_ARG_ELEMENT_RESET)
10174 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10177 if (ei->gfx_element != artwork_element)
10178 reset_frame = TRUE;
10180 ei->gfx_element = artwork_element;
10182 SCAN_PLAYFIELD(xx, yy)
10184 if (Feld[xx][yy] == element)
10188 ResetGfxAnimation(xx, yy);
10189 ResetRandomAnimationValue(xx, yy);
10192 TEST_DrawLevelField(xx, yy);
10199 /* ---------- engine actions ------------------------------------------ */
10201 case CA_SET_ENGINE_SCAN_MODE:
10203 InitPlayfieldScanMode(action_arg);
10213 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10215 int old_element = Feld[x][y];
10216 int new_element = GetElementFromGroupElement(element);
10217 int previous_move_direction = MovDir[x][y];
10218 int last_ce_value = CustomValue[x][y];
10219 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10220 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10221 boolean add_player_onto_element = (new_element_is_player &&
10222 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10223 IS_WALKABLE(old_element));
10225 if (!add_player_onto_element)
10227 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10228 RemoveMovingField(x, y);
10232 Feld[x][y] = new_element;
10234 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10235 MovDir[x][y] = previous_move_direction;
10237 if (element_info[new_element].use_last_ce_value)
10238 CustomValue[x][y] = last_ce_value;
10240 InitField_WithBug1(x, y, FALSE);
10242 new_element = Feld[x][y]; /* element may have changed */
10244 ResetGfxAnimation(x, y);
10245 ResetRandomAnimationValue(x, y);
10247 TEST_DrawLevelField(x, y);
10249 if (GFX_CRUMBLED(new_element))
10250 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10253 /* check if element under the player changes from accessible to unaccessible
10254 (needed for special case of dropping element which then changes) */
10255 /* (must be checked after creating new element for walkable group elements) */
10256 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10257 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10264 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10265 if (new_element_is_player)
10266 RelocatePlayer(x, y, new_element);
10269 ChangeCount[x][y]++; /* count number of changes in the same frame */
10271 TestIfBadThingTouchesPlayer(x, y);
10272 TestIfPlayerTouchesCustomElement(x, y);
10273 TestIfElementTouchesCustomElement(x, y);
10276 static void CreateField(int x, int y, int element)
10278 CreateFieldExt(x, y, element, FALSE);
10281 static void CreateElementFromChange(int x, int y, int element)
10283 element = GET_VALID_RUNTIME_ELEMENT(element);
10285 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10287 int old_element = Feld[x][y];
10289 /* prevent changed element from moving in same engine frame
10290 unless both old and new element can either fall or move */
10291 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10292 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10296 CreateFieldExt(x, y, element, TRUE);
10299 static boolean ChangeElement(int x, int y, int element, int page)
10301 struct ElementInfo *ei = &element_info[element];
10302 struct ElementChangeInfo *change = &ei->change_page[page];
10303 int ce_value = CustomValue[x][y];
10304 int ce_score = ei->collect_score;
10305 int target_element;
10306 int old_element = Feld[x][y];
10308 /* always use default change event to prevent running into a loop */
10309 if (ChangeEvent[x][y] == -1)
10310 ChangeEvent[x][y] = CE_DELAY;
10312 if (ChangeEvent[x][y] == CE_DELAY)
10314 /* reset actual trigger element, trigger player and action element */
10315 change->actual_trigger_element = EL_EMPTY;
10316 change->actual_trigger_player = EL_EMPTY;
10317 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10318 change->actual_trigger_side = CH_SIDE_NONE;
10319 change->actual_trigger_ce_value = 0;
10320 change->actual_trigger_ce_score = 0;
10323 /* do not change elements more than a specified maximum number of changes */
10324 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10327 ChangeCount[x][y]++; /* count number of changes in the same frame */
10329 if (change->explode)
10336 if (change->use_target_content)
10338 boolean complete_replace = TRUE;
10339 boolean can_replace[3][3];
10342 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10345 boolean is_walkable;
10346 boolean is_diggable;
10347 boolean is_collectible;
10348 boolean is_removable;
10349 boolean is_destructible;
10350 int ex = x + xx - 1;
10351 int ey = y + yy - 1;
10352 int content_element = change->target_content.e[xx][yy];
10355 can_replace[xx][yy] = TRUE;
10357 if (ex == x && ey == y) /* do not check changing element itself */
10360 if (content_element == EL_EMPTY_SPACE)
10362 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10367 if (!IN_LEV_FIELD(ex, ey))
10369 can_replace[xx][yy] = FALSE;
10370 complete_replace = FALSE;
10377 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10378 e = MovingOrBlocked2Element(ex, ey);
10380 is_empty = (IS_FREE(ex, ey) ||
10381 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10383 is_walkable = (is_empty || IS_WALKABLE(e));
10384 is_diggable = (is_empty || IS_DIGGABLE(e));
10385 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10386 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10387 is_removable = (is_diggable || is_collectible);
10389 can_replace[xx][yy] =
10390 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10391 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10392 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10393 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10394 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10395 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10396 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10398 if (!can_replace[xx][yy])
10399 complete_replace = FALSE;
10402 if (!change->only_if_complete || complete_replace)
10404 boolean something_has_changed = FALSE;
10406 if (change->only_if_complete && change->use_random_replace &&
10407 RND(100) < change->random_percentage)
10410 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10412 int ex = x + xx - 1;
10413 int ey = y + yy - 1;
10414 int content_element;
10416 if (can_replace[xx][yy] && (!change->use_random_replace ||
10417 RND(100) < change->random_percentage))
10419 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10420 RemoveMovingField(ex, ey);
10422 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10424 content_element = change->target_content.e[xx][yy];
10425 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10426 ce_value, ce_score);
10428 CreateElementFromChange(ex, ey, target_element);
10430 something_has_changed = TRUE;
10432 /* for symmetry reasons, freeze newly created border elements */
10433 if (ex != x || ey != y)
10434 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10438 if (something_has_changed)
10440 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10441 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10447 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10448 ce_value, ce_score);
10450 if (element == EL_DIAGONAL_GROWING ||
10451 element == EL_DIAGONAL_SHRINKING)
10453 target_element = Store[x][y];
10455 Store[x][y] = EL_EMPTY;
10458 CreateElementFromChange(x, y, target_element);
10460 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10461 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10464 /* this uses direct change before indirect change */
10465 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10470 static void HandleElementChange(int x, int y, int page)
10472 int element = MovingOrBlocked2Element(x, y);
10473 struct ElementInfo *ei = &element_info[element];
10474 struct ElementChangeInfo *change = &ei->change_page[page];
10475 boolean handle_action_before_change = FALSE;
10478 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10479 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10482 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10483 x, y, element, element_info[element].token_name);
10484 printf("HandleElementChange(): This should never happen!\n");
10489 /* this can happen with classic bombs on walkable, changing elements */
10490 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10495 if (ChangeDelay[x][y] == 0) /* initialize element change */
10497 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10499 if (change->can_change)
10501 /* !!! not clear why graphic animation should be reset at all here !!! */
10502 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10503 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10506 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10508 When using an animation frame delay of 1 (this only happens with
10509 "sp_zonk.moving.left/right" in the classic graphics), the default
10510 (non-moving) animation shows wrong animation frames (while the
10511 moving animation, like "sp_zonk.moving.left/right", is correct,
10512 so this graphical bug never shows up with the classic graphics).
10513 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10514 be drawn instead of the correct frames 0,1,2,3. This is caused by
10515 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10516 an element change: First when the change delay ("ChangeDelay[][]")
10517 counter has reached zero after decrementing, then a second time in
10518 the next frame (after "GfxFrame[][]" was already incremented) when
10519 "ChangeDelay[][]" is reset to the initial delay value again.
10521 This causes frame 0 to be drawn twice, while the last frame won't
10522 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10524 As some animations may already be cleverly designed around this bug
10525 (at least the "Snake Bite" snake tail animation does this), it cannot
10526 simply be fixed here without breaking such existing animations.
10527 Unfortunately, it cannot easily be detected if a graphics set was
10528 designed "before" or "after" the bug was fixed. As a workaround,
10529 a new graphics set option "game.graphics_engine_version" was added
10530 to be able to specify the game's major release version for which the
10531 graphics set was designed, which can then be used to decide if the
10532 bugfix should be used (version 4 and above) or not (version 3 or
10533 below, or if no version was specified at all, as with old sets).
10535 (The wrong/fixed animation frames can be tested with the test level set
10536 "test_gfxframe" and level "000", which contains a specially prepared
10537 custom element at level position (x/y) == (11/9) which uses the zonk
10538 animation mentioned above. Using "game.graphics_engine_version: 4"
10539 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10540 This can also be seen from the debug output for this test element.)
10543 /* when a custom element is about to change (for example by change delay),
10544 do not reset graphic animation when the custom element is moving */
10545 if (game.graphics_engine_version < 4 &&
10548 ResetGfxAnimation(x, y);
10549 ResetRandomAnimationValue(x, y);
10552 if (change->pre_change_function)
10553 change->pre_change_function(x, y);
10557 ChangeDelay[x][y]--;
10559 if (ChangeDelay[x][y] != 0) /* continue element change */
10561 if (change->can_change)
10563 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10565 if (IS_ANIMATED(graphic))
10566 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10568 if (change->change_function)
10569 change->change_function(x, y);
10572 else /* finish element change */
10574 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10576 page = ChangePage[x][y];
10577 ChangePage[x][y] = -1;
10579 change = &ei->change_page[page];
10582 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10584 ChangeDelay[x][y] = 1; /* try change after next move step */
10585 ChangePage[x][y] = page; /* remember page to use for change */
10590 /* special case: set new level random seed before changing element */
10591 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10592 handle_action_before_change = TRUE;
10594 if (change->has_action && handle_action_before_change)
10595 ExecuteCustomElementAction(x, y, element, page);
10597 if (change->can_change)
10599 if (ChangeElement(x, y, element, page))
10601 if (change->post_change_function)
10602 change->post_change_function(x, y);
10606 if (change->has_action && !handle_action_before_change)
10607 ExecuteCustomElementAction(x, y, element, page);
10611 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10612 int trigger_element,
10614 int trigger_player,
10618 boolean change_done_any = FALSE;
10619 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10622 if (!(trigger_events[trigger_element][trigger_event]))
10625 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10627 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10629 int element = EL_CUSTOM_START + i;
10630 boolean change_done = FALSE;
10633 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10634 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10637 for (p = 0; p < element_info[element].num_change_pages; p++)
10639 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10641 if (change->can_change_or_has_action &&
10642 change->has_event[trigger_event] &&
10643 change->trigger_side & trigger_side &&
10644 change->trigger_player & trigger_player &&
10645 change->trigger_page & trigger_page_bits &&
10646 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10648 change->actual_trigger_element = trigger_element;
10649 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10650 change->actual_trigger_player_bits = trigger_player;
10651 change->actual_trigger_side = trigger_side;
10652 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10653 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10655 if ((change->can_change && !change_done) || change->has_action)
10659 SCAN_PLAYFIELD(x, y)
10661 if (Feld[x][y] == element)
10663 if (change->can_change && !change_done)
10665 /* if element already changed in this frame, not only prevent
10666 another element change (checked in ChangeElement()), but
10667 also prevent additional element actions for this element */
10669 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10670 !level.use_action_after_change_bug)
10673 ChangeDelay[x][y] = 1;
10674 ChangeEvent[x][y] = trigger_event;
10676 HandleElementChange(x, y, p);
10678 else if (change->has_action)
10680 /* if element already changed in this frame, not only prevent
10681 another element change (checked in ChangeElement()), but
10682 also prevent additional element actions for this element */
10684 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10685 !level.use_action_after_change_bug)
10688 ExecuteCustomElementAction(x, y, element, p);
10689 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10694 if (change->can_change)
10696 change_done = TRUE;
10697 change_done_any = TRUE;
10704 RECURSION_LOOP_DETECTION_END();
10706 return change_done_any;
10709 static boolean CheckElementChangeExt(int x, int y,
10711 int trigger_element,
10713 int trigger_player,
10716 boolean change_done = FALSE;
10719 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10720 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10723 if (Feld[x][y] == EL_BLOCKED)
10725 Blocked2Moving(x, y, &x, &y);
10726 element = Feld[x][y];
10729 /* check if element has already changed or is about to change after moving */
10730 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10731 Feld[x][y] != element) ||
10733 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10734 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10735 ChangePage[x][y] != -1)))
10738 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10740 for (p = 0; p < element_info[element].num_change_pages; p++)
10742 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10744 /* check trigger element for all events where the element that is checked
10745 for changing interacts with a directly adjacent element -- this is
10746 different to element changes that affect other elements to change on the
10747 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10748 boolean check_trigger_element =
10749 (trigger_event == CE_TOUCHING_X ||
10750 trigger_event == CE_HITTING_X ||
10751 trigger_event == CE_HIT_BY_X ||
10752 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10754 if (change->can_change_or_has_action &&
10755 change->has_event[trigger_event] &&
10756 change->trigger_side & trigger_side &&
10757 change->trigger_player & trigger_player &&
10758 (!check_trigger_element ||
10759 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10761 change->actual_trigger_element = trigger_element;
10762 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10763 change->actual_trigger_player_bits = trigger_player;
10764 change->actual_trigger_side = trigger_side;
10765 change->actual_trigger_ce_value = CustomValue[x][y];
10766 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10768 /* special case: trigger element not at (x,y) position for some events */
10769 if (check_trigger_element)
10781 { 0, 0 }, { 0, 0 }, { 0, 0 },
10785 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10786 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10788 change->actual_trigger_ce_value = CustomValue[xx][yy];
10789 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10792 if (change->can_change && !change_done)
10794 ChangeDelay[x][y] = 1;
10795 ChangeEvent[x][y] = trigger_event;
10797 HandleElementChange(x, y, p);
10799 change_done = TRUE;
10801 else if (change->has_action)
10803 ExecuteCustomElementAction(x, y, element, p);
10804 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10809 RECURSION_LOOP_DETECTION_END();
10811 return change_done;
10814 static void PlayPlayerSound(struct PlayerInfo *player)
10816 int jx = player->jx, jy = player->jy;
10817 int sound_element = player->artwork_element;
10818 int last_action = player->last_action_waiting;
10819 int action = player->action_waiting;
10821 if (player->is_waiting)
10823 if (action != last_action)
10824 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10826 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10830 if (action != last_action)
10831 StopSound(element_info[sound_element].sound[last_action]);
10833 if (last_action == ACTION_SLEEPING)
10834 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10838 static void PlayAllPlayersSound()
10842 for (i = 0; i < MAX_PLAYERS; i++)
10843 if (stored_player[i].active)
10844 PlayPlayerSound(&stored_player[i]);
10847 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10849 boolean last_waiting = player->is_waiting;
10850 int move_dir = player->MovDir;
10852 player->dir_waiting = move_dir;
10853 player->last_action_waiting = player->action_waiting;
10857 if (!last_waiting) /* not waiting -> waiting */
10859 player->is_waiting = TRUE;
10861 player->frame_counter_bored =
10863 game.player_boring_delay_fixed +
10864 GetSimpleRandom(game.player_boring_delay_random);
10865 player->frame_counter_sleeping =
10867 game.player_sleeping_delay_fixed +
10868 GetSimpleRandom(game.player_sleeping_delay_random);
10870 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10873 if (game.player_sleeping_delay_fixed +
10874 game.player_sleeping_delay_random > 0 &&
10875 player->anim_delay_counter == 0 &&
10876 player->post_delay_counter == 0 &&
10877 FrameCounter >= player->frame_counter_sleeping)
10878 player->is_sleeping = TRUE;
10879 else if (game.player_boring_delay_fixed +
10880 game.player_boring_delay_random > 0 &&
10881 FrameCounter >= player->frame_counter_bored)
10882 player->is_bored = TRUE;
10884 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10885 player->is_bored ? ACTION_BORING :
10888 if (player->is_sleeping && player->use_murphy)
10890 /* special case for sleeping Murphy when leaning against non-free tile */
10892 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10893 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10894 !IS_MOVING(player->jx - 1, player->jy)))
10895 move_dir = MV_LEFT;
10896 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10897 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10898 !IS_MOVING(player->jx + 1, player->jy)))
10899 move_dir = MV_RIGHT;
10901 player->is_sleeping = FALSE;
10903 player->dir_waiting = move_dir;
10906 if (player->is_sleeping)
10908 if (player->num_special_action_sleeping > 0)
10910 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10912 int last_special_action = player->special_action_sleeping;
10913 int num_special_action = player->num_special_action_sleeping;
10914 int special_action =
10915 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10916 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10917 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10918 last_special_action + 1 : ACTION_SLEEPING);
10919 int special_graphic =
10920 el_act_dir2img(player->artwork_element, special_action, move_dir);
10922 player->anim_delay_counter =
10923 graphic_info[special_graphic].anim_delay_fixed +
10924 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10925 player->post_delay_counter =
10926 graphic_info[special_graphic].post_delay_fixed +
10927 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10929 player->special_action_sleeping = special_action;
10932 if (player->anim_delay_counter > 0)
10934 player->action_waiting = player->special_action_sleeping;
10935 player->anim_delay_counter--;
10937 else if (player->post_delay_counter > 0)
10939 player->post_delay_counter--;
10943 else if (player->is_bored)
10945 if (player->num_special_action_bored > 0)
10947 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10949 int special_action =
10950 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10951 int special_graphic =
10952 el_act_dir2img(player->artwork_element, special_action, move_dir);
10954 player->anim_delay_counter =
10955 graphic_info[special_graphic].anim_delay_fixed +
10956 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10957 player->post_delay_counter =
10958 graphic_info[special_graphic].post_delay_fixed +
10959 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10961 player->special_action_bored = special_action;
10964 if (player->anim_delay_counter > 0)
10966 player->action_waiting = player->special_action_bored;
10967 player->anim_delay_counter--;
10969 else if (player->post_delay_counter > 0)
10971 player->post_delay_counter--;
10976 else if (last_waiting) /* waiting -> not waiting */
10978 player->is_waiting = FALSE;
10979 player->is_bored = FALSE;
10980 player->is_sleeping = FALSE;
10982 player->frame_counter_bored = -1;
10983 player->frame_counter_sleeping = -1;
10985 player->anim_delay_counter = 0;
10986 player->post_delay_counter = 0;
10988 player->dir_waiting = player->MovDir;
10989 player->action_waiting = ACTION_DEFAULT;
10991 player->special_action_bored = ACTION_DEFAULT;
10992 player->special_action_sleeping = ACTION_DEFAULT;
10996 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10998 if ((!player->is_moving && player->was_moving) ||
10999 (player->MovPos == 0 && player->was_moving) ||
11000 (player->is_snapping && !player->was_snapping) ||
11001 (player->is_dropping && !player->was_dropping))
11003 if (!CheckSaveEngineSnapshotToList())
11006 player->was_moving = FALSE;
11007 player->was_snapping = TRUE;
11008 player->was_dropping = TRUE;
11012 if (player->is_moving)
11013 player->was_moving = TRUE;
11015 if (!player->is_snapping)
11016 player->was_snapping = FALSE;
11018 if (!player->is_dropping)
11019 player->was_dropping = FALSE;
11023 static void CheckSingleStepMode(struct PlayerInfo *player)
11025 if (tape.single_step && tape.recording && !tape.pausing)
11027 /* as it is called "single step mode", just return to pause mode when the
11028 player stopped moving after one tile (or never starts moving at all) */
11029 if (!player->is_moving &&
11030 !player->is_pushing &&
11031 !player->is_dropping_pressed)
11033 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11034 SnapField(player, 0, 0); /* stop snapping */
11038 CheckSaveEngineSnapshot(player);
11041 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11043 int left = player_action & JOY_LEFT;
11044 int right = player_action & JOY_RIGHT;
11045 int up = player_action & JOY_UP;
11046 int down = player_action & JOY_DOWN;
11047 int button1 = player_action & JOY_BUTTON_1;
11048 int button2 = player_action & JOY_BUTTON_2;
11049 int dx = (left ? -1 : right ? 1 : 0);
11050 int dy = (up ? -1 : down ? 1 : 0);
11052 if (!player->active || tape.pausing)
11058 SnapField(player, dx, dy);
11062 DropElement(player);
11064 MovePlayer(player, dx, dy);
11067 CheckSingleStepMode(player);
11069 SetPlayerWaiting(player, FALSE);
11071 return player_action;
11075 /* no actions for this player (no input at player's configured device) */
11077 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11078 SnapField(player, 0, 0);
11079 CheckGravityMovementWhenNotMoving(player);
11081 if (player->MovPos == 0)
11082 SetPlayerWaiting(player, TRUE);
11084 if (player->MovPos == 0) /* needed for tape.playing */
11085 player->is_moving = FALSE;
11087 player->is_dropping = FALSE;
11088 player->is_dropping_pressed = FALSE;
11089 player->drop_pressed_delay = 0;
11091 CheckSingleStepMode(player);
11097 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11100 if (!tape.use_mouse)
11103 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11104 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11105 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11108 static void SetTapeActionFromMouseAction(byte *tape_action,
11109 struct MouseActionInfo *mouse_action)
11111 if (!tape.use_mouse)
11114 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11115 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11116 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11119 static void CheckLevelTime()
11123 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11124 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11126 if (level.native_em_level->lev->home == 0) /* all players at home */
11128 PlayerWins(local_player);
11130 AllPlayersGone = TRUE;
11132 level.native_em_level->lev->home = -1;
11135 if (level.native_em_level->ply[0]->alive == 0 &&
11136 level.native_em_level->ply[1]->alive == 0 &&
11137 level.native_em_level->ply[2]->alive == 0 &&
11138 level.native_em_level->ply[3]->alive == 0) /* all dead */
11139 AllPlayersGone = TRUE;
11141 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11143 if (game_sp.LevelSolved &&
11144 !game_sp.GameOver) /* game won */
11146 PlayerWins(local_player);
11148 game_sp.GameOver = TRUE;
11150 AllPlayersGone = TRUE;
11153 if (game_sp.GameOver) /* game lost */
11154 AllPlayersGone = TRUE;
11156 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11158 if (game_mm.level_solved &&
11159 !game_mm.game_over) /* game won */
11161 PlayerWins(local_player);
11163 game_mm.game_over = TRUE;
11165 AllPlayersGone = TRUE;
11168 if (game_mm.game_over) /* game lost */
11169 AllPlayersGone = TRUE;
11172 if (TimeFrames >= FRAMES_PER_SECOND)
11177 for (i = 0; i < MAX_PLAYERS; i++)
11179 struct PlayerInfo *player = &stored_player[i];
11181 if (SHIELD_ON(player))
11183 player->shield_normal_time_left--;
11185 if (player->shield_deadly_time_left > 0)
11186 player->shield_deadly_time_left--;
11190 if (!local_player->LevelSolved && !level.use_step_counter)
11198 if (TimeLeft <= 10 && setup.time_limit)
11199 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11201 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11202 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11204 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11206 if (!TimeLeft && setup.time_limit)
11208 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11209 level.native_em_level->lev->killed_out_of_time = TRUE;
11211 for (i = 0; i < MAX_PLAYERS; i++)
11212 KillPlayer(&stored_player[i]);
11215 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11217 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11220 level.native_em_level->lev->time =
11221 (game.no_time_limit ? TimePlayed : TimeLeft);
11224 if (tape.recording || tape.playing)
11225 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11228 if (tape.recording || tape.playing)
11229 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11231 UpdateAndDisplayGameControlValues();
11234 void AdvanceFrameAndPlayerCounters(int player_nr)
11238 /* advance frame counters (global frame counter and time frame counter) */
11242 /* advance player counters (counters for move delay, move animation etc.) */
11243 for (i = 0; i < MAX_PLAYERS; i++)
11245 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11246 int move_delay_value = stored_player[i].move_delay_value;
11247 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11249 if (!advance_player_counters) /* not all players may be affected */
11252 if (move_frames == 0) /* less than one move per game frame */
11254 int stepsize = TILEX / move_delay_value;
11255 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11256 int count = (stored_player[i].is_moving ?
11257 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11259 if (count % delay == 0)
11263 stored_player[i].Frame += move_frames;
11265 if (stored_player[i].MovPos != 0)
11266 stored_player[i].StepFrame += move_frames;
11268 if (stored_player[i].move_delay > 0)
11269 stored_player[i].move_delay--;
11271 /* due to bugs in previous versions, counter must count up, not down */
11272 if (stored_player[i].push_delay != -1)
11273 stored_player[i].push_delay++;
11275 if (stored_player[i].drop_delay > 0)
11276 stored_player[i].drop_delay--;
11278 if (stored_player[i].is_dropping_pressed)
11279 stored_player[i].drop_pressed_delay++;
11283 void StartGameActions(boolean init_network_game, boolean record_tape,
11286 unsigned int new_random_seed = InitRND(random_seed);
11289 TapeStartRecording(new_random_seed);
11291 if (init_network_game)
11293 SendToServer_StartPlaying();
11301 void GameActionsExt()
11304 static unsigned int game_frame_delay = 0;
11306 unsigned int game_frame_delay_value;
11307 byte *recorded_player_action;
11308 byte summarized_player_action = 0;
11309 byte tape_action[MAX_PLAYERS];
11312 /* detect endless loops, caused by custom element programming */
11313 if (recursion_loop_detected && recursion_loop_depth == 0)
11315 char *message = getStringCat3("Internal Error! Element ",
11316 EL_NAME(recursion_loop_element),
11317 " caused endless loop! Quit the game?");
11319 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11320 EL_NAME(recursion_loop_element));
11322 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11324 recursion_loop_detected = FALSE; /* if game should be continued */
11331 if (game.restart_level)
11332 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11334 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11335 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11337 if (level.native_em_level->lev->home == 0) /* all players at home */
11339 PlayerWins(local_player);
11341 AllPlayersGone = TRUE;
11343 level.native_em_level->lev->home = -1;
11346 if (level.native_em_level->ply[0]->alive == 0 &&
11347 level.native_em_level->ply[1]->alive == 0 &&
11348 level.native_em_level->ply[2]->alive == 0 &&
11349 level.native_em_level->ply[3]->alive == 0) /* all dead */
11350 AllPlayersGone = TRUE;
11352 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11354 if (game_sp.LevelSolved &&
11355 !game_sp.GameOver) /* game won */
11357 PlayerWins(local_player);
11359 game_sp.GameOver = TRUE;
11361 AllPlayersGone = TRUE;
11364 if (game_sp.GameOver) /* game lost */
11365 AllPlayersGone = TRUE;
11367 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11369 if (game_mm.level_solved &&
11370 !game_mm.game_over) /* game won */
11372 PlayerWins(local_player);
11374 game_mm.game_over = TRUE;
11376 AllPlayersGone = TRUE;
11379 if (game_mm.game_over) /* game lost */
11380 AllPlayersGone = TRUE;
11383 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11386 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11389 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11392 game_frame_delay_value =
11393 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11395 if (tape.playing && tape.warp_forward && !tape.pausing)
11396 game_frame_delay_value = 0;
11398 SetVideoFrameDelay(game_frame_delay_value);
11402 /* ---------- main game synchronization point ---------- */
11404 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11406 printf("::: skip == %d\n", skip);
11409 /* ---------- main game synchronization point ---------- */
11411 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11415 if (network_playing && !network_player_action_received)
11417 /* try to get network player actions in time */
11419 /* last chance to get network player actions without main loop delay */
11420 HandleNetworking();
11422 /* game was quit by network peer */
11423 if (game_status != GAME_MODE_PLAYING)
11426 if (!network_player_action_received)
11427 return; /* failed to get network player actions in time */
11429 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11435 /* at this point we know that we really continue executing the game */
11437 network_player_action_received = FALSE;
11439 /* when playing tape, read previously recorded player input from tape data */
11440 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11442 local_player->effective_mouse_action = local_player->mouse_action;
11444 if (recorded_player_action != NULL)
11445 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11446 recorded_player_action);
11448 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11452 if (tape.set_centered_player)
11454 game.centered_player_nr_next = tape.centered_player_nr_next;
11455 game.set_centered_player = TRUE;
11458 for (i = 0; i < MAX_PLAYERS; i++)
11460 summarized_player_action |= stored_player[i].action;
11462 if (!network_playing && (game.team_mode || tape.playing))
11463 stored_player[i].effective_action = stored_player[i].action;
11466 if (network_playing)
11467 SendToServer_MovePlayer(summarized_player_action);
11469 // summarize all actions at local players mapped input device position
11470 // (this allows using different input devices in single player mode)
11471 if (!network.enabled && !game.team_mode)
11472 stored_player[map_player_action[local_player->index_nr]].effective_action =
11473 summarized_player_action;
11475 if (tape.recording &&
11477 setup.input_on_focus &&
11478 game.centered_player_nr != -1)
11480 for (i = 0; i < MAX_PLAYERS; i++)
11481 stored_player[i].effective_action =
11482 (i == game.centered_player_nr ? summarized_player_action : 0);
11485 if (recorded_player_action != NULL)
11486 for (i = 0; i < MAX_PLAYERS; i++)
11487 stored_player[i].effective_action = recorded_player_action[i];
11489 for (i = 0; i < MAX_PLAYERS; i++)
11491 tape_action[i] = stored_player[i].effective_action;
11493 /* (this may happen in the RND game engine if a player was not present on
11494 the playfield on level start, but appeared later from a custom element */
11495 if (setup.team_mode &&
11498 !tape.player_participates[i])
11499 tape.player_participates[i] = TRUE;
11502 SetTapeActionFromMouseAction(tape_action,
11503 &local_player->effective_mouse_action);
11505 /* only record actions from input devices, but not programmed actions */
11506 if (tape.recording)
11507 TapeRecordAction(tape_action);
11509 #if USE_NEW_PLAYER_ASSIGNMENTS
11510 // !!! also map player actions in single player mode !!!
11511 // if (game.team_mode)
11514 byte mapped_action[MAX_PLAYERS];
11516 #if DEBUG_PLAYER_ACTIONS
11518 for (i = 0; i < MAX_PLAYERS; i++)
11519 printf(" %d, ", stored_player[i].effective_action);
11522 for (i = 0; i < MAX_PLAYERS; i++)
11523 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11525 for (i = 0; i < MAX_PLAYERS; i++)
11526 stored_player[i].effective_action = mapped_action[i];
11528 #if DEBUG_PLAYER_ACTIONS
11530 for (i = 0; i < MAX_PLAYERS; i++)
11531 printf(" %d, ", stored_player[i].effective_action);
11535 #if DEBUG_PLAYER_ACTIONS
11539 for (i = 0; i < MAX_PLAYERS; i++)
11540 printf(" %d, ", stored_player[i].effective_action);
11546 for (i = 0; i < MAX_PLAYERS; i++)
11548 // allow engine snapshot in case of changed movement attempt
11549 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11550 (stored_player[i].effective_action & KEY_MOTION))
11551 game.snapshot.changed_action = TRUE;
11553 // allow engine snapshot in case of snapping/dropping attempt
11554 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11555 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11556 game.snapshot.changed_action = TRUE;
11558 game.snapshot.last_action[i] = stored_player[i].effective_action;
11561 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11563 GameActions_EM_Main();
11565 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11567 GameActions_SP_Main();
11569 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11571 GameActions_MM_Main();
11575 GameActions_RND_Main();
11578 BlitScreenToBitmap(backbuffer);
11582 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11584 if (global.show_frames_per_second)
11586 static unsigned int fps_counter = 0;
11587 static int fps_frames = 0;
11588 unsigned int fps_delay_ms = Counter() - fps_counter;
11592 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11594 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11597 fps_counter = Counter();
11599 /* always draw FPS to screen after FPS value was updated */
11600 redraw_mask |= REDRAW_FPS;
11603 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11604 if (GetDrawDeactivationMask() == REDRAW_NONE)
11605 redraw_mask |= REDRAW_FPS;
11609 static void GameActions_CheckSaveEngineSnapshot()
11611 if (!game.snapshot.save_snapshot)
11614 // clear flag for saving snapshot _before_ saving snapshot
11615 game.snapshot.save_snapshot = FALSE;
11617 SaveEngineSnapshotToList();
11624 GameActions_CheckSaveEngineSnapshot();
11627 void GameActions_EM_Main()
11629 byte effective_action[MAX_PLAYERS];
11630 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11633 for (i = 0; i < MAX_PLAYERS; i++)
11634 effective_action[i] = stored_player[i].effective_action;
11636 GameActions_EM(effective_action, warp_mode);
11639 void GameActions_SP_Main()
11641 byte effective_action[MAX_PLAYERS];
11642 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11645 for (i = 0; i < MAX_PLAYERS; i++)
11646 effective_action[i] = stored_player[i].effective_action;
11648 GameActions_SP(effective_action, warp_mode);
11650 for (i = 0; i < MAX_PLAYERS; i++)
11652 if (stored_player[i].force_dropping)
11653 stored_player[i].action |= KEY_BUTTON_DROP;
11655 stored_player[i].force_dropping = FALSE;
11659 void GameActions_MM_Main()
11661 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11663 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11666 void GameActions_RND_Main()
11671 void GameActions_RND()
11673 int magic_wall_x = 0, magic_wall_y = 0;
11674 int i, x, y, element, graphic, last_gfx_frame;
11676 InitPlayfieldScanModeVars();
11678 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11680 SCAN_PLAYFIELD(x, y)
11682 ChangeCount[x][y] = 0;
11683 ChangeEvent[x][y] = -1;
11687 if (game.set_centered_player)
11689 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11691 /* switching to "all players" only possible if all players fit to screen */
11692 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11694 game.centered_player_nr_next = game.centered_player_nr;
11695 game.set_centered_player = FALSE;
11698 /* do not switch focus to non-existing (or non-active) player */
11699 if (game.centered_player_nr_next >= 0 &&
11700 !stored_player[game.centered_player_nr_next].active)
11702 game.centered_player_nr_next = game.centered_player_nr;
11703 game.set_centered_player = FALSE;
11707 if (game.set_centered_player &&
11708 ScreenMovPos == 0) /* screen currently aligned at tile position */
11712 if (game.centered_player_nr_next == -1)
11714 setScreenCenteredToAllPlayers(&sx, &sy);
11718 sx = stored_player[game.centered_player_nr_next].jx;
11719 sy = stored_player[game.centered_player_nr_next].jy;
11722 game.centered_player_nr = game.centered_player_nr_next;
11723 game.set_centered_player = FALSE;
11725 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11726 DrawGameDoorValues();
11729 for (i = 0; i < MAX_PLAYERS; i++)
11731 int actual_player_action = stored_player[i].effective_action;
11734 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11735 - rnd_equinox_tetrachloride 048
11736 - rnd_equinox_tetrachloride_ii 096
11737 - rnd_emanuel_schmieg 002
11738 - doctor_sloan_ww 001, 020
11740 if (stored_player[i].MovPos == 0)
11741 CheckGravityMovement(&stored_player[i]);
11744 /* overwrite programmed action with tape action */
11745 if (stored_player[i].programmed_action)
11746 actual_player_action = stored_player[i].programmed_action;
11748 PlayerActions(&stored_player[i], actual_player_action);
11750 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11753 ScrollScreen(NULL, SCROLL_GO_ON);
11755 /* for backwards compatibility, the following code emulates a fixed bug that
11756 occured when pushing elements (causing elements that just made their last
11757 pushing step to already (if possible) make their first falling step in the
11758 same game frame, which is bad); this code is also needed to use the famous
11759 "spring push bug" which is used in older levels and might be wanted to be
11760 used also in newer levels, but in this case the buggy pushing code is only
11761 affecting the "spring" element and no other elements */
11763 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11765 for (i = 0; i < MAX_PLAYERS; i++)
11767 struct PlayerInfo *player = &stored_player[i];
11768 int x = player->jx;
11769 int y = player->jy;
11771 if (player->active && player->is_pushing && player->is_moving &&
11773 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11774 Feld[x][y] == EL_SPRING))
11776 ContinueMoving(x, y);
11778 /* continue moving after pushing (this is actually a bug) */
11779 if (!IS_MOVING(x, y))
11780 Stop[x][y] = FALSE;
11785 SCAN_PLAYFIELD(x, y)
11787 ChangeCount[x][y] = 0;
11788 ChangeEvent[x][y] = -1;
11790 /* this must be handled before main playfield loop */
11791 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11794 if (MovDelay[x][y] <= 0)
11798 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11801 if (MovDelay[x][y] <= 0)
11804 TEST_DrawLevelField(x, y);
11806 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11811 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11813 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11814 printf("GameActions(): This should never happen!\n");
11816 ChangePage[x][y] = -1;
11820 Stop[x][y] = FALSE;
11821 if (WasJustMoving[x][y] > 0)
11822 WasJustMoving[x][y]--;
11823 if (WasJustFalling[x][y] > 0)
11824 WasJustFalling[x][y]--;
11825 if (CheckCollision[x][y] > 0)
11826 CheckCollision[x][y]--;
11827 if (CheckImpact[x][y] > 0)
11828 CheckImpact[x][y]--;
11832 /* reset finished pushing action (not done in ContinueMoving() to allow
11833 continuous pushing animation for elements with zero push delay) */
11834 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11836 ResetGfxAnimation(x, y);
11837 TEST_DrawLevelField(x, y);
11841 if (IS_BLOCKED(x, y))
11845 Blocked2Moving(x, y, &oldx, &oldy);
11846 if (!IS_MOVING(oldx, oldy))
11848 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11849 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11850 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11851 printf("GameActions(): This should never happen!\n");
11857 SCAN_PLAYFIELD(x, y)
11859 element = Feld[x][y];
11860 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11861 last_gfx_frame = GfxFrame[x][y];
11863 ResetGfxFrame(x, y);
11865 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11866 DrawLevelGraphicAnimation(x, y, graphic);
11868 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11869 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11870 ResetRandomAnimationValue(x, y);
11872 SetRandomAnimationValue(x, y);
11874 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11876 if (IS_INACTIVE(element))
11878 if (IS_ANIMATED(graphic))
11879 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11884 /* this may take place after moving, so 'element' may have changed */
11885 if (IS_CHANGING(x, y) &&
11886 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11888 int page = element_info[element].event_page_nr[CE_DELAY];
11890 HandleElementChange(x, y, page);
11892 element = Feld[x][y];
11893 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11896 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11900 element = Feld[x][y];
11901 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11903 if (IS_ANIMATED(graphic) &&
11904 !IS_MOVING(x, y) &&
11906 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11908 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11909 TEST_DrawTwinkleOnField(x, y);
11911 else if (element == EL_ACID)
11914 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11916 else if ((element == EL_EXIT_OPEN ||
11917 element == EL_EM_EXIT_OPEN ||
11918 element == EL_SP_EXIT_OPEN ||
11919 element == EL_STEEL_EXIT_OPEN ||
11920 element == EL_EM_STEEL_EXIT_OPEN ||
11921 element == EL_SP_TERMINAL ||
11922 element == EL_SP_TERMINAL_ACTIVE ||
11923 element == EL_EXTRA_TIME ||
11924 element == EL_SHIELD_NORMAL ||
11925 element == EL_SHIELD_DEADLY) &&
11926 IS_ANIMATED(graphic))
11927 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11928 else if (IS_MOVING(x, y))
11929 ContinueMoving(x, y);
11930 else if (IS_ACTIVE_BOMB(element))
11931 CheckDynamite(x, y);
11932 else if (element == EL_AMOEBA_GROWING)
11933 AmoebeWaechst(x, y);
11934 else if (element == EL_AMOEBA_SHRINKING)
11935 AmoebaDisappearing(x, y);
11937 #if !USE_NEW_AMOEBA_CODE
11938 else if (IS_AMOEBALIVE(element))
11939 AmoebeAbleger(x, y);
11942 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11944 else if (element == EL_EXIT_CLOSED)
11946 else if (element == EL_EM_EXIT_CLOSED)
11948 else if (element == EL_STEEL_EXIT_CLOSED)
11949 CheckExitSteel(x, y);
11950 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11951 CheckExitSteelEM(x, y);
11952 else if (element == EL_SP_EXIT_CLOSED)
11954 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11955 element == EL_EXPANDABLE_STEELWALL_GROWING)
11956 MauerWaechst(x, y);
11957 else if (element == EL_EXPANDABLE_WALL ||
11958 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11959 element == EL_EXPANDABLE_WALL_VERTICAL ||
11960 element == EL_EXPANDABLE_WALL_ANY ||
11961 element == EL_BD_EXPANDABLE_WALL)
11962 MauerAbleger(x, y);
11963 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11964 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11965 element == EL_EXPANDABLE_STEELWALL_ANY)
11966 MauerAblegerStahl(x, y);
11967 else if (element == EL_FLAMES)
11968 CheckForDragon(x, y);
11969 else if (element == EL_EXPLOSION)
11970 ; /* drawing of correct explosion animation is handled separately */
11971 else if (element == EL_ELEMENT_SNAPPING ||
11972 element == EL_DIAGONAL_SHRINKING ||
11973 element == EL_DIAGONAL_GROWING)
11975 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11977 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11979 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11980 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11982 if (IS_BELT_ACTIVE(element))
11983 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11985 if (game.magic_wall_active)
11987 int jx = local_player->jx, jy = local_player->jy;
11989 /* play the element sound at the position nearest to the player */
11990 if ((element == EL_MAGIC_WALL_FULL ||
11991 element == EL_MAGIC_WALL_ACTIVE ||
11992 element == EL_MAGIC_WALL_EMPTYING ||
11993 element == EL_BD_MAGIC_WALL_FULL ||
11994 element == EL_BD_MAGIC_WALL_ACTIVE ||
11995 element == EL_BD_MAGIC_WALL_EMPTYING ||
11996 element == EL_DC_MAGIC_WALL_FULL ||
11997 element == EL_DC_MAGIC_WALL_ACTIVE ||
11998 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11999 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12007 #if USE_NEW_AMOEBA_CODE
12008 /* new experimental amoeba growth stuff */
12009 if (!(FrameCounter % 8))
12011 static unsigned int random = 1684108901;
12013 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12015 x = RND(lev_fieldx);
12016 y = RND(lev_fieldy);
12017 element = Feld[x][y];
12019 if (!IS_PLAYER(x,y) &&
12020 (element == EL_EMPTY ||
12021 CAN_GROW_INTO(element) ||
12022 element == EL_QUICKSAND_EMPTY ||
12023 element == EL_QUICKSAND_FAST_EMPTY ||
12024 element == EL_ACID_SPLASH_LEFT ||
12025 element == EL_ACID_SPLASH_RIGHT))
12027 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12028 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12029 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12030 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12031 Feld[x][y] = EL_AMOEBA_DROP;
12034 random = random * 129 + 1;
12039 game.explosions_delayed = FALSE;
12041 SCAN_PLAYFIELD(x, y)
12043 element = Feld[x][y];
12045 if (ExplodeField[x][y])
12046 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12047 else if (element == EL_EXPLOSION)
12048 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12050 ExplodeField[x][y] = EX_TYPE_NONE;
12053 game.explosions_delayed = TRUE;
12055 if (game.magic_wall_active)
12057 if (!(game.magic_wall_time_left % 4))
12059 int element = Feld[magic_wall_x][magic_wall_y];
12061 if (element == EL_BD_MAGIC_WALL_FULL ||
12062 element == EL_BD_MAGIC_WALL_ACTIVE ||
12063 element == EL_BD_MAGIC_WALL_EMPTYING)
12064 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12065 else if (element == EL_DC_MAGIC_WALL_FULL ||
12066 element == EL_DC_MAGIC_WALL_ACTIVE ||
12067 element == EL_DC_MAGIC_WALL_EMPTYING)
12068 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12070 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12073 if (game.magic_wall_time_left > 0)
12075 game.magic_wall_time_left--;
12077 if (!game.magic_wall_time_left)
12079 SCAN_PLAYFIELD(x, y)
12081 element = Feld[x][y];
12083 if (element == EL_MAGIC_WALL_ACTIVE ||
12084 element == EL_MAGIC_WALL_FULL)
12086 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12087 TEST_DrawLevelField(x, y);
12089 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12090 element == EL_BD_MAGIC_WALL_FULL)
12092 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12093 TEST_DrawLevelField(x, y);
12095 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12096 element == EL_DC_MAGIC_WALL_FULL)
12098 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12099 TEST_DrawLevelField(x, y);
12103 game.magic_wall_active = FALSE;
12108 if (game.light_time_left > 0)
12110 game.light_time_left--;
12112 if (game.light_time_left == 0)
12113 RedrawAllLightSwitchesAndInvisibleElements();
12116 if (game.timegate_time_left > 0)
12118 game.timegate_time_left--;
12120 if (game.timegate_time_left == 0)
12121 CloseAllOpenTimegates();
12124 if (game.lenses_time_left > 0)
12126 game.lenses_time_left--;
12128 if (game.lenses_time_left == 0)
12129 RedrawAllInvisibleElementsForLenses();
12132 if (game.magnify_time_left > 0)
12134 game.magnify_time_left--;
12136 if (game.magnify_time_left == 0)
12137 RedrawAllInvisibleElementsForMagnifier();
12140 for (i = 0; i < MAX_PLAYERS; i++)
12142 struct PlayerInfo *player = &stored_player[i];
12144 if (SHIELD_ON(player))
12146 if (player->shield_deadly_time_left)
12147 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12148 else if (player->shield_normal_time_left)
12149 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12153 #if USE_DELAYED_GFX_REDRAW
12154 SCAN_PLAYFIELD(x, y)
12156 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12158 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12159 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12161 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12162 DrawLevelField(x, y);
12164 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12165 DrawLevelFieldCrumbled(x, y);
12167 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12168 DrawLevelFieldCrumbledNeighbours(x, y);
12170 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12171 DrawTwinkleOnField(x, y);
12174 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12179 PlayAllPlayersSound();
12181 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12183 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12185 local_player->show_envelope = 0;
12188 /* use random number generator in every frame to make it less predictable */
12189 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12193 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12195 int min_x = x, min_y = y, max_x = x, max_y = y;
12198 for (i = 0; i < MAX_PLAYERS; i++)
12200 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12202 if (!stored_player[i].active || &stored_player[i] == player)
12205 min_x = MIN(min_x, jx);
12206 min_y = MIN(min_y, jy);
12207 max_x = MAX(max_x, jx);
12208 max_y = MAX(max_y, jy);
12211 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12214 static boolean AllPlayersInVisibleScreen()
12218 for (i = 0; i < MAX_PLAYERS; i++)
12220 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12222 if (!stored_player[i].active)
12225 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12232 void ScrollLevel(int dx, int dy)
12234 int scroll_offset = 2 * TILEX_VAR;
12237 BlitBitmap(drawto_field, drawto_field,
12238 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12239 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12240 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12241 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12242 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12243 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12247 x = (dx == 1 ? BX1 : BX2);
12248 for (y = BY1; y <= BY2; y++)
12249 DrawScreenField(x, y);
12254 y = (dy == 1 ? BY1 : BY2);
12255 for (x = BX1; x <= BX2; x++)
12256 DrawScreenField(x, y);
12259 redraw_mask |= REDRAW_FIELD;
12262 static boolean canFallDown(struct PlayerInfo *player)
12264 int jx = player->jx, jy = player->jy;
12266 return (IN_LEV_FIELD(jx, jy + 1) &&
12267 (IS_FREE(jx, jy + 1) ||
12268 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12269 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12270 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12273 static boolean canPassField(int x, int y, int move_dir)
12275 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12276 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12277 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12278 int nextx = x + dx;
12279 int nexty = y + dy;
12280 int element = Feld[x][y];
12282 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12283 !CAN_MOVE(element) &&
12284 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12285 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12286 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12289 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12291 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12292 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12293 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12297 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12298 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12299 (IS_DIGGABLE(Feld[newx][newy]) ||
12300 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12301 canPassField(newx, newy, move_dir)));
12304 static void CheckGravityMovement(struct PlayerInfo *player)
12306 if (player->gravity && !player->programmed_action)
12308 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12309 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12310 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12311 int jx = player->jx, jy = player->jy;
12312 boolean player_is_moving_to_valid_field =
12313 (!player_is_snapping &&
12314 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12315 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12316 boolean player_can_fall_down = canFallDown(player);
12318 if (player_can_fall_down &&
12319 !player_is_moving_to_valid_field)
12320 player->programmed_action = MV_DOWN;
12324 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12326 return CheckGravityMovement(player);
12328 if (player->gravity && !player->programmed_action)
12330 int jx = player->jx, jy = player->jy;
12331 boolean field_under_player_is_free =
12332 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12333 boolean player_is_standing_on_valid_field =
12334 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12335 (IS_WALKABLE(Feld[jx][jy]) &&
12336 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12338 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12339 player->programmed_action = MV_DOWN;
12344 MovePlayerOneStep()
12345 -----------------------------------------------------------------------------
12346 dx, dy: direction (non-diagonal) to try to move the player to
12347 real_dx, real_dy: direction as read from input device (can be diagonal)
12350 boolean MovePlayerOneStep(struct PlayerInfo *player,
12351 int dx, int dy, int real_dx, int real_dy)
12353 int jx = player->jx, jy = player->jy;
12354 int new_jx = jx + dx, new_jy = jy + dy;
12356 boolean player_can_move = !player->cannot_move;
12358 if (!player->active || (!dx && !dy))
12359 return MP_NO_ACTION;
12361 player->MovDir = (dx < 0 ? MV_LEFT :
12362 dx > 0 ? MV_RIGHT :
12364 dy > 0 ? MV_DOWN : MV_NONE);
12366 if (!IN_LEV_FIELD(new_jx, new_jy))
12367 return MP_NO_ACTION;
12369 if (!player_can_move)
12371 if (player->MovPos == 0)
12373 player->is_moving = FALSE;
12374 player->is_digging = FALSE;
12375 player->is_collecting = FALSE;
12376 player->is_snapping = FALSE;
12377 player->is_pushing = FALSE;
12381 if (!network.enabled && game.centered_player_nr == -1 &&
12382 !AllPlayersInSight(player, new_jx, new_jy))
12383 return MP_NO_ACTION;
12385 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12386 if (can_move != MP_MOVING)
12389 /* check if DigField() has caused relocation of the player */
12390 if (player->jx != jx || player->jy != jy)
12391 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12393 StorePlayer[jx][jy] = 0;
12394 player->last_jx = jx;
12395 player->last_jy = jy;
12396 player->jx = new_jx;
12397 player->jy = new_jy;
12398 StorePlayer[new_jx][new_jy] = player->element_nr;
12400 if (player->move_delay_value_next != -1)
12402 player->move_delay_value = player->move_delay_value_next;
12403 player->move_delay_value_next = -1;
12407 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12409 player->step_counter++;
12411 PlayerVisit[jx][jy] = FrameCounter;
12413 player->is_moving = TRUE;
12416 /* should better be called in MovePlayer(), but this breaks some tapes */
12417 ScrollPlayer(player, SCROLL_INIT);
12423 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12425 int jx = player->jx, jy = player->jy;
12426 int old_jx = jx, old_jy = jy;
12427 int moved = MP_NO_ACTION;
12429 if (!player->active)
12434 if (player->MovPos == 0)
12436 player->is_moving = FALSE;
12437 player->is_digging = FALSE;
12438 player->is_collecting = FALSE;
12439 player->is_snapping = FALSE;
12440 player->is_pushing = FALSE;
12446 if (player->move_delay > 0)
12449 player->move_delay = -1; /* set to "uninitialized" value */
12451 /* store if player is automatically moved to next field */
12452 player->is_auto_moving = (player->programmed_action != MV_NONE);
12454 /* remove the last programmed player action */
12455 player->programmed_action = 0;
12457 if (player->MovPos)
12459 /* should only happen if pre-1.2 tape recordings are played */
12460 /* this is only for backward compatibility */
12462 int original_move_delay_value = player->move_delay_value;
12465 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12469 /* scroll remaining steps with finest movement resolution */
12470 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12472 while (player->MovPos)
12474 ScrollPlayer(player, SCROLL_GO_ON);
12475 ScrollScreen(NULL, SCROLL_GO_ON);
12477 AdvanceFrameAndPlayerCounters(player->index_nr);
12480 BackToFront_WithFrameDelay(0);
12483 player->move_delay_value = original_move_delay_value;
12486 player->is_active = FALSE;
12488 if (player->last_move_dir & MV_HORIZONTAL)
12490 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12491 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12495 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12496 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12499 if (!moved && !player->is_active)
12501 player->is_moving = FALSE;
12502 player->is_digging = FALSE;
12503 player->is_collecting = FALSE;
12504 player->is_snapping = FALSE;
12505 player->is_pushing = FALSE;
12511 if (moved & MP_MOVING && !ScreenMovPos &&
12512 (player->index_nr == game.centered_player_nr ||
12513 game.centered_player_nr == -1))
12515 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12516 int offset = game.scroll_delay_value;
12518 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12520 /* actual player has left the screen -- scroll in that direction */
12521 if (jx != old_jx) /* player has moved horizontally */
12522 scroll_x += (jx - old_jx);
12523 else /* player has moved vertically */
12524 scroll_y += (jy - old_jy);
12528 if (jx != old_jx) /* player has moved horizontally */
12530 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12531 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12532 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12534 /* don't scroll over playfield boundaries */
12535 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12536 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12538 /* don't scroll more than one field at a time */
12539 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12541 /* don't scroll against the player's moving direction */
12542 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12543 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12544 scroll_x = old_scroll_x;
12546 else /* player has moved vertically */
12548 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12549 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12550 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12552 /* don't scroll over playfield boundaries */
12553 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12554 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12556 /* don't scroll more than one field at a time */
12557 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12559 /* don't scroll against the player's moving direction */
12560 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12561 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12562 scroll_y = old_scroll_y;
12566 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12568 if (!network.enabled && game.centered_player_nr == -1 &&
12569 !AllPlayersInVisibleScreen())
12571 scroll_x = old_scroll_x;
12572 scroll_y = old_scroll_y;
12576 ScrollScreen(player, SCROLL_INIT);
12577 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12582 player->StepFrame = 0;
12584 if (moved & MP_MOVING)
12586 if (old_jx != jx && old_jy == jy)
12587 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12588 else if (old_jx == jx && old_jy != jy)
12589 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12591 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12593 player->last_move_dir = player->MovDir;
12594 player->is_moving = TRUE;
12595 player->is_snapping = FALSE;
12596 player->is_switching = FALSE;
12597 player->is_dropping = FALSE;
12598 player->is_dropping_pressed = FALSE;
12599 player->drop_pressed_delay = 0;
12602 /* should better be called here than above, but this breaks some tapes */
12603 ScrollPlayer(player, SCROLL_INIT);
12608 CheckGravityMovementWhenNotMoving(player);
12610 player->is_moving = FALSE;
12612 /* at this point, the player is allowed to move, but cannot move right now
12613 (e.g. because of something blocking the way) -- ensure that the player
12614 is also allowed to move in the next frame (in old versions before 3.1.1,
12615 the player was forced to wait again for eight frames before next try) */
12617 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12618 player->move_delay = 0; /* allow direct movement in the next frame */
12621 if (player->move_delay == -1) /* not yet initialized by DigField() */
12622 player->move_delay = player->move_delay_value;
12624 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12626 TestIfPlayerTouchesBadThing(jx, jy);
12627 TestIfPlayerTouchesCustomElement(jx, jy);
12630 if (!player->active)
12631 RemovePlayer(player);
12636 void ScrollPlayer(struct PlayerInfo *player, int mode)
12638 int jx = player->jx, jy = player->jy;
12639 int last_jx = player->last_jx, last_jy = player->last_jy;
12640 int move_stepsize = TILEX / player->move_delay_value;
12642 if (!player->active)
12645 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12648 if (mode == SCROLL_INIT)
12650 player->actual_frame_counter = FrameCounter;
12651 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12653 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12654 Feld[last_jx][last_jy] == EL_EMPTY)
12656 int last_field_block_delay = 0; /* start with no blocking at all */
12657 int block_delay_adjustment = player->block_delay_adjustment;
12659 /* if player blocks last field, add delay for exactly one move */
12660 if (player->block_last_field)
12662 last_field_block_delay += player->move_delay_value;
12664 /* when blocking enabled, prevent moving up despite gravity */
12665 if (player->gravity && player->MovDir == MV_UP)
12666 block_delay_adjustment = -1;
12669 /* add block delay adjustment (also possible when not blocking) */
12670 last_field_block_delay += block_delay_adjustment;
12672 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12673 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12676 if (player->MovPos != 0) /* player has not yet reached destination */
12679 else if (!FrameReached(&player->actual_frame_counter, 1))
12682 if (player->MovPos != 0)
12684 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12685 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12687 /* before DrawPlayer() to draw correct player graphic for this case */
12688 if (player->MovPos == 0)
12689 CheckGravityMovement(player);
12692 if (player->MovPos == 0) /* player reached destination field */
12694 if (player->move_delay_reset_counter > 0)
12696 player->move_delay_reset_counter--;
12698 if (player->move_delay_reset_counter == 0)
12700 /* continue with normal speed after quickly moving through gate */
12701 HALVE_PLAYER_SPEED(player);
12703 /* be able to make the next move without delay */
12704 player->move_delay = 0;
12708 player->last_jx = jx;
12709 player->last_jy = jy;
12711 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12712 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12713 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12714 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12715 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12716 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12717 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12718 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12720 DrawPlayer(player); /* needed here only to cleanup last field */
12721 RemovePlayer(player);
12723 if (local_player->friends_still_needed == 0 ||
12724 IS_SP_ELEMENT(Feld[jx][jy]))
12725 PlayerWins(player);
12728 /* this breaks one level: "machine", level 000 */
12730 int move_direction = player->MovDir;
12731 int enter_side = MV_DIR_OPPOSITE(move_direction);
12732 int leave_side = move_direction;
12733 int old_jx = last_jx;
12734 int old_jy = last_jy;
12735 int old_element = Feld[old_jx][old_jy];
12736 int new_element = Feld[jx][jy];
12738 if (IS_CUSTOM_ELEMENT(old_element))
12739 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12741 player->index_bit, leave_side);
12743 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12744 CE_PLAYER_LEAVES_X,
12745 player->index_bit, leave_side);
12747 if (IS_CUSTOM_ELEMENT(new_element))
12748 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12749 player->index_bit, enter_side);
12751 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12752 CE_PLAYER_ENTERS_X,
12753 player->index_bit, enter_side);
12755 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12756 CE_MOVE_OF_X, move_direction);
12759 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12761 TestIfPlayerTouchesBadThing(jx, jy);
12762 TestIfPlayerTouchesCustomElement(jx, jy);
12764 /* needed because pushed element has not yet reached its destination,
12765 so it would trigger a change event at its previous field location */
12766 if (!player->is_pushing)
12767 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12769 if (!player->active)
12770 RemovePlayer(player);
12773 if (!local_player->LevelSolved && level.use_step_counter)
12783 if (TimeLeft <= 10 && setup.time_limit)
12784 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12786 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12788 DisplayGameControlValues();
12790 if (!TimeLeft && setup.time_limit)
12791 for (i = 0; i < MAX_PLAYERS; i++)
12792 KillPlayer(&stored_player[i]);
12794 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12796 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12798 DisplayGameControlValues();
12802 if (tape.single_step && tape.recording && !tape.pausing &&
12803 !player->programmed_action)
12804 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12806 if (!player->programmed_action)
12807 CheckSaveEngineSnapshot(player);
12811 void ScrollScreen(struct PlayerInfo *player, int mode)
12813 static unsigned int screen_frame_counter = 0;
12815 if (mode == SCROLL_INIT)
12817 /* set scrolling step size according to actual player's moving speed */
12818 ScrollStepSize = TILEX / player->move_delay_value;
12820 screen_frame_counter = FrameCounter;
12821 ScreenMovDir = player->MovDir;
12822 ScreenMovPos = player->MovPos;
12823 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12826 else if (!FrameReached(&screen_frame_counter, 1))
12831 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12832 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12833 redraw_mask |= REDRAW_FIELD;
12836 ScreenMovDir = MV_NONE;
12839 void TestIfPlayerTouchesCustomElement(int x, int y)
12841 static int xy[4][2] =
12848 static int trigger_sides[4][2] =
12850 /* center side border side */
12851 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12852 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12853 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12854 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12856 static int touch_dir[4] =
12858 MV_LEFT | MV_RIGHT,
12863 int center_element = Feld[x][y]; /* should always be non-moving! */
12866 for (i = 0; i < NUM_DIRECTIONS; i++)
12868 int xx = x + xy[i][0];
12869 int yy = y + xy[i][1];
12870 int center_side = trigger_sides[i][0];
12871 int border_side = trigger_sides[i][1];
12872 int border_element;
12874 if (!IN_LEV_FIELD(xx, yy))
12877 if (IS_PLAYER(x, y)) /* player found at center element */
12879 struct PlayerInfo *player = PLAYERINFO(x, y);
12881 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12882 border_element = Feld[xx][yy]; /* may be moving! */
12883 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12884 border_element = Feld[xx][yy];
12885 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12886 border_element = MovingOrBlocked2Element(xx, yy);
12888 continue; /* center and border element do not touch */
12890 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12891 player->index_bit, border_side);
12892 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12893 CE_PLAYER_TOUCHES_X,
12894 player->index_bit, border_side);
12897 /* use player element that is initially defined in the level playfield,
12898 not the player element that corresponds to the runtime player number
12899 (example: a level that contains EL_PLAYER_3 as the only player would
12900 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12901 int player_element = PLAYERINFO(x, y)->initial_element;
12903 CheckElementChangeBySide(xx, yy, border_element, player_element,
12904 CE_TOUCHING_X, border_side);
12907 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12909 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12911 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12913 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12914 continue; /* center and border element do not touch */
12917 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12918 player->index_bit, center_side);
12919 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12920 CE_PLAYER_TOUCHES_X,
12921 player->index_bit, center_side);
12924 /* use player element that is initially defined in the level playfield,
12925 not the player element that corresponds to the runtime player number
12926 (example: a level that contains EL_PLAYER_3 as the only player would
12927 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12928 int player_element = PLAYERINFO(xx, yy)->initial_element;
12930 CheckElementChangeBySide(x, y, center_element, player_element,
12931 CE_TOUCHING_X, center_side);
12939 void TestIfElementTouchesCustomElement(int x, int y)
12941 static int xy[4][2] =
12948 static int trigger_sides[4][2] =
12950 /* center side border side */
12951 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12952 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12953 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12954 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12956 static int touch_dir[4] =
12958 MV_LEFT | MV_RIGHT,
12963 boolean change_center_element = FALSE;
12964 int center_element = Feld[x][y]; /* should always be non-moving! */
12965 int border_element_old[NUM_DIRECTIONS];
12968 for (i = 0; i < NUM_DIRECTIONS; i++)
12970 int xx = x + xy[i][0];
12971 int yy = y + xy[i][1];
12972 int border_element;
12974 border_element_old[i] = -1;
12976 if (!IN_LEV_FIELD(xx, yy))
12979 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12980 border_element = Feld[xx][yy]; /* may be moving! */
12981 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12982 border_element = Feld[xx][yy];
12983 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12984 border_element = MovingOrBlocked2Element(xx, yy);
12986 continue; /* center and border element do not touch */
12988 border_element_old[i] = border_element;
12991 for (i = 0; i < NUM_DIRECTIONS; i++)
12993 int xx = x + xy[i][0];
12994 int yy = y + xy[i][1];
12995 int center_side = trigger_sides[i][0];
12996 int border_element = border_element_old[i];
12998 if (border_element == -1)
13001 /* check for change of border element */
13002 CheckElementChangeBySide(xx, yy, border_element, center_element,
13003 CE_TOUCHING_X, center_side);
13005 /* (center element cannot be player, so we dont have to check this here) */
13008 for (i = 0; i < NUM_DIRECTIONS; i++)
13010 int xx = x + xy[i][0];
13011 int yy = y + xy[i][1];
13012 int border_side = trigger_sides[i][1];
13013 int border_element = border_element_old[i];
13015 if (border_element == -1)
13018 /* check for change of center element (but change it only once) */
13019 if (!change_center_element)
13020 change_center_element =
13021 CheckElementChangeBySide(x, y, center_element, border_element,
13022 CE_TOUCHING_X, border_side);
13024 if (IS_PLAYER(xx, yy))
13026 /* use player element that is initially defined in the level playfield,
13027 not the player element that corresponds to the runtime player number
13028 (example: a level that contains EL_PLAYER_3 as the only player would
13029 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13030 int player_element = PLAYERINFO(xx, yy)->initial_element;
13032 CheckElementChangeBySide(x, y, center_element, player_element,
13033 CE_TOUCHING_X, border_side);
13038 void TestIfElementHitsCustomElement(int x, int y, int direction)
13040 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13041 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13042 int hitx = x + dx, hity = y + dy;
13043 int hitting_element = Feld[x][y];
13044 int touched_element;
13046 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13049 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13050 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13052 if (IN_LEV_FIELD(hitx, hity))
13054 int opposite_direction = MV_DIR_OPPOSITE(direction);
13055 int hitting_side = direction;
13056 int touched_side = opposite_direction;
13057 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13058 MovDir[hitx][hity] != direction ||
13059 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13065 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13066 CE_HITTING_X, touched_side);
13068 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13069 CE_HIT_BY_X, hitting_side);
13071 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13072 CE_HIT_BY_SOMETHING, opposite_direction);
13074 if (IS_PLAYER(hitx, hity))
13076 /* use player element that is initially defined in the level playfield,
13077 not the player element that corresponds to the runtime player number
13078 (example: a level that contains EL_PLAYER_3 as the only player would
13079 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13080 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13082 CheckElementChangeBySide(x, y, hitting_element, player_element,
13083 CE_HITTING_X, touched_side);
13088 /* "hitting something" is also true when hitting the playfield border */
13089 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13090 CE_HITTING_SOMETHING, direction);
13093 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13095 int i, kill_x = -1, kill_y = -1;
13097 int bad_element = -1;
13098 static int test_xy[4][2] =
13105 static int test_dir[4] =
13113 for (i = 0; i < NUM_DIRECTIONS; i++)
13115 int test_x, test_y, test_move_dir, test_element;
13117 test_x = good_x + test_xy[i][0];
13118 test_y = good_y + test_xy[i][1];
13120 if (!IN_LEV_FIELD(test_x, test_y))
13124 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13126 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13128 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13129 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13131 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13132 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13136 bad_element = test_element;
13142 if (kill_x != -1 || kill_y != -1)
13144 if (IS_PLAYER(good_x, good_y))
13146 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13148 if (player->shield_deadly_time_left > 0 &&
13149 !IS_INDESTRUCTIBLE(bad_element))
13150 Bang(kill_x, kill_y);
13151 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13152 KillPlayer(player);
13155 Bang(good_x, good_y);
13159 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13161 int i, kill_x = -1, kill_y = -1;
13162 int bad_element = Feld[bad_x][bad_y];
13163 static int test_xy[4][2] =
13170 static int touch_dir[4] =
13172 MV_LEFT | MV_RIGHT,
13177 static int test_dir[4] =
13185 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13188 for (i = 0; i < NUM_DIRECTIONS; i++)
13190 int test_x, test_y, test_move_dir, test_element;
13192 test_x = bad_x + test_xy[i][0];
13193 test_y = bad_y + test_xy[i][1];
13195 if (!IN_LEV_FIELD(test_x, test_y))
13199 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13201 test_element = Feld[test_x][test_y];
13203 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13204 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13206 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13207 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13209 /* good thing is player or penguin that does not move away */
13210 if (IS_PLAYER(test_x, test_y))
13212 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13214 if (bad_element == EL_ROBOT && player->is_moving)
13215 continue; /* robot does not kill player if he is moving */
13217 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13219 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13220 continue; /* center and border element do not touch */
13228 else if (test_element == EL_PENGUIN)
13238 if (kill_x != -1 || kill_y != -1)
13240 if (IS_PLAYER(kill_x, kill_y))
13242 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13244 if (player->shield_deadly_time_left > 0 &&
13245 !IS_INDESTRUCTIBLE(bad_element))
13246 Bang(bad_x, bad_y);
13247 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13248 KillPlayer(player);
13251 Bang(kill_x, kill_y);
13255 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13257 int bad_element = Feld[bad_x][bad_y];
13258 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13259 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13260 int test_x = bad_x + dx, test_y = bad_y + dy;
13261 int test_move_dir, test_element;
13262 int kill_x = -1, kill_y = -1;
13264 if (!IN_LEV_FIELD(test_x, test_y))
13268 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13270 test_element = Feld[test_x][test_y];
13272 if (test_move_dir != bad_move_dir)
13274 /* good thing can be player or penguin that does not move away */
13275 if (IS_PLAYER(test_x, test_y))
13277 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13279 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13280 player as being hit when he is moving towards the bad thing, because
13281 the "get hit by" condition would be lost after the player stops) */
13282 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13283 return; /* player moves away from bad thing */
13288 else if (test_element == EL_PENGUIN)
13295 if (kill_x != -1 || kill_y != -1)
13297 if (IS_PLAYER(kill_x, kill_y))
13299 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13301 if (player->shield_deadly_time_left > 0 &&
13302 !IS_INDESTRUCTIBLE(bad_element))
13303 Bang(bad_x, bad_y);
13304 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13305 KillPlayer(player);
13308 Bang(kill_x, kill_y);
13312 void TestIfPlayerTouchesBadThing(int x, int y)
13314 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13317 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13319 TestIfGoodThingHitsBadThing(x, y, move_dir);
13322 void TestIfBadThingTouchesPlayer(int x, int y)
13324 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13327 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13329 TestIfBadThingHitsGoodThing(x, y, move_dir);
13332 void TestIfFriendTouchesBadThing(int x, int y)
13334 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13337 void TestIfBadThingTouchesFriend(int x, int y)
13339 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13342 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13344 int i, kill_x = bad_x, kill_y = bad_y;
13345 static int xy[4][2] =
13353 for (i = 0; i < NUM_DIRECTIONS; i++)
13357 x = bad_x + xy[i][0];
13358 y = bad_y + xy[i][1];
13359 if (!IN_LEV_FIELD(x, y))
13362 element = Feld[x][y];
13363 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13364 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13372 if (kill_x != bad_x || kill_y != bad_y)
13373 Bang(bad_x, bad_y);
13376 void KillPlayer(struct PlayerInfo *player)
13378 int jx = player->jx, jy = player->jy;
13380 if (!player->active)
13384 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13385 player->killed, player->active, player->reanimated);
13388 /* the following code was introduced to prevent an infinite loop when calling
13390 -> CheckTriggeredElementChangeExt()
13391 -> ExecuteCustomElementAction()
13393 -> (infinitely repeating the above sequence of function calls)
13394 which occurs when killing the player while having a CE with the setting
13395 "kill player X when explosion of <player X>"; the solution using a new
13396 field "player->killed" was chosen for backwards compatibility, although
13397 clever use of the fields "player->active" etc. would probably also work */
13399 if (player->killed)
13403 player->killed = TRUE;
13405 /* remove accessible field at the player's position */
13406 Feld[jx][jy] = EL_EMPTY;
13408 /* deactivate shield (else Bang()/Explode() would not work right) */
13409 player->shield_normal_time_left = 0;
13410 player->shield_deadly_time_left = 0;
13413 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13414 player->killed, player->active, player->reanimated);
13420 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13421 player->killed, player->active, player->reanimated);
13424 if (player->reanimated) /* killed player may have been reanimated */
13425 player->killed = player->reanimated = FALSE;
13427 BuryPlayer(player);
13430 static void KillPlayerUnlessEnemyProtected(int x, int y)
13432 if (!PLAYER_ENEMY_PROTECTED(x, y))
13433 KillPlayer(PLAYERINFO(x, y));
13436 static void KillPlayerUnlessExplosionProtected(int x, int y)
13438 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13439 KillPlayer(PLAYERINFO(x, y));
13442 void BuryPlayer(struct PlayerInfo *player)
13444 int jx = player->jx, jy = player->jy;
13446 if (!player->active)
13449 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13450 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13452 player->GameOver = TRUE;
13453 RemovePlayer(player);
13456 void RemovePlayer(struct PlayerInfo *player)
13458 int jx = player->jx, jy = player->jy;
13459 int i, found = FALSE;
13461 player->present = FALSE;
13462 player->active = FALSE;
13464 if (!ExplodeField[jx][jy])
13465 StorePlayer[jx][jy] = 0;
13467 if (player->is_moving)
13468 TEST_DrawLevelField(player->last_jx, player->last_jy);
13470 for (i = 0; i < MAX_PLAYERS; i++)
13471 if (stored_player[i].active)
13475 AllPlayersGone = TRUE;
13481 static void setFieldForSnapping(int x, int y, int element, int direction)
13483 struct ElementInfo *ei = &element_info[element];
13484 int direction_bit = MV_DIR_TO_BIT(direction);
13485 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13486 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13487 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13489 Feld[x][y] = EL_ELEMENT_SNAPPING;
13490 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13492 ResetGfxAnimation(x, y);
13494 GfxElement[x][y] = element;
13495 GfxAction[x][y] = action;
13496 GfxDir[x][y] = direction;
13497 GfxFrame[x][y] = -1;
13501 =============================================================================
13502 checkDiagonalPushing()
13503 -----------------------------------------------------------------------------
13504 check if diagonal input device direction results in pushing of object
13505 (by checking if the alternative direction is walkable, diggable, ...)
13506 =============================================================================
13509 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13510 int x, int y, int real_dx, int real_dy)
13512 int jx, jy, dx, dy, xx, yy;
13514 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13517 /* diagonal direction: check alternative direction */
13522 xx = jx + (dx == 0 ? real_dx : 0);
13523 yy = jy + (dy == 0 ? real_dy : 0);
13525 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13529 =============================================================================
13531 -----------------------------------------------------------------------------
13532 x, y: field next to player (non-diagonal) to try to dig to
13533 real_dx, real_dy: direction as read from input device (can be diagonal)
13534 =============================================================================
13537 static int DigField(struct PlayerInfo *player,
13538 int oldx, int oldy, int x, int y,
13539 int real_dx, int real_dy, int mode)
13541 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13542 boolean player_was_pushing = player->is_pushing;
13543 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13544 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13545 int jx = oldx, jy = oldy;
13546 int dx = x - jx, dy = y - jy;
13547 int nextx = x + dx, nexty = y + dy;
13548 int move_direction = (dx == -1 ? MV_LEFT :
13549 dx == +1 ? MV_RIGHT :
13551 dy == +1 ? MV_DOWN : MV_NONE);
13552 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13553 int dig_side = MV_DIR_OPPOSITE(move_direction);
13554 int old_element = Feld[jx][jy];
13555 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13558 if (is_player) /* function can also be called by EL_PENGUIN */
13560 if (player->MovPos == 0)
13562 player->is_digging = FALSE;
13563 player->is_collecting = FALSE;
13566 if (player->MovPos == 0) /* last pushing move finished */
13567 player->is_pushing = FALSE;
13569 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13571 player->is_switching = FALSE;
13572 player->push_delay = -1;
13574 return MP_NO_ACTION;
13578 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13579 old_element = Back[jx][jy];
13581 /* in case of element dropped at player position, check background */
13582 else if (Back[jx][jy] != EL_EMPTY &&
13583 game.engine_version >= VERSION_IDENT(2,2,0,0))
13584 old_element = Back[jx][jy];
13586 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13587 return MP_NO_ACTION; /* field has no opening in this direction */
13589 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13590 return MP_NO_ACTION; /* field has no opening in this direction */
13592 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13596 Feld[jx][jy] = player->artwork_element;
13597 InitMovingField(jx, jy, MV_DOWN);
13598 Store[jx][jy] = EL_ACID;
13599 ContinueMoving(jx, jy);
13600 BuryPlayer(player);
13602 return MP_DONT_RUN_INTO;
13605 if (player_can_move && DONT_RUN_INTO(element))
13607 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13609 return MP_DONT_RUN_INTO;
13612 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13613 return MP_NO_ACTION;
13615 collect_count = element_info[element].collect_count_initial;
13617 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13618 return MP_NO_ACTION;
13620 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13621 player_can_move = player_can_move_or_snap;
13623 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13624 game.engine_version >= VERSION_IDENT(2,2,0,0))
13626 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13627 player->index_bit, dig_side);
13628 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13629 player->index_bit, dig_side);
13631 if (element == EL_DC_LANDMINE)
13634 if (Feld[x][y] != element) /* field changed by snapping */
13637 return MP_NO_ACTION;
13640 if (player->gravity && is_player && !player->is_auto_moving &&
13641 canFallDown(player) && move_direction != MV_DOWN &&
13642 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13643 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13645 if (player_can_move &&
13646 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13648 int sound_element = SND_ELEMENT(element);
13649 int sound_action = ACTION_WALKING;
13651 if (IS_RND_GATE(element))
13653 if (!player->key[RND_GATE_NR(element)])
13654 return MP_NO_ACTION;
13656 else if (IS_RND_GATE_GRAY(element))
13658 if (!player->key[RND_GATE_GRAY_NR(element)])
13659 return MP_NO_ACTION;
13661 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13663 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13664 return MP_NO_ACTION;
13666 else if (element == EL_EXIT_OPEN ||
13667 element == EL_EM_EXIT_OPEN ||
13668 element == EL_EM_EXIT_OPENING ||
13669 element == EL_STEEL_EXIT_OPEN ||
13670 element == EL_EM_STEEL_EXIT_OPEN ||
13671 element == EL_EM_STEEL_EXIT_OPENING ||
13672 element == EL_SP_EXIT_OPEN ||
13673 element == EL_SP_EXIT_OPENING)
13675 sound_action = ACTION_PASSING; /* player is passing exit */
13677 else if (element == EL_EMPTY)
13679 sound_action = ACTION_MOVING; /* nothing to walk on */
13682 /* play sound from background or player, whatever is available */
13683 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13684 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13686 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13688 else if (player_can_move &&
13689 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13691 if (!ACCESS_FROM(element, opposite_direction))
13692 return MP_NO_ACTION; /* field not accessible from this direction */
13694 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13695 return MP_NO_ACTION;
13697 if (IS_EM_GATE(element))
13699 if (!player->key[EM_GATE_NR(element)])
13700 return MP_NO_ACTION;
13702 else if (IS_EM_GATE_GRAY(element))
13704 if (!player->key[EM_GATE_GRAY_NR(element)])
13705 return MP_NO_ACTION;
13707 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13709 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13710 return MP_NO_ACTION;
13712 else if (IS_EMC_GATE(element))
13714 if (!player->key[EMC_GATE_NR(element)])
13715 return MP_NO_ACTION;
13717 else if (IS_EMC_GATE_GRAY(element))
13719 if (!player->key[EMC_GATE_GRAY_NR(element)])
13720 return MP_NO_ACTION;
13722 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13724 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13725 return MP_NO_ACTION;
13727 else if (element == EL_DC_GATE_WHITE ||
13728 element == EL_DC_GATE_WHITE_GRAY ||
13729 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13731 if (player->num_white_keys == 0)
13732 return MP_NO_ACTION;
13734 player->num_white_keys--;
13736 else if (IS_SP_PORT(element))
13738 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13739 element == EL_SP_GRAVITY_PORT_RIGHT ||
13740 element == EL_SP_GRAVITY_PORT_UP ||
13741 element == EL_SP_GRAVITY_PORT_DOWN)
13742 player->gravity = !player->gravity;
13743 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13744 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13745 element == EL_SP_GRAVITY_ON_PORT_UP ||
13746 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13747 player->gravity = TRUE;
13748 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13749 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13750 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13751 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13752 player->gravity = FALSE;
13755 /* automatically move to the next field with double speed */
13756 player->programmed_action = move_direction;
13758 if (player->move_delay_reset_counter == 0)
13760 player->move_delay_reset_counter = 2; /* two double speed steps */
13762 DOUBLE_PLAYER_SPEED(player);
13765 PlayLevelSoundAction(x, y, ACTION_PASSING);
13767 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13771 if (mode != DF_SNAP)
13773 GfxElement[x][y] = GFX_ELEMENT(element);
13774 player->is_digging = TRUE;
13777 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13779 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13780 player->index_bit, dig_side);
13782 if (mode == DF_SNAP)
13784 if (level.block_snap_field)
13785 setFieldForSnapping(x, y, element, move_direction);
13787 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13789 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13790 player->index_bit, dig_side);
13793 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13797 if (is_player && mode != DF_SNAP)
13799 GfxElement[x][y] = element;
13800 player->is_collecting = TRUE;
13803 if (element == EL_SPEED_PILL)
13805 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13807 else if (element == EL_EXTRA_TIME && level.time > 0)
13809 TimeLeft += level.extra_time;
13811 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13813 DisplayGameControlValues();
13815 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13817 player->shield_normal_time_left += level.shield_normal_time;
13818 if (element == EL_SHIELD_DEADLY)
13819 player->shield_deadly_time_left += level.shield_deadly_time;
13821 else if (element == EL_DYNAMITE ||
13822 element == EL_EM_DYNAMITE ||
13823 element == EL_SP_DISK_RED)
13825 if (player->inventory_size < MAX_INVENTORY_SIZE)
13826 player->inventory_element[player->inventory_size++] = element;
13828 DrawGameDoorValues();
13830 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13832 player->dynabomb_count++;
13833 player->dynabombs_left++;
13835 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13837 player->dynabomb_size++;
13839 else if (element == EL_DYNABOMB_INCREASE_POWER)
13841 player->dynabomb_xl = TRUE;
13843 else if (IS_KEY(element))
13845 player->key[KEY_NR(element)] = TRUE;
13847 DrawGameDoorValues();
13849 else if (element == EL_DC_KEY_WHITE)
13851 player->num_white_keys++;
13853 /* display white keys? */
13854 /* DrawGameDoorValues(); */
13856 else if (IS_ENVELOPE(element))
13858 player->show_envelope = element;
13860 else if (element == EL_EMC_LENSES)
13862 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13864 RedrawAllInvisibleElementsForLenses();
13866 else if (element == EL_EMC_MAGNIFIER)
13868 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13870 RedrawAllInvisibleElementsForMagnifier();
13872 else if (IS_DROPPABLE(element) ||
13873 IS_THROWABLE(element)) /* can be collected and dropped */
13877 if (collect_count == 0)
13878 player->inventory_infinite_element = element;
13880 for (i = 0; i < collect_count; i++)
13881 if (player->inventory_size < MAX_INVENTORY_SIZE)
13882 player->inventory_element[player->inventory_size++] = element;
13884 DrawGameDoorValues();
13886 else if (collect_count > 0)
13888 local_player->gems_still_needed -= collect_count;
13889 if (local_player->gems_still_needed < 0)
13890 local_player->gems_still_needed = 0;
13892 game.snapshot.collected_item = TRUE;
13894 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13896 DisplayGameControlValues();
13899 RaiseScoreElement(element);
13900 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13903 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13904 player->index_bit, dig_side);
13906 if (mode == DF_SNAP)
13908 if (level.block_snap_field)
13909 setFieldForSnapping(x, y, element, move_direction);
13911 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13913 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13914 player->index_bit, dig_side);
13917 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13919 if (mode == DF_SNAP && element != EL_BD_ROCK)
13920 return MP_NO_ACTION;
13922 if (CAN_FALL(element) && dy)
13923 return MP_NO_ACTION;
13925 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13926 !(element == EL_SPRING && level.use_spring_bug))
13927 return MP_NO_ACTION;
13929 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13930 ((move_direction & MV_VERTICAL &&
13931 ((element_info[element].move_pattern & MV_LEFT &&
13932 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13933 (element_info[element].move_pattern & MV_RIGHT &&
13934 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13935 (move_direction & MV_HORIZONTAL &&
13936 ((element_info[element].move_pattern & MV_UP &&
13937 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13938 (element_info[element].move_pattern & MV_DOWN &&
13939 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13940 return MP_NO_ACTION;
13942 /* do not push elements already moving away faster than player */
13943 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13944 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13945 return MP_NO_ACTION;
13947 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13949 if (player->push_delay_value == -1 || !player_was_pushing)
13950 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13952 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13954 if (player->push_delay_value == -1)
13955 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13957 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13959 if (!player->is_pushing)
13960 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13963 player->is_pushing = TRUE;
13964 player->is_active = TRUE;
13966 if (!(IN_LEV_FIELD(nextx, nexty) &&
13967 (IS_FREE(nextx, nexty) ||
13968 (IS_SB_ELEMENT(element) &&
13969 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13970 (IS_CUSTOM_ELEMENT(element) &&
13971 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13972 return MP_NO_ACTION;
13974 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13975 return MP_NO_ACTION;
13977 if (player->push_delay == -1) /* new pushing; restart delay */
13978 player->push_delay = 0;
13980 if (player->push_delay < player->push_delay_value &&
13981 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13982 element != EL_SPRING && element != EL_BALLOON)
13984 /* make sure that there is no move delay before next try to push */
13985 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13986 player->move_delay = 0;
13988 return MP_NO_ACTION;
13991 if (IS_CUSTOM_ELEMENT(element) &&
13992 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13994 if (!DigFieldByCE(nextx, nexty, element))
13995 return MP_NO_ACTION;
13998 if (IS_SB_ELEMENT(element))
14000 if (element == EL_SOKOBAN_FIELD_FULL)
14002 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14003 local_player->sokobanfields_still_needed++;
14006 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14008 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14009 local_player->sokobanfields_still_needed--;
14012 Feld[x][y] = EL_SOKOBAN_OBJECT;
14014 if (Back[x][y] == Back[nextx][nexty])
14015 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14016 else if (Back[x][y] != 0)
14017 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14020 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14023 if (local_player->sokobanfields_still_needed == 0 &&
14024 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14026 PlayerWins(player);
14028 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14032 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14034 InitMovingField(x, y, move_direction);
14035 GfxAction[x][y] = ACTION_PUSHING;
14037 if (mode == DF_SNAP)
14038 ContinueMoving(x, y);
14040 MovPos[x][y] = (dx != 0 ? dx : dy);
14042 Pushed[x][y] = TRUE;
14043 Pushed[nextx][nexty] = TRUE;
14045 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14046 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14048 player->push_delay_value = -1; /* get new value later */
14050 /* check for element change _after_ element has been pushed */
14051 if (game.use_change_when_pushing_bug)
14053 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14054 player->index_bit, dig_side);
14055 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14056 player->index_bit, dig_side);
14059 else if (IS_SWITCHABLE(element))
14061 if (PLAYER_SWITCHING(player, x, y))
14063 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14064 player->index_bit, dig_side);
14069 player->is_switching = TRUE;
14070 player->switch_x = x;
14071 player->switch_y = y;
14073 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14075 if (element == EL_ROBOT_WHEEL)
14077 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14081 game.robot_wheel_active = TRUE;
14083 TEST_DrawLevelField(x, y);
14085 else if (element == EL_SP_TERMINAL)
14089 SCAN_PLAYFIELD(xx, yy)
14091 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14095 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14097 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14099 ResetGfxAnimation(xx, yy);
14100 TEST_DrawLevelField(xx, yy);
14104 else if (IS_BELT_SWITCH(element))
14106 ToggleBeltSwitch(x, y);
14108 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14109 element == EL_SWITCHGATE_SWITCH_DOWN ||
14110 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14111 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14113 ToggleSwitchgateSwitch(x, y);
14115 else if (element == EL_LIGHT_SWITCH ||
14116 element == EL_LIGHT_SWITCH_ACTIVE)
14118 ToggleLightSwitch(x, y);
14120 else if (element == EL_TIMEGATE_SWITCH ||
14121 element == EL_DC_TIMEGATE_SWITCH)
14123 ActivateTimegateSwitch(x, y);
14125 else if (element == EL_BALLOON_SWITCH_LEFT ||
14126 element == EL_BALLOON_SWITCH_RIGHT ||
14127 element == EL_BALLOON_SWITCH_UP ||
14128 element == EL_BALLOON_SWITCH_DOWN ||
14129 element == EL_BALLOON_SWITCH_NONE ||
14130 element == EL_BALLOON_SWITCH_ANY)
14132 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14133 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14134 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14135 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14136 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14139 else if (element == EL_LAMP)
14141 Feld[x][y] = EL_LAMP_ACTIVE;
14142 local_player->lights_still_needed--;
14144 ResetGfxAnimation(x, y);
14145 TEST_DrawLevelField(x, y);
14147 else if (element == EL_TIME_ORB_FULL)
14149 Feld[x][y] = EL_TIME_ORB_EMPTY;
14151 if (level.time > 0 || level.use_time_orb_bug)
14153 TimeLeft += level.time_orb_time;
14154 game.no_time_limit = FALSE;
14156 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14158 DisplayGameControlValues();
14161 ResetGfxAnimation(x, y);
14162 TEST_DrawLevelField(x, y);
14164 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14165 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14169 game.ball_state = !game.ball_state;
14171 SCAN_PLAYFIELD(xx, yy)
14173 int e = Feld[xx][yy];
14175 if (game.ball_state)
14177 if (e == EL_EMC_MAGIC_BALL)
14178 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14179 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14180 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14184 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14185 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14186 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14187 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14193 player->index_bit, dig_side);
14195 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14196 player->index_bit, dig_side);
14198 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14199 player->index_bit, dig_side);
14205 if (!PLAYER_SWITCHING(player, x, y))
14207 player->is_switching = TRUE;
14208 player->switch_x = x;
14209 player->switch_y = y;
14211 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14212 player->index_bit, dig_side);
14213 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14214 player->index_bit, dig_side);
14216 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14217 player->index_bit, dig_side);
14218 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14219 player->index_bit, dig_side);
14222 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14223 player->index_bit, dig_side);
14224 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14225 player->index_bit, dig_side);
14227 return MP_NO_ACTION;
14230 player->push_delay = -1;
14232 if (is_player) /* function can also be called by EL_PENGUIN */
14234 if (Feld[x][y] != element) /* really digged/collected something */
14236 player->is_collecting = !player->is_digging;
14237 player->is_active = TRUE;
14244 static boolean DigFieldByCE(int x, int y, int digging_element)
14246 int element = Feld[x][y];
14248 if (!IS_FREE(x, y))
14250 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14251 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14254 /* no element can dig solid indestructible elements */
14255 if (IS_INDESTRUCTIBLE(element) &&
14256 !IS_DIGGABLE(element) &&
14257 !IS_COLLECTIBLE(element))
14260 if (AmoebaNr[x][y] &&
14261 (element == EL_AMOEBA_FULL ||
14262 element == EL_BD_AMOEBA ||
14263 element == EL_AMOEBA_GROWING))
14265 AmoebaCnt[AmoebaNr[x][y]]--;
14266 AmoebaCnt2[AmoebaNr[x][y]]--;
14269 if (IS_MOVING(x, y))
14270 RemoveMovingField(x, y);
14274 TEST_DrawLevelField(x, y);
14277 /* if digged element was about to explode, prevent the explosion */
14278 ExplodeField[x][y] = EX_TYPE_NONE;
14280 PlayLevelSoundAction(x, y, action);
14283 Store[x][y] = EL_EMPTY;
14285 /* this makes it possible to leave the removed element again */
14286 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14287 Store[x][y] = element;
14292 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14294 int jx = player->jx, jy = player->jy;
14295 int x = jx + dx, y = jy + dy;
14296 int snap_direction = (dx == -1 ? MV_LEFT :
14297 dx == +1 ? MV_RIGHT :
14299 dy == +1 ? MV_DOWN : MV_NONE);
14300 boolean can_continue_snapping = (level.continuous_snapping &&
14301 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14303 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14306 if (!player->active || !IN_LEV_FIELD(x, y))
14314 if (player->MovPos == 0)
14315 player->is_pushing = FALSE;
14317 player->is_snapping = FALSE;
14319 if (player->MovPos == 0)
14321 player->is_moving = FALSE;
14322 player->is_digging = FALSE;
14323 player->is_collecting = FALSE;
14329 /* prevent snapping with already pressed snap key when not allowed */
14330 if (player->is_snapping && !can_continue_snapping)
14333 player->MovDir = snap_direction;
14335 if (player->MovPos == 0)
14337 player->is_moving = FALSE;
14338 player->is_digging = FALSE;
14339 player->is_collecting = FALSE;
14342 player->is_dropping = FALSE;
14343 player->is_dropping_pressed = FALSE;
14344 player->drop_pressed_delay = 0;
14346 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14349 player->is_snapping = TRUE;
14350 player->is_active = TRUE;
14352 if (player->MovPos == 0)
14354 player->is_moving = FALSE;
14355 player->is_digging = FALSE;
14356 player->is_collecting = FALSE;
14359 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14360 TEST_DrawLevelField(player->last_jx, player->last_jy);
14362 TEST_DrawLevelField(x, y);
14367 static boolean DropElement(struct PlayerInfo *player)
14369 int old_element, new_element;
14370 int dropx = player->jx, dropy = player->jy;
14371 int drop_direction = player->MovDir;
14372 int drop_side = drop_direction;
14373 int drop_element = get_next_dropped_element(player);
14375 /* do not drop an element on top of another element; when holding drop key
14376 pressed without moving, dropped element must move away before the next
14377 element can be dropped (this is especially important if the next element
14378 is dynamite, which can be placed on background for historical reasons) */
14379 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14382 if (IS_THROWABLE(drop_element))
14384 dropx += GET_DX_FROM_DIR(drop_direction);
14385 dropy += GET_DY_FROM_DIR(drop_direction);
14387 if (!IN_LEV_FIELD(dropx, dropy))
14391 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14392 new_element = drop_element; /* default: no change when dropping */
14394 /* check if player is active, not moving and ready to drop */
14395 if (!player->active || player->MovPos || player->drop_delay > 0)
14398 /* check if player has anything that can be dropped */
14399 if (new_element == EL_UNDEFINED)
14402 /* only set if player has anything that can be dropped */
14403 player->is_dropping_pressed = TRUE;
14405 /* check if drop key was pressed long enough for EM style dynamite */
14406 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14409 /* check if anything can be dropped at the current position */
14410 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14413 /* collected custom elements can only be dropped on empty fields */
14414 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14417 if (old_element != EL_EMPTY)
14418 Back[dropx][dropy] = old_element; /* store old element on this field */
14420 ResetGfxAnimation(dropx, dropy);
14421 ResetRandomAnimationValue(dropx, dropy);
14423 if (player->inventory_size > 0 ||
14424 player->inventory_infinite_element != EL_UNDEFINED)
14426 if (player->inventory_size > 0)
14428 player->inventory_size--;
14430 DrawGameDoorValues();
14432 if (new_element == EL_DYNAMITE)
14433 new_element = EL_DYNAMITE_ACTIVE;
14434 else if (new_element == EL_EM_DYNAMITE)
14435 new_element = EL_EM_DYNAMITE_ACTIVE;
14436 else if (new_element == EL_SP_DISK_RED)
14437 new_element = EL_SP_DISK_RED_ACTIVE;
14440 Feld[dropx][dropy] = new_element;
14442 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14443 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14444 el2img(Feld[dropx][dropy]), 0);
14446 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14448 /* needed if previous element just changed to "empty" in the last frame */
14449 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14451 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14452 player->index_bit, drop_side);
14453 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14455 player->index_bit, drop_side);
14457 TestIfElementTouchesCustomElement(dropx, dropy);
14459 else /* player is dropping a dyna bomb */
14461 player->dynabombs_left--;
14463 Feld[dropx][dropy] = new_element;
14465 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14466 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14467 el2img(Feld[dropx][dropy]), 0);
14469 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14472 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14473 InitField_WithBug1(dropx, dropy, FALSE);
14475 new_element = Feld[dropx][dropy]; /* element might have changed */
14477 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14478 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14480 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14481 MovDir[dropx][dropy] = drop_direction;
14483 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14485 /* do not cause impact style collision by dropping elements that can fall */
14486 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14489 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14490 player->is_dropping = TRUE;
14492 player->drop_pressed_delay = 0;
14493 player->is_dropping_pressed = FALSE;
14495 player->drop_x = dropx;
14496 player->drop_y = dropy;
14501 /* ------------------------------------------------------------------------- */
14502 /* game sound playing functions */
14503 /* ------------------------------------------------------------------------- */
14505 static int *loop_sound_frame = NULL;
14506 static int *loop_sound_volume = NULL;
14508 void InitPlayLevelSound()
14510 int num_sounds = getSoundListSize();
14512 checked_free(loop_sound_frame);
14513 checked_free(loop_sound_volume);
14515 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14516 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14519 static void PlayLevelSound(int x, int y, int nr)
14521 int sx = SCREENX(x), sy = SCREENY(y);
14522 int volume, stereo_position;
14523 int max_distance = 8;
14524 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14526 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14527 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14530 if (!IN_LEV_FIELD(x, y) ||
14531 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14532 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14535 volume = SOUND_MAX_VOLUME;
14537 if (!IN_SCR_FIELD(sx, sy))
14539 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14540 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14542 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14545 stereo_position = (SOUND_MAX_LEFT +
14546 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14547 (SCR_FIELDX + 2 * max_distance));
14549 if (IS_LOOP_SOUND(nr))
14551 /* This assures that quieter loop sounds do not overwrite louder ones,
14552 while restarting sound volume comparison with each new game frame. */
14554 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14557 loop_sound_volume[nr] = volume;
14558 loop_sound_frame[nr] = FrameCounter;
14561 PlaySoundExt(nr, volume, stereo_position, type);
14564 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14566 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14567 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14568 y < LEVELY(BY1) ? LEVELY(BY1) :
14569 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14573 static void PlayLevelSoundAction(int x, int y, int action)
14575 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14578 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14580 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14582 if (sound_effect != SND_UNDEFINED)
14583 PlayLevelSound(x, y, sound_effect);
14586 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14589 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14591 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14592 PlayLevelSound(x, y, sound_effect);
14595 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14597 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14599 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14600 PlayLevelSound(x, y, sound_effect);
14603 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14605 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14607 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14608 StopSound(sound_effect);
14611 static int getLevelMusicNr()
14613 if (levelset.music[level_nr] != MUS_UNDEFINED)
14614 return levelset.music[level_nr]; /* from config file */
14616 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14619 static void FadeLevelSounds()
14624 static void FadeLevelMusic()
14626 int music_nr = getLevelMusicNr();
14627 char *curr_music = getCurrentlyPlayingMusicFilename();
14628 char *next_music = getMusicInfoEntryFilename(music_nr);
14630 if (!strEqual(curr_music, next_music))
14634 void FadeLevelSoundsAndMusic()
14640 static void PlayLevelMusic()
14642 int music_nr = getLevelMusicNr();
14643 char *curr_music = getCurrentlyPlayingMusicFilename();
14644 char *next_music = getMusicInfoEntryFilename(music_nr);
14646 if (!strEqual(curr_music, next_music))
14647 PlayMusic(music_nr);
14650 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14652 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14653 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14654 int x = xx - 1 - offset;
14655 int y = yy - 1 - offset;
14660 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14664 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14668 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14672 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14676 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14680 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14684 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14687 case SAMPLE_android_clone:
14688 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14691 case SAMPLE_android_move:
14692 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14695 case SAMPLE_spring:
14696 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14700 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14704 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14707 case SAMPLE_eater_eat:
14708 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14712 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14715 case SAMPLE_collect:
14716 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14719 case SAMPLE_diamond:
14720 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14723 case SAMPLE_squash:
14724 /* !!! CHECK THIS !!! */
14726 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14728 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14732 case SAMPLE_wonderfall:
14733 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14737 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14741 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14745 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14749 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14753 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14757 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14760 case SAMPLE_wonder:
14761 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14765 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14768 case SAMPLE_exit_open:
14769 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14772 case SAMPLE_exit_leave:
14773 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14776 case SAMPLE_dynamite:
14777 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14781 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14785 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14789 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14793 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14797 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14801 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14805 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14810 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14812 int element = map_element_SP_to_RND(element_sp);
14813 int action = map_action_SP_to_RND(action_sp);
14814 int offset = (setup.sp_show_border_elements ? 0 : 1);
14815 int x = xx - offset;
14816 int y = yy - offset;
14818 PlayLevelSoundElementAction(x, y, element, action);
14821 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14823 int element = map_element_MM_to_RND(element_mm);
14824 int action = map_action_MM_to_RND(action_mm);
14826 int x = xx - offset;
14827 int y = yy - offset;
14829 if (!IS_MM_ELEMENT(element))
14830 element = EL_MM_DEFAULT;
14832 PlayLevelSoundElementAction(x, y, element, action);
14835 void PlaySound_MM(int sound_mm)
14837 int sound = map_sound_MM_to_RND(sound_mm);
14839 if (sound == SND_UNDEFINED)
14845 void PlaySoundLoop_MM(int sound_mm)
14847 int sound = map_sound_MM_to_RND(sound_mm);
14849 if (sound == SND_UNDEFINED)
14852 PlaySoundLoop(sound);
14855 void StopSound_MM(int sound_mm)
14857 int sound = map_sound_MM_to_RND(sound_mm);
14859 if (sound == SND_UNDEFINED)
14865 void RaiseScore(int value)
14867 local_player->score += value;
14869 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14871 DisplayGameControlValues();
14874 void RaiseScoreElement(int element)
14879 case EL_BD_DIAMOND:
14880 case EL_EMERALD_YELLOW:
14881 case EL_EMERALD_RED:
14882 case EL_EMERALD_PURPLE:
14883 case EL_SP_INFOTRON:
14884 RaiseScore(level.score[SC_EMERALD]);
14887 RaiseScore(level.score[SC_DIAMOND]);
14890 RaiseScore(level.score[SC_CRYSTAL]);
14893 RaiseScore(level.score[SC_PEARL]);
14896 case EL_BD_BUTTERFLY:
14897 case EL_SP_ELECTRON:
14898 RaiseScore(level.score[SC_BUG]);
14901 case EL_BD_FIREFLY:
14902 case EL_SP_SNIKSNAK:
14903 RaiseScore(level.score[SC_SPACESHIP]);
14906 case EL_DARK_YAMYAM:
14907 RaiseScore(level.score[SC_YAMYAM]);
14910 RaiseScore(level.score[SC_ROBOT]);
14913 RaiseScore(level.score[SC_PACMAN]);
14916 RaiseScore(level.score[SC_NUT]);
14919 case EL_EM_DYNAMITE:
14920 case EL_SP_DISK_RED:
14921 case EL_DYNABOMB_INCREASE_NUMBER:
14922 case EL_DYNABOMB_INCREASE_SIZE:
14923 case EL_DYNABOMB_INCREASE_POWER:
14924 RaiseScore(level.score[SC_DYNAMITE]);
14926 case EL_SHIELD_NORMAL:
14927 case EL_SHIELD_DEADLY:
14928 RaiseScore(level.score[SC_SHIELD]);
14930 case EL_EXTRA_TIME:
14931 RaiseScore(level.extra_time_score);
14945 case EL_DC_KEY_WHITE:
14946 RaiseScore(level.score[SC_KEY]);
14949 RaiseScore(element_info[element].collect_score);
14954 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14956 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14958 /* closing door required in case of envelope style request dialogs */
14960 CloseDoor(DOOR_CLOSE_1);
14962 if (network.enabled)
14963 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14967 FadeSkipNextFadeIn();
14969 SetGameStatus(GAME_MODE_MAIN);
14974 else /* continue playing the game */
14976 if (tape.playing && tape.deactivate_display)
14977 TapeDeactivateDisplayOff(TRUE);
14979 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14981 if (tape.playing && tape.deactivate_display)
14982 TapeDeactivateDisplayOn();
14986 void RequestQuitGame(boolean ask_if_really_quit)
14988 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14989 boolean skip_request = AllPlayersGone || quick_quit;
14991 RequestQuitGameExt(skip_request, quick_quit,
14992 "Do you really want to quit the game?");
14995 void RequestRestartGame(char *message)
14997 game.restart_game_message = NULL;
14999 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
15001 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15005 SetGameStatus(GAME_MODE_MAIN);
15012 /* ------------------------------------------------------------------------- */
15013 /* random generator functions */
15014 /* ------------------------------------------------------------------------- */
15016 unsigned int InitEngineRandom_RND(int seed)
15018 game.num_random_calls = 0;
15020 return InitEngineRandom(seed);
15023 unsigned int RND(int max)
15027 game.num_random_calls++;
15029 return GetEngineRandom(max);
15036 /* ------------------------------------------------------------------------- */
15037 /* game engine snapshot handling functions */
15038 /* ------------------------------------------------------------------------- */
15040 struct EngineSnapshotInfo
15042 /* runtime values for custom element collect score */
15043 int collect_score[NUM_CUSTOM_ELEMENTS];
15045 /* runtime values for group element choice position */
15046 int choice_pos[NUM_GROUP_ELEMENTS];
15048 /* runtime values for belt position animations */
15049 int belt_graphic[4][NUM_BELT_PARTS];
15050 int belt_anim_mode[4][NUM_BELT_PARTS];
15053 static struct EngineSnapshotInfo engine_snapshot_rnd;
15054 static char *snapshot_level_identifier = NULL;
15055 static int snapshot_level_nr = -1;
15057 static void SaveEngineSnapshotValues_RND()
15059 static int belt_base_active_element[4] =
15061 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15062 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15063 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15064 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15068 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15070 int element = EL_CUSTOM_START + i;
15072 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15075 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15077 int element = EL_GROUP_START + i;
15079 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15082 for (i = 0; i < 4; i++)
15084 for (j = 0; j < NUM_BELT_PARTS; j++)
15086 int element = belt_base_active_element[i] + j;
15087 int graphic = el2img(element);
15088 int anim_mode = graphic_info[graphic].anim_mode;
15090 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15091 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15096 static void LoadEngineSnapshotValues_RND()
15098 unsigned int num_random_calls = game.num_random_calls;
15101 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15103 int element = EL_CUSTOM_START + i;
15105 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15108 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15110 int element = EL_GROUP_START + i;
15112 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15115 for (i = 0; i < 4; i++)
15117 for (j = 0; j < NUM_BELT_PARTS; j++)
15119 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15120 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15122 graphic_info[graphic].anim_mode = anim_mode;
15126 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15128 InitRND(tape.random_seed);
15129 for (i = 0; i < num_random_calls; i++)
15133 if (game.num_random_calls != num_random_calls)
15135 Error(ERR_INFO, "number of random calls out of sync");
15136 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15137 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15138 Error(ERR_EXIT, "this should not happen -- please debug");
15142 void FreeEngineSnapshotSingle()
15144 FreeSnapshotSingle();
15146 setString(&snapshot_level_identifier, NULL);
15147 snapshot_level_nr = -1;
15150 void FreeEngineSnapshotList()
15152 FreeSnapshotList();
15155 ListNode *SaveEngineSnapshotBuffers()
15157 ListNode *buffers = NULL;
15159 /* copy some special values to a structure better suited for the snapshot */
15161 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15162 SaveEngineSnapshotValues_RND();
15163 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15164 SaveEngineSnapshotValues_EM();
15165 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15166 SaveEngineSnapshotValues_SP(&buffers);
15167 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15168 SaveEngineSnapshotValues_MM(&buffers);
15170 /* save values stored in special snapshot structure */
15172 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15173 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15174 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15176 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15178 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15181 /* save further RND engine values */
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15192 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15194 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15195 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15196 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15198 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15199 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15200 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15202 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15204 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15206 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15207 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15209 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15210 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15211 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15212 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15213 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15214 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15215 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15216 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15217 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15218 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15219 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15220 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15221 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15222 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15223 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15224 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15225 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15226 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15228 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15229 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15231 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15232 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15233 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15235 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15236 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15238 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15239 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15240 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15241 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15242 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15244 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15245 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15248 ListNode *node = engine_snapshot_list_rnd;
15251 while (node != NULL)
15253 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15258 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15264 void SaveEngineSnapshotSingle()
15266 ListNode *buffers = SaveEngineSnapshotBuffers();
15268 /* finally save all snapshot buffers to single snapshot */
15269 SaveSnapshotSingle(buffers);
15271 /* save level identification information */
15272 setString(&snapshot_level_identifier, leveldir_current->identifier);
15273 snapshot_level_nr = level_nr;
15276 boolean CheckSaveEngineSnapshotToList()
15278 boolean save_snapshot =
15279 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15280 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15281 game.snapshot.changed_action) ||
15282 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15283 game.snapshot.collected_item));
15285 game.snapshot.changed_action = FALSE;
15286 game.snapshot.collected_item = FALSE;
15287 game.snapshot.save_snapshot = save_snapshot;
15289 return save_snapshot;
15292 void SaveEngineSnapshotToList()
15294 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15298 ListNode *buffers = SaveEngineSnapshotBuffers();
15300 /* finally save all snapshot buffers to snapshot list */
15301 SaveSnapshotToList(buffers);
15304 void SaveEngineSnapshotToListInitial()
15306 FreeEngineSnapshotList();
15308 SaveEngineSnapshotToList();
15311 void LoadEngineSnapshotValues()
15313 /* restore special values from snapshot structure */
15315 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15316 LoadEngineSnapshotValues_RND();
15317 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15318 LoadEngineSnapshotValues_EM();
15319 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15320 LoadEngineSnapshotValues_SP();
15321 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15322 LoadEngineSnapshotValues_MM();
15325 void LoadEngineSnapshotSingle()
15327 LoadSnapshotSingle();
15329 LoadEngineSnapshotValues();
15332 void LoadEngineSnapshot_Undo(int steps)
15334 LoadSnapshotFromList_Older(steps);
15336 LoadEngineSnapshotValues();
15339 void LoadEngineSnapshot_Redo(int steps)
15341 LoadSnapshotFromList_Newer(steps);
15343 LoadEngineSnapshotValues();
15346 boolean CheckEngineSnapshotSingle()
15348 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15349 snapshot_level_nr == level_nr);
15352 boolean CheckEngineSnapshotList()
15354 return CheckSnapshotList();
15358 /* ---------- new game button stuff ---------------------------------------- */
15365 boolean *setup_value;
15366 boolean allowed_on_tape;
15368 } gamebutton_info[NUM_GAME_BUTTONS] =
15371 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15372 GAME_CTRL_ID_STOP, NULL,
15376 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15377 GAME_CTRL_ID_PAUSE, NULL,
15381 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15382 GAME_CTRL_ID_PLAY, NULL,
15386 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15387 GAME_CTRL_ID_UNDO, NULL,
15391 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15392 GAME_CTRL_ID_REDO, NULL,
15396 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15397 GAME_CTRL_ID_SAVE, NULL,
15401 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15402 GAME_CTRL_ID_PAUSE2, NULL,
15406 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15407 GAME_CTRL_ID_LOAD, NULL,
15411 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15412 GAME_CTRL_ID_PANEL_STOP, NULL,
15416 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15417 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15418 FALSE, "pause game"
15421 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15422 GAME_CTRL_ID_PANEL_PLAY, NULL,
15426 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15427 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15428 TRUE, "background music on/off"
15431 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15432 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15433 TRUE, "sound loops on/off"
15436 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15437 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15438 TRUE, "normal sounds on/off"
15441 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15442 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15443 FALSE, "background music on/off"
15446 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15447 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15448 FALSE, "sound loops on/off"
15451 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15452 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15453 FALSE, "normal sounds on/off"
15457 void CreateGameButtons()
15461 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15463 int graphic = gamebutton_info[i].graphic;
15464 struct GraphicInfo *gfx = &graphic_info[graphic];
15465 struct XY *pos = gamebutton_info[i].pos;
15466 struct GadgetInfo *gi;
15469 unsigned int event_mask;
15470 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15471 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15472 int base_x = (on_tape ? VX : DX);
15473 int base_y = (on_tape ? VY : DY);
15474 int gd_x = gfx->src_x;
15475 int gd_y = gfx->src_y;
15476 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15477 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15478 int gd_xa = gfx->src_x + gfx->active_xoffset;
15479 int gd_ya = gfx->src_y + gfx->active_yoffset;
15480 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15481 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15484 if (gfx->bitmap == NULL)
15486 game_gadget[id] = NULL;
15491 if (id == GAME_CTRL_ID_STOP ||
15492 id == GAME_CTRL_ID_PANEL_STOP ||
15493 id == GAME_CTRL_ID_PLAY ||
15494 id == GAME_CTRL_ID_PANEL_PLAY ||
15495 id == GAME_CTRL_ID_SAVE ||
15496 id == GAME_CTRL_ID_LOAD)
15498 button_type = GD_TYPE_NORMAL_BUTTON;
15500 event_mask = GD_EVENT_RELEASED;
15502 else if (id == GAME_CTRL_ID_UNDO ||
15503 id == GAME_CTRL_ID_REDO)
15505 button_type = GD_TYPE_NORMAL_BUTTON;
15507 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15511 button_type = GD_TYPE_CHECK_BUTTON;
15512 checked = (gamebutton_info[i].setup_value != NULL ?
15513 *gamebutton_info[i].setup_value : FALSE);
15514 event_mask = GD_EVENT_PRESSED;
15517 gi = CreateGadget(GDI_CUSTOM_ID, id,
15518 GDI_IMAGE_ID, graphic,
15519 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15520 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15521 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15522 GDI_WIDTH, gfx->width,
15523 GDI_HEIGHT, gfx->height,
15524 GDI_TYPE, button_type,
15525 GDI_STATE, GD_BUTTON_UNPRESSED,
15526 GDI_CHECKED, checked,
15527 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15528 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15529 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15530 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15531 GDI_DIRECT_DRAW, FALSE,
15532 GDI_EVENT_MASK, event_mask,
15533 GDI_CALLBACK_ACTION, HandleGameButtons,
15537 Error(ERR_EXIT, "cannot create gadget");
15539 game_gadget[id] = gi;
15543 void FreeGameButtons()
15547 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15548 FreeGadget(game_gadget[i]);
15551 static void UnmapGameButtonsAtSamePosition(int id)
15555 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15557 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15558 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15559 UnmapGadget(game_gadget[i]);
15562 static void UnmapGameButtonsAtSamePosition_All()
15564 if (setup.show_snapshot_buttons)
15566 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15567 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15568 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15572 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15573 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15574 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15576 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15577 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15578 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15582 static void MapGameButtonsAtSamePosition(int id)
15586 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15588 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15589 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15590 MapGadget(game_gadget[i]);
15592 UnmapGameButtonsAtSamePosition_All();
15595 void MapUndoRedoButtons()
15597 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15598 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15600 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15601 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15603 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15606 void UnmapUndoRedoButtons()
15608 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15609 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15611 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15612 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15614 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15617 void MapGameButtonsExt(boolean on_tape)
15621 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15622 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15623 i != GAME_CTRL_ID_UNDO &&
15624 i != GAME_CTRL_ID_REDO)
15625 MapGadget(game_gadget[i]);
15627 UnmapGameButtonsAtSamePosition_All();
15629 RedrawGameButtons();
15632 void UnmapGameButtonsExt(boolean on_tape)
15636 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15637 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15638 UnmapGadget(game_gadget[i]);
15641 void RedrawGameButtonsExt(boolean on_tape)
15645 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15646 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15647 RedrawGadget(game_gadget[i]);
15649 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15650 redraw_mask &= ~REDRAW_ALL;
15653 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15658 gi->checked = state;
15661 void RedrawSoundButtonGadget(int id)
15663 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15664 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15665 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15666 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15667 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15668 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15671 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15672 RedrawGadget(game_gadget[id2]);
15675 void MapGameButtons()
15677 MapGameButtonsExt(FALSE);
15680 void UnmapGameButtons()
15682 UnmapGameButtonsExt(FALSE);
15685 void RedrawGameButtons()
15687 RedrawGameButtonsExt(FALSE);
15690 void MapGameButtonsOnTape()
15692 MapGameButtonsExt(TRUE);
15695 void UnmapGameButtonsOnTape()
15697 UnmapGameButtonsExt(TRUE);
15700 void RedrawGameButtonsOnTape()
15702 RedrawGameButtonsExt(TRUE);
15705 void GameUndoRedoExt()
15707 ClearPlayerAction();
15709 tape.pausing = TRUE;
15712 UpdateAndDisplayGameControlValues();
15714 DrawCompleteVideoDisplay();
15715 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15716 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15717 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15722 void GameUndo(int steps)
15724 if (!CheckEngineSnapshotList())
15727 LoadEngineSnapshot_Undo(steps);
15732 void GameRedo(int steps)
15734 if (!CheckEngineSnapshotList())
15737 LoadEngineSnapshot_Redo(steps);
15742 static void HandleGameButtonsExt(int id, int button)
15744 static boolean game_undo_executed = FALSE;
15745 int steps = BUTTON_STEPSIZE(button);
15746 boolean handle_game_buttons =
15747 (game_status == GAME_MODE_PLAYING ||
15748 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15750 if (!handle_game_buttons)
15755 case GAME_CTRL_ID_STOP:
15756 case GAME_CTRL_ID_PANEL_STOP:
15757 if (game_status == GAME_MODE_MAIN)
15763 RequestQuitGame(TRUE);
15767 case GAME_CTRL_ID_PAUSE:
15768 case GAME_CTRL_ID_PAUSE2:
15769 case GAME_CTRL_ID_PANEL_PAUSE:
15770 if (network.enabled && game_status == GAME_MODE_PLAYING)
15773 SendToServer_ContinuePlaying();
15775 SendToServer_PausePlaying();
15778 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15780 game_undo_executed = FALSE;
15784 case GAME_CTRL_ID_PLAY:
15785 case GAME_CTRL_ID_PANEL_PLAY:
15786 if (game_status == GAME_MODE_MAIN)
15788 StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15790 else if (tape.pausing)
15792 if (network.enabled)
15793 SendToServer_ContinuePlaying();
15795 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15799 case GAME_CTRL_ID_UNDO:
15800 // Important: When using "save snapshot when collecting an item" mode,
15801 // load last (current) snapshot for first "undo" after pressing "pause"
15802 // (else the last-but-one snapshot would be loaded, because the snapshot
15803 // pointer already points to the last snapshot when pressing "pause",
15804 // which is fine for "every step/move" mode, but not for "every collect")
15805 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15806 !game_undo_executed)
15809 game_undo_executed = TRUE;
15814 case GAME_CTRL_ID_REDO:
15818 case GAME_CTRL_ID_SAVE:
15822 case GAME_CTRL_ID_LOAD:
15826 case SOUND_CTRL_ID_MUSIC:
15827 case SOUND_CTRL_ID_PANEL_MUSIC:
15828 if (setup.sound_music)
15830 setup.sound_music = FALSE;
15834 else if (audio.music_available)
15836 setup.sound = setup.sound_music = TRUE;
15838 SetAudioMode(setup.sound);
15840 if (game_status == GAME_MODE_PLAYING)
15844 RedrawSoundButtonGadget(id);
15848 case SOUND_CTRL_ID_LOOPS:
15849 case SOUND_CTRL_ID_PANEL_LOOPS:
15850 if (setup.sound_loops)
15851 setup.sound_loops = FALSE;
15852 else if (audio.loops_available)
15854 setup.sound = setup.sound_loops = TRUE;
15856 SetAudioMode(setup.sound);
15859 RedrawSoundButtonGadget(id);
15863 case SOUND_CTRL_ID_SIMPLE:
15864 case SOUND_CTRL_ID_PANEL_SIMPLE:
15865 if (setup.sound_simple)
15866 setup.sound_simple = FALSE;
15867 else if (audio.sound_available)
15869 setup.sound = setup.sound_simple = TRUE;
15871 SetAudioMode(setup.sound);
15874 RedrawSoundButtonGadget(id);
15883 static void HandleGameButtons(struct GadgetInfo *gi)
15885 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15888 void HandleSoundButtonKeys(Key key)
15890 if (key == setup.shortcut.sound_simple)
15891 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15892 else if (key == setup.shortcut.sound_loops)
15893 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15894 else if (key == setup.shortcut.sound_music)
15895 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);