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_FRAME 35
126 #define GAME_PANEL_SHIELD_NORMAL 36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
128 #define GAME_PANEL_SHIELD_DEADLY 38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
130 #define GAME_PANEL_EXIT 40
131 #define GAME_PANEL_EMC_MAGIC_BALL 41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
133 #define GAME_PANEL_LIGHT_SWITCH 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
135 #define GAME_PANEL_TIMEGATE_SWITCH 45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
137 #define GAME_PANEL_SWITCHGATE_SWITCH 47
138 #define GAME_PANEL_EMC_LENSES 48
139 #define GAME_PANEL_EMC_LENSES_TIME 49
140 #define GAME_PANEL_EMC_MAGNIFIER 50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
142 #define GAME_PANEL_BALLOON_SWITCH 52
143 #define GAME_PANEL_DYNABOMB_NUMBER 53
144 #define GAME_PANEL_DYNABOMB_SIZE 54
145 #define GAME_PANEL_DYNABOMB_POWER 55
146 #define GAME_PANEL_PENGUINS 56
147 #define GAME_PANEL_SOKOBAN_OBJECTS 57
148 #define GAME_PANEL_SOKOBAN_FIELDS 58
149 #define GAME_PANEL_ROBOT_WHEEL 59
150 #define GAME_PANEL_CONVEYOR_BELT_1 60
151 #define GAME_PANEL_CONVEYOR_BELT_2 61
152 #define GAME_PANEL_CONVEYOR_BELT_3 62
153 #define GAME_PANEL_CONVEYOR_BELT_4 63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
158 #define GAME_PANEL_MAGIC_WALL 68
159 #define GAME_PANEL_MAGIC_WALL_TIME 69
160 #define GAME_PANEL_GRAVITY_STATE 70
161 #define GAME_PANEL_GRAPHIC_1 71
162 #define GAME_PANEL_GRAPHIC_2 72
163 #define GAME_PANEL_GRAPHIC_3 73
164 #define GAME_PANEL_GRAPHIC_4 74
165 #define GAME_PANEL_GRAPHIC_5 75
166 #define GAME_PANEL_GRAPHIC_6 76
167 #define GAME_PANEL_GRAPHIC_7 77
168 #define GAME_PANEL_GRAPHIC_8 78
169 #define GAME_PANEL_ELEMENT_1 79
170 #define GAME_PANEL_ELEMENT_2 80
171 #define GAME_PANEL_ELEMENT_3 81
172 #define GAME_PANEL_ELEMENT_4 82
173 #define GAME_PANEL_ELEMENT_5 83
174 #define GAME_PANEL_ELEMENT_6 84
175 #define GAME_PANEL_ELEMENT_7 85
176 #define GAME_PANEL_ELEMENT_8 86
177 #define GAME_PANEL_ELEMENT_COUNT_1 87
178 #define GAME_PANEL_ELEMENT_COUNT_2 88
179 #define GAME_PANEL_ELEMENT_COUNT_3 89
180 #define GAME_PANEL_ELEMENT_COUNT_4 90
181 #define GAME_PANEL_ELEMENT_COUNT_5 91
182 #define GAME_PANEL_ELEMENT_COUNT_6 92
183 #define GAME_PANEL_ELEMENT_COUNT_7 93
184 #define GAME_PANEL_ELEMENT_COUNT_8 94
185 #define GAME_PANEL_CE_SCORE_1 95
186 #define GAME_PANEL_CE_SCORE_2 96
187 #define GAME_PANEL_CE_SCORE_3 97
188 #define GAME_PANEL_CE_SCORE_4 98
189 #define GAME_PANEL_CE_SCORE_5 99
190 #define GAME_PANEL_CE_SCORE_6 100
191 #define GAME_PANEL_CE_SCORE_7 101
192 #define GAME_PANEL_CE_SCORE_8 102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
201 #define GAME_PANEL_PLAYER_NAME 111
202 #define GAME_PANEL_LEVEL_NAME 112
203 #define GAME_PANEL_LEVEL_AUTHOR 113
205 #define NUM_GAME_PANEL_CONTROLS 114
207 struct GamePanelOrderInfo
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
215 struct GamePanelControlInfo
219 struct TextPosInfo *pos;
222 int value, last_value;
223 int frame, last_frame;
228 static struct GamePanelControlInfo game_panel_controls[] =
231 GAME_PANEL_LEVEL_NUMBER,
232 &game.panel.level_number,
241 GAME_PANEL_INVENTORY_COUNT,
242 &game.panel.inventory_count,
246 GAME_PANEL_INVENTORY_FIRST_1,
247 &game.panel.inventory_first[0],
251 GAME_PANEL_INVENTORY_FIRST_2,
252 &game.panel.inventory_first[1],
256 GAME_PANEL_INVENTORY_FIRST_3,
257 &game.panel.inventory_first[2],
261 GAME_PANEL_INVENTORY_FIRST_4,
262 &game.panel.inventory_first[3],
266 GAME_PANEL_INVENTORY_FIRST_5,
267 &game.panel.inventory_first[4],
271 GAME_PANEL_INVENTORY_FIRST_6,
272 &game.panel.inventory_first[5],
276 GAME_PANEL_INVENTORY_FIRST_7,
277 &game.panel.inventory_first[6],
281 GAME_PANEL_INVENTORY_FIRST_8,
282 &game.panel.inventory_first[7],
286 GAME_PANEL_INVENTORY_LAST_1,
287 &game.panel.inventory_last[0],
291 GAME_PANEL_INVENTORY_LAST_2,
292 &game.panel.inventory_last[1],
296 GAME_PANEL_INVENTORY_LAST_3,
297 &game.panel.inventory_last[2],
301 GAME_PANEL_INVENTORY_LAST_4,
302 &game.panel.inventory_last[3],
306 GAME_PANEL_INVENTORY_LAST_5,
307 &game.panel.inventory_last[4],
311 GAME_PANEL_INVENTORY_LAST_6,
312 &game.panel.inventory_last[5],
316 GAME_PANEL_INVENTORY_LAST_7,
317 &game.panel.inventory_last[6],
321 GAME_PANEL_INVENTORY_LAST_8,
322 &game.panel.inventory_last[7],
366 GAME_PANEL_KEY_WHITE,
367 &game.panel.key_white,
371 GAME_PANEL_KEY_WHITE_COUNT,
372 &game.panel.key_white_count,
381 GAME_PANEL_HIGHSCORE,
382 &game.panel.highscore,
411 GAME_PANEL_SHIELD_NORMAL,
412 &game.panel.shield_normal,
416 GAME_PANEL_SHIELD_NORMAL_TIME,
417 &game.panel.shield_normal_time,
421 GAME_PANEL_SHIELD_DEADLY,
422 &game.panel.shield_deadly,
426 GAME_PANEL_SHIELD_DEADLY_TIME,
427 &game.panel.shield_deadly_time,
436 GAME_PANEL_EMC_MAGIC_BALL,
437 &game.panel.emc_magic_ball,
441 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442 &game.panel.emc_magic_ball_switch,
446 GAME_PANEL_LIGHT_SWITCH,
447 &game.panel.light_switch,
451 GAME_PANEL_LIGHT_SWITCH_TIME,
452 &game.panel.light_switch_time,
456 GAME_PANEL_TIMEGATE_SWITCH,
457 &game.panel.timegate_switch,
461 GAME_PANEL_TIMEGATE_SWITCH_TIME,
462 &game.panel.timegate_switch_time,
466 GAME_PANEL_SWITCHGATE_SWITCH,
467 &game.panel.switchgate_switch,
471 GAME_PANEL_EMC_LENSES,
472 &game.panel.emc_lenses,
476 GAME_PANEL_EMC_LENSES_TIME,
477 &game.panel.emc_lenses_time,
481 GAME_PANEL_EMC_MAGNIFIER,
482 &game.panel.emc_magnifier,
486 GAME_PANEL_EMC_MAGNIFIER_TIME,
487 &game.panel.emc_magnifier_time,
491 GAME_PANEL_BALLOON_SWITCH,
492 &game.panel.balloon_switch,
496 GAME_PANEL_DYNABOMB_NUMBER,
497 &game.panel.dynabomb_number,
501 GAME_PANEL_DYNABOMB_SIZE,
502 &game.panel.dynabomb_size,
506 GAME_PANEL_DYNABOMB_POWER,
507 &game.panel.dynabomb_power,
512 &game.panel.penguins,
516 GAME_PANEL_SOKOBAN_OBJECTS,
517 &game.panel.sokoban_objects,
521 GAME_PANEL_SOKOBAN_FIELDS,
522 &game.panel.sokoban_fields,
526 GAME_PANEL_ROBOT_WHEEL,
527 &game.panel.robot_wheel,
531 GAME_PANEL_CONVEYOR_BELT_1,
532 &game.panel.conveyor_belt[0],
536 GAME_PANEL_CONVEYOR_BELT_2,
537 &game.panel.conveyor_belt[1],
541 GAME_PANEL_CONVEYOR_BELT_3,
542 &game.panel.conveyor_belt[2],
546 GAME_PANEL_CONVEYOR_BELT_4,
547 &game.panel.conveyor_belt[3],
551 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552 &game.panel.conveyor_belt_switch[0],
556 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557 &game.panel.conveyor_belt_switch[1],
561 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562 &game.panel.conveyor_belt_switch[2],
566 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567 &game.panel.conveyor_belt_switch[3],
571 GAME_PANEL_MAGIC_WALL,
572 &game.panel.magic_wall,
576 GAME_PANEL_MAGIC_WALL_TIME,
577 &game.panel.magic_wall_time,
581 GAME_PANEL_GRAVITY_STATE,
582 &game.panel.gravity_state,
586 GAME_PANEL_GRAPHIC_1,
587 &game.panel.graphic[0],
591 GAME_PANEL_GRAPHIC_2,
592 &game.panel.graphic[1],
596 GAME_PANEL_GRAPHIC_3,
597 &game.panel.graphic[2],
601 GAME_PANEL_GRAPHIC_4,
602 &game.panel.graphic[3],
606 GAME_PANEL_GRAPHIC_5,
607 &game.panel.graphic[4],
611 GAME_PANEL_GRAPHIC_6,
612 &game.panel.graphic[5],
616 GAME_PANEL_GRAPHIC_7,
617 &game.panel.graphic[6],
621 GAME_PANEL_GRAPHIC_8,
622 &game.panel.graphic[7],
626 GAME_PANEL_ELEMENT_1,
627 &game.panel.element[0],
631 GAME_PANEL_ELEMENT_2,
632 &game.panel.element[1],
636 GAME_PANEL_ELEMENT_3,
637 &game.panel.element[2],
641 GAME_PANEL_ELEMENT_4,
642 &game.panel.element[3],
646 GAME_PANEL_ELEMENT_5,
647 &game.panel.element[4],
651 GAME_PANEL_ELEMENT_6,
652 &game.panel.element[5],
656 GAME_PANEL_ELEMENT_7,
657 &game.panel.element[6],
661 GAME_PANEL_ELEMENT_8,
662 &game.panel.element[7],
666 GAME_PANEL_ELEMENT_COUNT_1,
667 &game.panel.element_count[0],
671 GAME_PANEL_ELEMENT_COUNT_2,
672 &game.panel.element_count[1],
676 GAME_PANEL_ELEMENT_COUNT_3,
677 &game.panel.element_count[2],
681 GAME_PANEL_ELEMENT_COUNT_4,
682 &game.panel.element_count[3],
686 GAME_PANEL_ELEMENT_COUNT_5,
687 &game.panel.element_count[4],
691 GAME_PANEL_ELEMENT_COUNT_6,
692 &game.panel.element_count[5],
696 GAME_PANEL_ELEMENT_COUNT_7,
697 &game.panel.element_count[6],
701 GAME_PANEL_ELEMENT_COUNT_8,
702 &game.panel.element_count[7],
706 GAME_PANEL_CE_SCORE_1,
707 &game.panel.ce_score[0],
711 GAME_PANEL_CE_SCORE_2,
712 &game.panel.ce_score[1],
716 GAME_PANEL_CE_SCORE_3,
717 &game.panel.ce_score[2],
721 GAME_PANEL_CE_SCORE_4,
722 &game.panel.ce_score[3],
726 GAME_PANEL_CE_SCORE_5,
727 &game.panel.ce_score[4],
731 GAME_PANEL_CE_SCORE_6,
732 &game.panel.ce_score[5],
736 GAME_PANEL_CE_SCORE_7,
737 &game.panel.ce_score[6],
741 GAME_PANEL_CE_SCORE_8,
742 &game.panel.ce_score[7],
746 GAME_PANEL_CE_SCORE_1_ELEMENT,
747 &game.panel.ce_score_element[0],
751 GAME_PANEL_CE_SCORE_2_ELEMENT,
752 &game.panel.ce_score_element[1],
756 GAME_PANEL_CE_SCORE_3_ELEMENT,
757 &game.panel.ce_score_element[2],
761 GAME_PANEL_CE_SCORE_4_ELEMENT,
762 &game.panel.ce_score_element[3],
766 GAME_PANEL_CE_SCORE_5_ELEMENT,
767 &game.panel.ce_score_element[4],
771 GAME_PANEL_CE_SCORE_6_ELEMENT,
772 &game.panel.ce_score_element[5],
776 GAME_PANEL_CE_SCORE_7_ELEMENT,
777 &game.panel.ce_score_element[6],
781 GAME_PANEL_CE_SCORE_8_ELEMENT,
782 &game.panel.ce_score_element[7],
786 GAME_PANEL_PLAYER_NAME,
787 &game.panel.player_name,
791 GAME_PANEL_LEVEL_NAME,
792 &game.panel.level_name,
796 GAME_PANEL_LEVEL_AUTHOR,
797 &game.panel.level_author,
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING 3
810 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION 2
812 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF -1
816 #define INITIAL_MOVE_DELAY_ON 0
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED 32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED 4
822 #define MOVE_DELAY_MAX_SPEED 1
824 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
827 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
832 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
834 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN (1)
841 #define MOVE_STEPSIZE_MAX (TILEX)
843 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
846 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
848 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
849 RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
851 RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
853 RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
855 (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
857 RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
860 RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
862 RND((c)->delay_random))
865 #define GET_VALID_RUNTIME_ELEMENT(e) \
866 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
868 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
869 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
870 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
871 (be) + (e) - EL_SELF)
873 #define GET_PLAYER_FROM_BITS(p) \
874 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
877 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
878 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
879 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
880 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
881 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
882 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
883 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
884 RESOLVED_REFERENCE_ELEMENT(be, e) : \
887 #define CAN_GROW_INTO(e) \
888 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
891 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
895 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
896 (CAN_MOVE_INTO_ACID(e) && \
897 Feld[x][y] == EL_ACID) || \
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
901 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
902 (CAN_MOVE_INTO_ACID(e) && \
903 Feld[x][y] == EL_ACID) || \
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
907 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
909 (CAN_MOVE_INTO_ACID(e) && \
910 Feld[x][y] == EL_ACID) || \
911 (DONT_COLLIDE_WITH(e) && \
913 !PLAYER_ENEMY_PROTECTED(x, y))))
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
916 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
918 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
919 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
922 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
924 #define ANDROID_CAN_CLONE_FIELD(x, y) \
925 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
929 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
932 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
935 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
938 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
940 #define PIG_CAN_ENTER_FIELD(e, x, y) \
941 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
944 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945 Feld[x][y] == EL_EM_EXIT_OPEN || \
946 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948 IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
950 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
953 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
955 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
956 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
959 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
960 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
962 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
964 #define CE_ENTER_FIELD_COND(e, x, y) \
965 (!IS_PLAYER(x, y) && \
966 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
969 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
974 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP 0
981 #define GAME_CTRL_ID_PAUSE 1
982 #define GAME_CTRL_ID_PLAY 2
983 #define GAME_CTRL_ID_UNDO 3
984 #define GAME_CTRL_ID_REDO 4
985 #define GAME_CTRL_ID_SAVE 5
986 #define GAME_CTRL_ID_PAUSE2 6
987 #define GAME_CTRL_ID_LOAD 7
988 #define SOUND_CTRL_ID_MUSIC 8
989 #define SOUND_CTRL_ID_LOOPS 9
990 #define SOUND_CTRL_ID_SIMPLE 10
992 #define NUM_GAME_BUTTONS 11
995 /* forward declaration for internal use */
997 static void CreateField(int, int, int);
999 static void ResetGfxAnimation(int, int);
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev) \
1031 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1033 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1035 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1037 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev) \
1041 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1043 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1045 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1056 static void HandleGameButtons(struct GadgetInfo *);
1058 int AmoebeNachbarNr(int, int);
1059 void AmoebeUmwandeln(int, int);
1060 void ContinueMoving(int, int);
1061 void Bang(int, int);
1062 void InitMovDir(int, int);
1063 void InitAmoebaNr(int, int);
1064 int NewHiScore(void);
1066 void TestIfGoodThingHitsBadThing(int, int, int);
1067 void TestIfBadThingHitsGoodThing(int, int, int);
1068 void TestIfPlayerTouchesBadThing(int, int);
1069 void TestIfPlayerRunsIntoBadThing(int, int, int);
1070 void TestIfBadThingTouchesPlayer(int, int);
1071 void TestIfBadThingRunsIntoPlayer(int, int, int);
1072 void TestIfFriendTouchesBadThing(int, int);
1073 void TestIfBadThingTouchesFriend(int, int);
1074 void TestIfBadThingTouchesOtherBadThing(int, int);
1075 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1077 void KillPlayer(struct PlayerInfo *);
1078 void BuryPlayer(struct PlayerInfo *);
1079 void RemovePlayer(struct PlayerInfo *);
1081 static int getInvisibleActiveFromInvisibleElement(int);
1082 static int getInvisibleFromInvisibleActiveElement(int);
1084 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1086 /* for detection of endless loops, caused by custom element programming */
1087 /* (using maximal playfield width x 10 is just a rough approximation) */
1088 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1090 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1092 if (recursion_loop_detected) \
1095 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1097 recursion_loop_detected = TRUE; \
1098 recursion_loop_element = (e); \
1101 recursion_loop_depth++; \
1104 #define RECURSION_LOOP_DETECTION_END() \
1106 recursion_loop_depth--; \
1109 static int recursion_loop_depth;
1110 static boolean recursion_loop_detected;
1111 static boolean recursion_loop_element;
1113 static int map_player_action[MAX_PLAYERS];
1116 /* ------------------------------------------------------------------------- */
1117 /* definition of elements that automatically change to other elements after */
1118 /* a specified time, eventually calling a function when changing */
1119 /* ------------------------------------------------------------------------- */
1121 /* forward declaration for changer functions */
1122 static void InitBuggyBase(int, int);
1123 static void WarnBuggyBase(int, int);
1125 static void InitTrap(int, int);
1126 static void ActivateTrap(int, int);
1127 static void ChangeActiveTrap(int, int);
1129 static void InitRobotWheel(int, int);
1130 static void RunRobotWheel(int, int);
1131 static void StopRobotWheel(int, int);
1133 static void InitTimegateWheel(int, int);
1134 static void RunTimegateWheel(int, int);
1136 static void InitMagicBallDelay(int, int);
1137 static void ActivateMagicBall(int, int);
1139 struct ChangingElementInfo
1144 void (*pre_change_function)(int x, int y);
1145 void (*change_function)(int x, int y);
1146 void (*post_change_function)(int x, int y);
1149 static struct ChangingElementInfo change_delay_list[] =
1184 EL_STEEL_EXIT_OPENING,
1192 EL_STEEL_EXIT_CLOSING,
1193 EL_STEEL_EXIT_CLOSED,
1216 EL_EM_STEEL_EXIT_OPENING,
1217 EL_EM_STEEL_EXIT_OPEN,
1224 EL_EM_STEEL_EXIT_CLOSING,
1248 EL_SWITCHGATE_OPENING,
1256 EL_SWITCHGATE_CLOSING,
1257 EL_SWITCHGATE_CLOSED,
1264 EL_TIMEGATE_OPENING,
1272 EL_TIMEGATE_CLOSING,
1281 EL_ACID_SPLASH_LEFT,
1289 EL_ACID_SPLASH_RIGHT,
1298 EL_SP_BUGGY_BASE_ACTIVATING,
1305 EL_SP_BUGGY_BASE_ACTIVATING,
1306 EL_SP_BUGGY_BASE_ACTIVE,
1313 EL_SP_BUGGY_BASE_ACTIVE,
1337 EL_ROBOT_WHEEL_ACTIVE,
1345 EL_TIMEGATE_SWITCH_ACTIVE,
1353 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1354 EL_DC_TIMEGATE_SWITCH,
1361 EL_EMC_MAGIC_BALL_ACTIVE,
1362 EL_EMC_MAGIC_BALL_ACTIVE,
1369 EL_EMC_SPRING_BUMPER_ACTIVE,
1370 EL_EMC_SPRING_BUMPER,
1377 EL_DIAGONAL_SHRINKING,
1385 EL_DIAGONAL_GROWING,
1406 int push_delay_fixed, push_delay_random;
1410 { EL_SPRING, 0, 0 },
1411 { EL_BALLOON, 0, 0 },
1413 { EL_SOKOBAN_OBJECT, 2, 0 },
1414 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1415 { EL_SATELLITE, 2, 0 },
1416 { EL_SP_DISK_YELLOW, 2, 0 },
1418 { EL_UNDEFINED, 0, 0 },
1426 move_stepsize_list[] =
1428 { EL_AMOEBA_DROP, 2 },
1429 { EL_AMOEBA_DROPPING, 2 },
1430 { EL_QUICKSAND_FILLING, 1 },
1431 { EL_QUICKSAND_EMPTYING, 1 },
1432 { EL_QUICKSAND_FAST_FILLING, 2 },
1433 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1434 { EL_MAGIC_WALL_FILLING, 2 },
1435 { EL_MAGIC_WALL_EMPTYING, 2 },
1436 { EL_BD_MAGIC_WALL_FILLING, 2 },
1437 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1438 { EL_DC_MAGIC_WALL_FILLING, 2 },
1439 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1441 { EL_UNDEFINED, 0 },
1449 collect_count_list[] =
1452 { EL_BD_DIAMOND, 1 },
1453 { EL_EMERALD_YELLOW, 1 },
1454 { EL_EMERALD_RED, 1 },
1455 { EL_EMERALD_PURPLE, 1 },
1457 { EL_SP_INFOTRON, 1 },
1461 { EL_UNDEFINED, 0 },
1469 access_direction_list[] =
1471 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1472 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1473 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1474 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1475 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1476 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1477 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1478 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1479 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1480 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1481 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1483 { EL_SP_PORT_LEFT, MV_RIGHT },
1484 { EL_SP_PORT_RIGHT, MV_LEFT },
1485 { EL_SP_PORT_UP, MV_DOWN },
1486 { EL_SP_PORT_DOWN, MV_UP },
1487 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1488 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1489 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1490 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1491 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1492 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1493 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1494 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1495 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1496 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1497 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1498 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1499 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1500 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1501 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1503 { EL_UNDEFINED, MV_NONE }
1506 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1508 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1509 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1510 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1511 IS_JUST_CHANGING(x, y))
1513 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1515 /* static variables for playfield scan mode (scanning forward or backward) */
1516 static int playfield_scan_start_x = 0;
1517 static int playfield_scan_start_y = 0;
1518 static int playfield_scan_delta_x = 1;
1519 static int playfield_scan_delta_y = 1;
1521 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1522 (y) >= 0 && (y) <= lev_fieldy - 1; \
1523 (y) += playfield_scan_delta_y) \
1524 for ((x) = playfield_scan_start_x; \
1525 (x) >= 0 && (x) <= lev_fieldx - 1; \
1526 (x) += playfield_scan_delta_x)
1529 void DEBUG_SetMaximumDynamite()
1533 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1534 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1535 local_player->inventory_element[local_player->inventory_size++] =
1540 static void InitPlayfieldScanModeVars()
1542 if (game.use_reverse_scan_direction)
1544 playfield_scan_start_x = lev_fieldx - 1;
1545 playfield_scan_start_y = lev_fieldy - 1;
1547 playfield_scan_delta_x = -1;
1548 playfield_scan_delta_y = -1;
1552 playfield_scan_start_x = 0;
1553 playfield_scan_start_y = 0;
1555 playfield_scan_delta_x = 1;
1556 playfield_scan_delta_y = 1;
1560 static void InitPlayfieldScanMode(int mode)
1562 game.use_reverse_scan_direction =
1563 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1565 InitPlayfieldScanModeVars();
1568 static int get_move_delay_from_stepsize(int move_stepsize)
1571 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1573 /* make sure that stepsize value is always a power of 2 */
1574 move_stepsize = (1 << log_2(move_stepsize));
1576 return TILEX / move_stepsize;
1579 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1582 int player_nr = player->index_nr;
1583 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1584 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1586 /* do no immediately change move delay -- the player might just be moving */
1587 player->move_delay_value_next = move_delay;
1589 /* information if player can move must be set separately */
1590 player->cannot_move = cannot_move;
1594 player->move_delay = game.initial_move_delay[player_nr];
1595 player->move_delay_value = game.initial_move_delay_value[player_nr];
1597 player->move_delay_value_next = -1;
1599 player->move_delay_reset_counter = 0;
1603 void GetPlayerConfig()
1605 GameFrameDelay = setup.game_frame_delay;
1607 if (!audio.sound_available)
1608 setup.sound_simple = FALSE;
1610 if (!audio.loops_available)
1611 setup.sound_loops = FALSE;
1613 if (!audio.music_available)
1614 setup.sound_music = FALSE;
1616 if (!video.fullscreen_available)
1617 setup.fullscreen = FALSE;
1619 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1621 SetAudioMode(setup.sound);
1624 int GetElementFromGroupElement(int element)
1626 if (IS_GROUP_ELEMENT(element))
1628 struct ElementGroupInfo *group = element_info[element].group;
1629 int last_anim_random_frame = gfx.anim_random_frame;
1632 if (group->choice_mode == ANIM_RANDOM)
1633 gfx.anim_random_frame = RND(group->num_elements_resolved);
1635 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1636 group->choice_mode, 0,
1639 if (group->choice_mode == ANIM_RANDOM)
1640 gfx.anim_random_frame = last_anim_random_frame;
1642 group->choice_pos++;
1644 element = group->element_resolved[element_pos];
1650 static void InitPlayerField(int x, int y, int element, boolean init_game)
1652 if (element == EL_SP_MURPHY)
1656 if (stored_player[0].present)
1658 Feld[x][y] = EL_SP_MURPHY_CLONE;
1664 stored_player[0].initial_element = element;
1665 stored_player[0].use_murphy = TRUE;
1667 if (!level.use_artwork_element[0])
1668 stored_player[0].artwork_element = EL_SP_MURPHY;
1671 Feld[x][y] = EL_PLAYER_1;
1677 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1678 int jx = player->jx, jy = player->jy;
1680 player->present = TRUE;
1682 player->block_last_field = (element == EL_SP_MURPHY ?
1683 level.sp_block_last_field :
1684 level.block_last_field);
1686 /* ---------- initialize player's last field block delay --------------- */
1688 /* always start with reliable default value (no adjustment needed) */
1689 player->block_delay_adjustment = 0;
1691 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1692 if (player->block_last_field && element == EL_SP_MURPHY)
1693 player->block_delay_adjustment = 1;
1695 /* special case 2: in game engines before 3.1.1, blocking was different */
1696 if (game.use_block_last_field_bug)
1697 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1699 if (!options.network || player->connected)
1701 player->active = TRUE;
1703 /* remove potentially duplicate players */
1704 if (StorePlayer[jx][jy] == Feld[x][y])
1705 StorePlayer[jx][jy] = 0;
1707 StorePlayer[x][y] = Feld[x][y];
1709 #if DEBUG_INIT_PLAYER
1712 printf("- player element %d activated", player->element_nr);
1713 printf(" (local player is %d and currently %s)\n",
1714 local_player->element_nr,
1715 local_player->active ? "active" : "not active");
1720 Feld[x][y] = EL_EMPTY;
1722 player->jx = player->last_jx = x;
1723 player->jy = player->last_jy = y;
1728 int player_nr = GET_PLAYER_NR(element);
1729 struct PlayerInfo *player = &stored_player[player_nr];
1731 if (player->active && player->killed)
1732 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1736 static void InitField(int x, int y, boolean init_game)
1738 int element = Feld[x][y];
1747 InitPlayerField(x, y, element, init_game);
1750 case EL_SOKOBAN_FIELD_PLAYER:
1751 element = Feld[x][y] = EL_PLAYER_1;
1752 InitField(x, y, init_game);
1754 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1755 InitField(x, y, init_game);
1758 case EL_SOKOBAN_FIELD_EMPTY:
1759 local_player->sokobanfields_still_needed++;
1763 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1764 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1765 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1766 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1767 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1768 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1769 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1770 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1771 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1772 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1781 case EL_SPACESHIP_RIGHT:
1782 case EL_SPACESHIP_UP:
1783 case EL_SPACESHIP_LEFT:
1784 case EL_SPACESHIP_DOWN:
1785 case EL_BD_BUTTERFLY:
1786 case EL_BD_BUTTERFLY_RIGHT:
1787 case EL_BD_BUTTERFLY_UP:
1788 case EL_BD_BUTTERFLY_LEFT:
1789 case EL_BD_BUTTERFLY_DOWN:
1791 case EL_BD_FIREFLY_RIGHT:
1792 case EL_BD_FIREFLY_UP:
1793 case EL_BD_FIREFLY_LEFT:
1794 case EL_BD_FIREFLY_DOWN:
1795 case EL_PACMAN_RIGHT:
1797 case EL_PACMAN_LEFT:
1798 case EL_PACMAN_DOWN:
1800 case EL_YAMYAM_LEFT:
1801 case EL_YAMYAM_RIGHT:
1803 case EL_YAMYAM_DOWN:
1804 case EL_DARK_YAMYAM:
1807 case EL_SP_SNIKSNAK:
1808 case EL_SP_ELECTRON:
1817 case EL_AMOEBA_FULL:
1822 case EL_AMOEBA_DROP:
1823 if (y == lev_fieldy - 1)
1825 Feld[x][y] = EL_AMOEBA_GROWING;
1826 Store[x][y] = EL_AMOEBA_WET;
1830 case EL_DYNAMITE_ACTIVE:
1831 case EL_SP_DISK_RED_ACTIVE:
1832 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1833 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1834 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1835 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1836 MovDelay[x][y] = 96;
1839 case EL_EM_DYNAMITE_ACTIVE:
1840 MovDelay[x][y] = 32;
1844 local_player->lights_still_needed++;
1848 local_player->friends_still_needed++;
1853 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1856 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1857 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1858 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1859 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1860 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1861 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1862 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1863 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1864 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1865 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1866 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1867 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1870 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1871 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1872 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1874 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1876 game.belt_dir[belt_nr] = belt_dir;
1877 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1879 else /* more than one switch -- set it like the first switch */
1881 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1886 case EL_LIGHT_SWITCH_ACTIVE:
1888 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1891 case EL_INVISIBLE_STEELWALL:
1892 case EL_INVISIBLE_WALL:
1893 case EL_INVISIBLE_SAND:
1894 if (game.light_time_left > 0 ||
1895 game.lenses_time_left > 0)
1896 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1899 case EL_EMC_MAGIC_BALL:
1900 if (game.ball_state)
1901 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1904 case EL_EMC_MAGIC_BALL_SWITCH:
1905 if (game.ball_state)
1906 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1909 case EL_TRIGGER_PLAYER:
1910 case EL_TRIGGER_ELEMENT:
1911 case EL_TRIGGER_CE_VALUE:
1912 case EL_TRIGGER_CE_SCORE:
1914 case EL_ANY_ELEMENT:
1915 case EL_CURRENT_CE_VALUE:
1916 case EL_CURRENT_CE_SCORE:
1933 /* reference elements should not be used on the playfield */
1934 Feld[x][y] = EL_EMPTY;
1938 if (IS_CUSTOM_ELEMENT(element))
1940 if (CAN_MOVE(element))
1943 if (!element_info[element].use_last_ce_value || init_game)
1944 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946 else if (IS_GROUP_ELEMENT(element))
1948 Feld[x][y] = GetElementFromGroupElement(element);
1950 InitField(x, y, init_game);
1957 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1960 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1962 InitField(x, y, init_game);
1964 /* not needed to call InitMovDir() -- already done by InitField()! */
1965 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966 CAN_MOVE(Feld[x][y]))
1970 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1972 int old_element = Feld[x][y];
1974 InitField(x, y, init_game);
1976 /* not needed to call InitMovDir() -- already done by InitField()! */
1977 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1978 CAN_MOVE(old_element) &&
1979 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1982 /* this case is in fact a combination of not less than three bugs:
1983 first, it calls InitMovDir() for elements that can move, although this is
1984 already done by InitField(); then, it checks the element that was at this
1985 field _before_ the call to InitField() (which can change it); lastly, it
1986 was not called for "mole with direction" elements, which were treated as
1987 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1991 static int get_key_element_from_nr(int key_nr)
1993 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1994 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1995 EL_EM_KEY_1 : EL_KEY_1);
1997 return key_base_element + key_nr;
2000 static int get_next_dropped_element(struct PlayerInfo *player)
2002 return (player->inventory_size > 0 ?
2003 player->inventory_element[player->inventory_size - 1] :
2004 player->inventory_infinite_element != EL_UNDEFINED ?
2005 player->inventory_infinite_element :
2006 player->dynabombs_left > 0 ?
2007 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2011 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2013 /* pos >= 0: get element from bottom of the stack;
2014 pos < 0: get element from top of the stack */
2018 int min_inventory_size = -pos;
2019 int inventory_pos = player->inventory_size - min_inventory_size;
2020 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2022 return (player->inventory_size >= min_inventory_size ?
2023 player->inventory_element[inventory_pos] :
2024 player->inventory_infinite_element != EL_UNDEFINED ?
2025 player->inventory_infinite_element :
2026 player->dynabombs_left >= min_dynabombs_left ?
2027 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032 int min_dynabombs_left = pos + 1;
2033 int min_inventory_size = pos + 1 - player->dynabombs_left;
2034 int inventory_pos = pos - player->dynabombs_left;
2036 return (player->inventory_infinite_element != EL_UNDEFINED ?
2037 player->inventory_infinite_element :
2038 player->dynabombs_left >= min_dynabombs_left ?
2039 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2040 player->inventory_size >= min_inventory_size ?
2041 player->inventory_element[inventory_pos] :
2046 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2048 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2049 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2052 if (gpo1->sort_priority != gpo2->sort_priority)
2053 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2055 compare_result = gpo1->nr - gpo2->nr;
2057 return compare_result;
2060 int getPlayerInventorySize(int player_nr)
2062 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2063 return level.native_em_level->ply[player_nr]->dynamite;
2064 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2065 return level.native_sp_level->game_sp->red_disk_count;
2067 return stored_player[player_nr].inventory_size;
2070 void InitGameControlValues()
2074 for (i = 0; game_panel_controls[i].nr != -1; i++)
2076 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2077 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2078 struct TextPosInfo *pos = gpc->pos;
2080 int type = gpc->type;
2084 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2085 Error(ERR_EXIT, "this should not happen -- please debug");
2088 /* force update of game controls after initialization */
2089 gpc->value = gpc->last_value = -1;
2090 gpc->frame = gpc->last_frame = -1;
2091 gpc->gfx_frame = -1;
2093 /* determine panel value width for later calculation of alignment */
2094 if (type == TYPE_INTEGER || type == TYPE_STRING)
2096 pos->width = pos->size * getFontWidth(pos->font);
2097 pos->height = getFontHeight(pos->font);
2099 else if (type == TYPE_ELEMENT)
2101 pos->width = pos->size;
2102 pos->height = pos->size;
2105 /* fill structure for game panel draw order */
2107 gpo->sort_priority = pos->sort_priority;
2110 /* sort game panel controls according to sort_priority and control number */
2111 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2112 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2115 void UpdatePlayfieldElementCount()
2117 boolean use_element_count = FALSE;
2120 /* first check if it is needed at all to calculate playfield element count */
2121 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2122 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2123 use_element_count = TRUE;
2125 if (!use_element_count)
2128 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2129 element_info[i].element_count = 0;
2131 SCAN_PLAYFIELD(x, y)
2133 element_info[Feld[x][y]].element_count++;
2136 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2137 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2138 if (IS_IN_GROUP(j, i))
2139 element_info[EL_GROUP_START + i].element_count +=
2140 element_info[j].element_count;
2143 void UpdateGameControlValues()
2146 int time = (local_player->LevelSolved ?
2147 local_player->LevelSolved_CountingTime :
2148 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->time :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->time_played :
2152 game.no_time_limit ? TimePlayed : TimeLeft);
2153 int score = (local_player->LevelSolved ?
2154 local_player->LevelSolved_CountingScore :
2155 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2156 level.native_em_level->lev->score :
2157 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2158 level.native_sp_level->game_sp->score :
2159 local_player->score);
2160 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2161 level.native_em_level->lev->required :
2162 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2163 level.native_sp_level->game_sp->infotrons_still_needed :
2164 local_player->gems_still_needed);
2165 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2166 level.native_em_level->lev->required > 0 :
2167 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2168 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2169 local_player->gems_still_needed > 0 ||
2170 local_player->sokobanfields_still_needed > 0 ||
2171 local_player->lights_still_needed > 0);
2173 UpdatePlayfieldElementCount();
2175 /* update game panel control values */
2177 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2178 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2180 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2181 for (i = 0; i < MAX_NUM_KEYS; i++)
2182 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2183 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2184 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2186 if (game.centered_player_nr == -1)
2188 for (i = 0; i < MAX_PLAYERS; i++)
2190 /* only one player in Supaplex game engine */
2191 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2194 for (k = 0; k < MAX_NUM_KEYS; k++)
2196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2198 if (level.native_em_level->ply[i]->keys & (1 << k))
2199 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2200 get_key_element_from_nr(k);
2202 else if (stored_player[i].key[k])
2203 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2204 get_key_element_from_nr(k);
2207 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2208 getPlayerInventorySize(i);
2210 if (stored_player[i].num_white_keys > 0)
2211 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2214 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2215 stored_player[i].num_white_keys;
2220 int player_nr = game.centered_player_nr;
2222 for (k = 0; k < MAX_NUM_KEYS; k++)
2224 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2226 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2227 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2228 get_key_element_from_nr(k);
2230 else if (stored_player[player_nr].key[k])
2231 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2232 get_key_element_from_nr(k);
2235 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236 getPlayerInventorySize(player_nr);
2238 if (stored_player[player_nr].num_white_keys > 0)
2239 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2241 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242 stored_player[player_nr].num_white_keys;
2245 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2247 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248 get_inventory_element_from_pos(local_player, i);
2249 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250 get_inventory_element_from_pos(local_player, -i - 1);
2253 game_panel_controls[GAME_PANEL_SCORE].value = score;
2254 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2256 game_panel_controls[GAME_PANEL_TIME].value = time;
2258 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2262 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2264 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2267 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268 local_player->shield_normal_time_left;
2269 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2272 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273 local_player->shield_deadly_time_left;
2275 game_panel_controls[GAME_PANEL_EXIT].value =
2276 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2278 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282 EL_EMC_MAGIC_BALL_SWITCH);
2284 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287 game.light_time_left;
2289 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292 game.timegate_time_left;
2294 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2297 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300 game.lenses_time_left;
2302 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305 game.magnify_time_left;
2307 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2309 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2311 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2312 EL_BALLOON_SWITCH_NONE);
2314 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315 local_player->dynabomb_count;
2316 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317 local_player->dynabomb_size;
2318 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2321 game_panel_controls[GAME_PANEL_PENGUINS].value =
2322 local_player->friends_still_needed;
2324 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325 local_player->sokobanfields_still_needed;
2326 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327 local_player->sokobanfields_still_needed;
2329 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2332 for (i = 0; i < NUM_BELTS; i++)
2334 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2341 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344 game.magic_wall_time_left;
2346 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347 local_player->gravity;
2349 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2352 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355 game.panel.element[i].id : EL_UNDEFINED);
2357 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360 element_info[game.panel.element_count[i].id].element_count : 0);
2362 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365 element_info[game.panel.ce_score[i].id].collect_score : 0);
2367 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370 element_info[game.panel.ce_score_element[i].id].collect_score :
2373 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2377 /* update game panel control frames */
2379 for (i = 0; game_panel_controls[i].nr != -1; i++)
2381 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2383 if (gpc->type == TYPE_ELEMENT)
2385 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2387 int last_anim_random_frame = gfx.anim_random_frame;
2388 int element = gpc->value;
2389 int graphic = el2panelimg(element);
2391 if (gpc->value != gpc->last_value)
2394 gpc->gfx_random = INIT_GFX_RANDOM();
2400 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402 gpc->gfx_random = INIT_GFX_RANDOM();
2405 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406 gfx.anim_random_frame = gpc->gfx_random;
2408 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409 gpc->gfx_frame = element_info[element].collect_score;
2411 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2414 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415 gfx.anim_random_frame = last_anim_random_frame;
2421 void DisplayGameControlValues()
2423 boolean redraw_panel = FALSE;
2426 for (i = 0; game_panel_controls[i].nr != -1; i++)
2428 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2430 if (PANEL_DEACTIVATED(gpc->pos))
2433 if (gpc->value == gpc->last_value &&
2434 gpc->frame == gpc->last_frame)
2437 redraw_panel = TRUE;
2443 /* copy default game door content to main double buffer */
2445 /* !!! CHECK AGAIN !!! */
2446 SetPanelBackground();
2447 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2450 /* redraw game control buttons */
2451 RedrawGameButtons();
2453 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2455 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2457 int nr = game_panel_order[i].nr;
2458 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459 struct TextPosInfo *pos = gpc->pos;
2460 int type = gpc->type;
2461 int value = gpc->value;
2462 int frame = gpc->frame;
2463 int size = pos->size;
2464 int font = pos->font;
2465 boolean draw_masked = pos->draw_masked;
2466 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2468 if (PANEL_DEACTIVATED(pos))
2471 gpc->last_value = value;
2472 gpc->last_frame = frame;
2474 if (type == TYPE_INTEGER)
2476 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477 nr == GAME_PANEL_TIME)
2479 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2481 if (use_dynamic_size) /* use dynamic number of digits */
2483 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485 int size2 = size1 + 1;
2486 int font1 = pos->font;
2487 int font2 = pos->font_alt;
2489 size = (value < value_change ? size1 : size2);
2490 font = (value < value_change ? font1 : font2);
2494 /* correct text size if "digits" is zero or less */
2496 size = strlen(int2str(value, size));
2498 /* dynamically correct text alignment */
2499 pos->width = size * getFontWidth(font);
2501 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502 int2str(value, size), font, mask_mode);
2504 else if (type == TYPE_ELEMENT)
2506 int element, graphic;
2510 int dst_x = PANEL_XPOS(pos);
2511 int dst_y = PANEL_YPOS(pos);
2513 if (value != EL_UNDEFINED && value != EL_EMPTY)
2516 graphic = el2panelimg(value);
2518 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2520 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2523 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2526 width = graphic_info[graphic].width * size / TILESIZE;
2527 height = graphic_info[graphic].height * size / TILESIZE;
2530 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2533 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2537 else if (type == TYPE_STRING)
2539 boolean active = (value != 0);
2540 char *state_normal = "off";
2541 char *state_active = "on";
2542 char *state = (active ? state_active : state_normal);
2543 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2545 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2546 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2548 if (nr == GAME_PANEL_GRAVITY_STATE)
2550 int font1 = pos->font; /* (used for normal state) */
2551 int font2 = pos->font_alt; /* (used for active state) */
2553 font = (active ? font2 : font1);
2562 /* don't truncate output if "chars" is zero or less */
2565 /* dynamically correct text alignment */
2566 pos->width = size * getFontWidth(font);
2569 s_cut = getStringCopyN(s, size);
2571 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572 s_cut, font, mask_mode);
2578 redraw_mask |= REDRAW_DOOR_1;
2581 SetGameStatus(GAME_MODE_PLAYING);
2584 void UpdateAndDisplayGameControlValues()
2586 if (tape.deactivate_display)
2589 UpdateGameControlValues();
2590 DisplayGameControlValues();
2593 void UpdateGameDoorValues()
2595 UpdateGameControlValues();
2598 void DrawGameDoorValues()
2600 DisplayGameControlValues();
2605 =============================================================================
2607 -----------------------------------------------------------------------------
2608 initialize game engine due to level / tape version number
2609 =============================================================================
2612 static void InitGameEngine()
2614 int i, j, k, l, x, y;
2616 /* set game engine from tape file when re-playing, else from level file */
2617 game.engine_version = (tape.playing ? tape.engine_version :
2618 level.game_version);
2620 /* set single or multi-player game mode (needed for re-playing tapes) */
2621 game.team_mode = setup.team_mode;
2625 int num_players = 0;
2627 for (i = 0; i < MAX_PLAYERS; i++)
2628 if (tape.player_participates[i])
2631 /* multi-player tapes contain input data for more than one player */
2632 game.team_mode = (num_players > 1);
2635 /* ---------------------------------------------------------------------- */
2636 /* set flags for bugs and changes according to active game engine version */
2637 /* ---------------------------------------------------------------------- */
2640 Summary of bugfix/change:
2641 Fixed handling for custom elements that change when pushed by the player.
2643 Fixed/changed in version:
2647 Before 3.1.0, custom elements that "change when pushing" changed directly
2648 after the player started pushing them (until then handled in "DigField()").
2649 Since 3.1.0, these custom elements are not changed until the "pushing"
2650 move of the element is finished (now handled in "ContinueMoving()").
2652 Affected levels/tapes:
2653 The first condition is generally needed for all levels/tapes before version
2654 3.1.0, which might use the old behaviour before it was changed; known tapes
2655 that are affected are some tapes from the level set "Walpurgis Gardens" by
2657 The second condition is an exception from the above case and is needed for
2658 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659 above (including some development versions of 3.1.0), but before it was
2660 known that this change would break tapes like the above and was fixed in
2661 3.1.1, so that the changed behaviour was active although the engine version
2662 while recording maybe was before 3.1.0. There is at least one tape that is
2663 affected by this exception, which is the tape for the one-level set "Bug
2664 Machine" by Juergen Bonhagen.
2667 game.use_change_when_pushing_bug =
2668 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2670 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671 tape.game_version < VERSION_IDENT(3,1,1,0)));
2674 Summary of bugfix/change:
2675 Fixed handling for blocking the field the player leaves when moving.
2677 Fixed/changed in version:
2681 Before 3.1.1, when "block last field when moving" was enabled, the field
2682 the player is leaving when moving was blocked for the time of the move,
2683 and was directly unblocked afterwards. This resulted in the last field
2684 being blocked for exactly one less than the number of frames of one player
2685 move. Additionally, even when blocking was disabled, the last field was
2686 blocked for exactly one frame.
2687 Since 3.1.1, due to changes in player movement handling, the last field
2688 is not blocked at all when blocking is disabled. When blocking is enabled,
2689 the last field is blocked for exactly the number of frames of one player
2690 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691 last field is blocked for exactly one more than the number of frames of
2694 Affected levels/tapes:
2695 (!!! yet to be determined -- probably many !!!)
2698 game.use_block_last_field_bug =
2699 (game.engine_version < VERSION_IDENT(3,1,1,0));
2701 game_em.use_single_button =
2702 (game.engine_version > VERSION_IDENT(4,0,0,2));
2704 /* ---------------------------------------------------------------------- */
2706 /* set maximal allowed number of custom element changes per game frame */
2707 game.max_num_changes_per_frame = 1;
2709 /* default scan direction: scan playfield from top/left to bottom/right */
2710 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2712 /* dynamically adjust element properties according to game engine version */
2713 InitElementPropertiesEngine(game.engine_version);
2716 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2717 printf(" tape version == %06d [%s] [file: %06d]\n",
2718 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2720 printf(" => game.engine_version == %06d\n", game.engine_version);
2723 /* ---------- initialize player's initial move delay --------------------- */
2725 /* dynamically adjust player properties according to level information */
2726 for (i = 0; i < MAX_PLAYERS; i++)
2727 game.initial_move_delay_value[i] =
2728 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2730 /* dynamically adjust player properties according to game engine version */
2731 for (i = 0; i < MAX_PLAYERS; i++)
2732 game.initial_move_delay[i] =
2733 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2734 game.initial_move_delay_value[i] : 0);
2736 /* ---------- initialize player's initial push delay --------------------- */
2738 /* dynamically adjust player properties according to game engine version */
2739 game.initial_push_delay_value =
2740 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2742 /* ---------- initialize changing elements ------------------------------- */
2744 /* initialize changing elements information */
2745 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2747 struct ElementInfo *ei = &element_info[i];
2749 /* this pointer might have been changed in the level editor */
2750 ei->change = &ei->change_page[0];
2752 if (!IS_CUSTOM_ELEMENT(i))
2754 ei->change->target_element = EL_EMPTY_SPACE;
2755 ei->change->delay_fixed = 0;
2756 ei->change->delay_random = 0;
2757 ei->change->delay_frames = 1;
2760 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2762 ei->has_change_event[j] = FALSE;
2764 ei->event_page_nr[j] = 0;
2765 ei->event_page[j] = &ei->change_page[0];
2769 /* add changing elements from pre-defined list */
2770 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2772 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2773 struct ElementInfo *ei = &element_info[ch_delay->element];
2775 ei->change->target_element = ch_delay->target_element;
2776 ei->change->delay_fixed = ch_delay->change_delay;
2778 ei->change->pre_change_function = ch_delay->pre_change_function;
2779 ei->change->change_function = ch_delay->change_function;
2780 ei->change->post_change_function = ch_delay->post_change_function;
2782 ei->change->can_change = TRUE;
2783 ei->change->can_change_or_has_action = TRUE;
2785 ei->has_change_event[CE_DELAY] = TRUE;
2787 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2788 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2791 /* ---------- initialize internal run-time variables --------------------- */
2793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2795 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2797 for (j = 0; j < ei->num_change_pages; j++)
2799 ei->change_page[j].can_change_or_has_action =
2800 (ei->change_page[j].can_change |
2801 ei->change_page[j].has_action);
2805 /* add change events from custom element configuration */
2806 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2808 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2810 for (j = 0; j < ei->num_change_pages; j++)
2812 if (!ei->change_page[j].can_change_or_has_action)
2815 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2817 /* only add event page for the first page found with this event */
2818 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2820 ei->has_change_event[k] = TRUE;
2822 ei->event_page_nr[k] = j;
2823 ei->event_page[k] = &ei->change_page[j];
2829 /* ---------- initialize reference elements in change conditions --------- */
2831 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2833 int element = EL_CUSTOM_START + i;
2834 struct ElementInfo *ei = &element_info[element];
2836 for (j = 0; j < ei->num_change_pages; j++)
2838 int trigger_element = ei->change_page[j].initial_trigger_element;
2840 if (trigger_element >= EL_PREV_CE_8 &&
2841 trigger_element <= EL_NEXT_CE_8)
2842 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2844 ei->change_page[j].trigger_element = trigger_element;
2848 /* ---------- initialize run-time trigger player and element ------------- */
2850 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2852 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2854 for (j = 0; j < ei->num_change_pages; j++)
2856 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2857 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2858 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2859 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2860 ei->change_page[j].actual_trigger_ce_value = 0;
2861 ei->change_page[j].actual_trigger_ce_score = 0;
2865 /* ---------- initialize trigger events ---------------------------------- */
2867 /* initialize trigger events information */
2868 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2870 trigger_events[i][j] = FALSE;
2872 /* add trigger events from element change event properties */
2873 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2875 struct ElementInfo *ei = &element_info[i];
2877 for (j = 0; j < ei->num_change_pages; j++)
2879 if (!ei->change_page[j].can_change_or_has_action)
2882 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2884 int trigger_element = ei->change_page[j].trigger_element;
2886 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2888 if (ei->change_page[j].has_event[k])
2890 if (IS_GROUP_ELEMENT(trigger_element))
2892 struct ElementGroupInfo *group =
2893 element_info[trigger_element].group;
2895 for (l = 0; l < group->num_elements_resolved; l++)
2896 trigger_events[group->element_resolved[l]][k] = TRUE;
2898 else if (trigger_element == EL_ANY_ELEMENT)
2899 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2900 trigger_events[l][k] = TRUE;
2902 trigger_events[trigger_element][k] = TRUE;
2909 /* ---------- initialize push delay -------------------------------------- */
2911 /* initialize push delay values to default */
2912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2914 if (!IS_CUSTOM_ELEMENT(i))
2916 /* set default push delay values (corrected since version 3.0.7-1) */
2917 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2919 element_info[i].push_delay_fixed = 2;
2920 element_info[i].push_delay_random = 8;
2924 element_info[i].push_delay_fixed = 8;
2925 element_info[i].push_delay_random = 8;
2930 /* set push delay value for certain elements from pre-defined list */
2931 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2933 int e = push_delay_list[i].element;
2935 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2936 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2939 /* set push delay value for Supaplex elements for newer engine versions */
2940 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2942 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2944 if (IS_SP_ELEMENT(i))
2946 /* set SP push delay to just enough to push under a falling zonk */
2947 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2949 element_info[i].push_delay_fixed = delay;
2950 element_info[i].push_delay_random = 0;
2955 /* ---------- initialize move stepsize ----------------------------------- */
2957 /* initialize move stepsize values to default */
2958 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2959 if (!IS_CUSTOM_ELEMENT(i))
2960 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2962 /* set move stepsize value for certain elements from pre-defined list */
2963 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2965 int e = move_stepsize_list[i].element;
2967 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2970 /* ---------- initialize collect score ----------------------------------- */
2972 /* initialize collect score values for custom elements from initial value */
2973 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974 if (IS_CUSTOM_ELEMENT(i))
2975 element_info[i].collect_score = element_info[i].collect_score_initial;
2977 /* ---------- initialize collect count ----------------------------------- */
2979 /* initialize collect count values for non-custom elements */
2980 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2981 if (!IS_CUSTOM_ELEMENT(i))
2982 element_info[i].collect_count_initial = 0;
2984 /* add collect count values for all elements from pre-defined list */
2985 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2986 element_info[collect_count_list[i].element].collect_count_initial =
2987 collect_count_list[i].count;
2989 /* ---------- initialize access direction -------------------------------- */
2991 /* initialize access direction values to default (access from every side) */
2992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993 if (!IS_CUSTOM_ELEMENT(i))
2994 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2996 /* set access direction value for certain elements from pre-defined list */
2997 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2998 element_info[access_direction_list[i].element].access_direction =
2999 access_direction_list[i].direction;
3001 /* ---------- initialize explosion content ------------------------------- */
3002 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3004 if (IS_CUSTOM_ELEMENT(i))
3007 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3009 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3011 element_info[i].content.e[x][y] =
3012 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3013 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3014 i == EL_PLAYER_3 ? EL_EMERALD :
3015 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3016 i == EL_MOLE ? EL_EMERALD_RED :
3017 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3018 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3019 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3020 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3021 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3022 i == EL_WALL_EMERALD ? EL_EMERALD :
3023 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3024 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3025 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3026 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3027 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3028 i == EL_WALL_PEARL ? EL_PEARL :
3029 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3034 /* ---------- initialize recursion detection ------------------------------ */
3035 recursion_loop_depth = 0;
3036 recursion_loop_detected = FALSE;
3037 recursion_loop_element = EL_UNDEFINED;
3039 /* ---------- initialize graphics engine ---------------------------------- */
3040 game.scroll_delay_value =
3041 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3042 setup.scroll_delay ? setup.scroll_delay_value : 0);
3043 game.scroll_delay_value =
3044 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3046 /* ---------- initialize game engine snapshots ---------------------------- */
3047 for (i = 0; i < MAX_PLAYERS; i++)
3048 game.snapshot.last_action[i] = 0;
3049 game.snapshot.changed_action = FALSE;
3050 game.snapshot.collected_item = FALSE;
3051 game.snapshot.mode =
3052 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3053 SNAPSHOT_MODE_EVERY_STEP :
3054 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3055 SNAPSHOT_MODE_EVERY_MOVE :
3056 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3057 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3058 game.snapshot.save_snapshot = FALSE;
3061 int get_num_special_action(int element, int action_first, int action_last)
3063 int num_special_action = 0;
3066 for (i = action_first; i <= action_last; i++)
3068 boolean found = FALSE;
3070 for (j = 0; j < NUM_DIRECTIONS; j++)
3071 if (el_act_dir2img(element, i, j) !=
3072 el_act_dir2img(element, ACTION_DEFAULT, j))
3076 num_special_action++;
3081 return num_special_action;
3086 =============================================================================
3088 -----------------------------------------------------------------------------
3089 initialize and start new game
3090 =============================================================================
3095 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3096 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3097 int fade_mask = REDRAW_FIELD;
3099 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3100 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3101 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3102 int initial_move_dir = MV_DOWN;
3105 // required here to update video display before fading (FIX THIS)
3106 DrawMaskedBorder(REDRAW_DOOR_2);
3108 if (!game.restart_level)
3109 CloseDoor(DOOR_CLOSE_1);
3111 SetGameStatus(GAME_MODE_PLAYING);
3113 if (level_editor_test_game)
3114 FadeSkipNextFadeIn();
3116 FadeSetEnterScreen();
3118 if (CheckIfGlobalBorderHasChanged())
3119 fade_mask = REDRAW_ALL;
3121 FadeSoundsAndMusic();
3123 ExpireSoundLoops(TRUE);
3127 /* needed if different viewport properties defined for playing */
3128 ChangeViewportPropertiesIfNeeded();
3132 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3134 DrawCompleteVideoDisplay();
3137 InitGameControlValues();
3139 /* don't play tapes over network */
3140 network_playing = (options.network && !tape.playing);
3142 for (i = 0; i < MAX_PLAYERS; i++)
3144 struct PlayerInfo *player = &stored_player[i];
3146 player->index_nr = i;
3147 player->index_bit = (1 << i);
3148 player->element_nr = EL_PLAYER_1 + i;
3150 player->present = FALSE;
3151 player->active = FALSE;
3152 player->mapped = FALSE;
3154 player->killed = FALSE;
3155 player->reanimated = FALSE;
3158 player->effective_action = 0;
3159 player->programmed_action = 0;
3162 player->score_final = 0;
3164 player->gems_still_needed = level.gems_needed;
3165 player->sokobanfields_still_needed = 0;
3166 player->lights_still_needed = 0;
3167 player->friends_still_needed = 0;
3169 for (j = 0; j < MAX_NUM_KEYS; j++)
3170 player->key[j] = FALSE;
3172 player->num_white_keys = 0;
3174 player->dynabomb_count = 0;
3175 player->dynabomb_size = 1;
3176 player->dynabombs_left = 0;
3177 player->dynabomb_xl = FALSE;
3179 player->MovDir = initial_move_dir;
3182 player->GfxDir = initial_move_dir;
3183 player->GfxAction = ACTION_DEFAULT;
3185 player->StepFrame = 0;
3187 player->initial_element = player->element_nr;
3188 player->artwork_element =
3189 (level.use_artwork_element[i] ? level.artwork_element[i] :
3190 player->element_nr);
3191 player->use_murphy = FALSE;
3193 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3194 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3196 player->gravity = level.initial_player_gravity[i];
3198 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3200 player->actual_frame_counter = 0;
3202 player->step_counter = 0;
3204 player->last_move_dir = initial_move_dir;
3206 player->is_active = FALSE;
3208 player->is_waiting = FALSE;
3209 player->is_moving = FALSE;
3210 player->is_auto_moving = FALSE;
3211 player->is_digging = FALSE;
3212 player->is_snapping = FALSE;
3213 player->is_collecting = FALSE;
3214 player->is_pushing = FALSE;
3215 player->is_switching = FALSE;
3216 player->is_dropping = FALSE;
3217 player->is_dropping_pressed = FALSE;
3219 player->is_bored = FALSE;
3220 player->is_sleeping = FALSE;
3222 player->was_waiting = TRUE;
3223 player->was_moving = FALSE;
3224 player->was_snapping = FALSE;
3225 player->was_dropping = FALSE;
3227 player->force_dropping = FALSE;
3229 player->frame_counter_bored = -1;
3230 player->frame_counter_sleeping = -1;
3232 player->anim_delay_counter = 0;
3233 player->post_delay_counter = 0;
3235 player->dir_waiting = initial_move_dir;
3236 player->action_waiting = ACTION_DEFAULT;
3237 player->last_action_waiting = ACTION_DEFAULT;
3238 player->special_action_bored = ACTION_DEFAULT;
3239 player->special_action_sleeping = ACTION_DEFAULT;
3241 player->switch_x = -1;
3242 player->switch_y = -1;
3244 player->drop_x = -1;
3245 player->drop_y = -1;
3247 player->show_envelope = 0;
3249 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3251 player->push_delay = -1; /* initialized when pushing starts */
3252 player->push_delay_value = game.initial_push_delay_value;
3254 player->drop_delay = 0;
3255 player->drop_pressed_delay = 0;
3257 player->last_jx = -1;
3258 player->last_jy = -1;
3262 player->shield_normal_time_left = 0;
3263 player->shield_deadly_time_left = 0;
3265 player->inventory_infinite_element = EL_UNDEFINED;
3266 player->inventory_size = 0;
3268 if (level.use_initial_inventory[i])
3270 for (j = 0; j < level.initial_inventory_size[i]; j++)
3272 int element = level.initial_inventory_content[i][j];
3273 int collect_count = element_info[element].collect_count_initial;
3276 if (!IS_CUSTOM_ELEMENT(element))
3279 if (collect_count == 0)
3280 player->inventory_infinite_element = element;
3282 for (k = 0; k < collect_count; k++)
3283 if (player->inventory_size < MAX_INVENTORY_SIZE)
3284 player->inventory_element[player->inventory_size++] = element;
3288 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3289 SnapField(player, 0, 0);
3291 player->LevelSolved = FALSE;
3292 player->GameOver = FALSE;
3294 player->LevelSolved_GameWon = FALSE;
3295 player->LevelSolved_GameEnd = FALSE;
3296 player->LevelSolved_PanelOff = FALSE;
3297 player->LevelSolved_SaveTape = FALSE;
3298 player->LevelSolved_SaveScore = FALSE;
3299 player->LevelSolved_CountingTime = 0;
3300 player->LevelSolved_CountingScore = 0;
3302 map_player_action[i] = i;
3305 network_player_action_received = FALSE;
3307 #if defined(NETWORK_AVALIABLE)
3308 /* initial null action */
3309 if (network_playing)
3310 SendToServer_MovePlayer(MV_NONE);
3319 TimeLeft = level.time;
3322 ScreenMovDir = MV_NONE;
3326 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3328 AllPlayersGone = FALSE;
3330 game.no_time_limit = (level.time == 0);
3332 game.yamyam_content_nr = 0;
3333 game.robot_wheel_active = FALSE;
3334 game.magic_wall_active = FALSE;
3335 game.magic_wall_time_left = 0;
3336 game.light_time_left = 0;
3337 game.timegate_time_left = 0;
3338 game.switchgate_pos = 0;
3339 game.wind_direction = level.wind_direction_initial;
3341 game.lenses_time_left = 0;
3342 game.magnify_time_left = 0;
3344 game.ball_state = level.ball_state_initial;
3345 game.ball_content_nr = 0;
3347 game.envelope_active = FALSE;
3349 /* set focus to local player for network games, else to all players */
3350 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3351 game.centered_player_nr_next = game.centered_player_nr;
3352 game.set_centered_player = FALSE;
3354 if (network_playing && tape.recording)
3356 /* store client dependent player focus when recording network games */
3357 tape.centered_player_nr_next = game.centered_player_nr_next;
3358 tape.set_centered_player = TRUE;
3361 for (i = 0; i < NUM_BELTS; i++)
3363 game.belt_dir[i] = MV_NONE;
3364 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3367 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3368 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3370 #if DEBUG_INIT_PLAYER
3373 printf("Player status at level initialization:\n");
3377 SCAN_PLAYFIELD(x, y)
3379 Feld[x][y] = level.field[x][y];
3380 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3381 ChangeDelay[x][y] = 0;
3382 ChangePage[x][y] = -1;
3383 CustomValue[x][y] = 0; /* initialized in InitField() */
3384 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3386 WasJustMoving[x][y] = 0;
3387 WasJustFalling[x][y] = 0;
3388 CheckCollision[x][y] = 0;
3389 CheckImpact[x][y] = 0;
3391 Pushed[x][y] = FALSE;
3393 ChangeCount[x][y] = 0;
3394 ChangeEvent[x][y] = -1;
3396 ExplodePhase[x][y] = 0;
3397 ExplodeDelay[x][y] = 0;
3398 ExplodeField[x][y] = EX_TYPE_NONE;
3400 RunnerVisit[x][y] = 0;
3401 PlayerVisit[x][y] = 0;
3404 GfxRandom[x][y] = INIT_GFX_RANDOM();
3405 GfxElement[x][y] = EL_UNDEFINED;
3406 GfxAction[x][y] = ACTION_DEFAULT;
3407 GfxDir[x][y] = MV_NONE;
3408 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3411 SCAN_PLAYFIELD(x, y)
3413 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3415 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3417 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3420 InitField(x, y, TRUE);
3422 ResetGfxAnimation(x, y);
3427 for (i = 0; i < MAX_PLAYERS; i++)
3429 struct PlayerInfo *player = &stored_player[i];
3431 /* set number of special actions for bored and sleeping animation */
3432 player->num_special_action_bored =
3433 get_num_special_action(player->artwork_element,
3434 ACTION_BORING_1, ACTION_BORING_LAST);
3435 player->num_special_action_sleeping =
3436 get_num_special_action(player->artwork_element,
3437 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3440 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3441 emulate_sb ? EMU_SOKOBAN :
3442 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3444 /* initialize type of slippery elements */
3445 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3447 if (!IS_CUSTOM_ELEMENT(i))
3449 /* default: elements slip down either to the left or right randomly */
3450 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3452 /* SP style elements prefer to slip down on the left side */
3453 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3454 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3456 /* BD style elements prefer to slip down on the left side */
3457 if (game.emulation == EMU_BOULDERDASH)
3458 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3462 /* initialize explosion and ignition delay */
3463 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465 if (!IS_CUSTOM_ELEMENT(i))
3468 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3469 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3470 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3471 int last_phase = (num_phase + 1) * delay;
3472 int half_phase = (num_phase / 2) * delay;
3474 element_info[i].explosion_delay = last_phase - 1;
3475 element_info[i].ignition_delay = half_phase;
3477 if (i == EL_BLACK_ORB)
3478 element_info[i].ignition_delay = 1;
3482 /* correct non-moving belts to start moving left */
3483 for (i = 0; i < NUM_BELTS; i++)
3484 if (game.belt_dir[i] == MV_NONE)
3485 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3487 #if USE_NEW_PLAYER_ASSIGNMENTS
3488 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3489 /* choose default local player */
3490 local_player = &stored_player[0];
3492 for (i = 0; i < MAX_PLAYERS; i++)
3493 stored_player[i].connected = FALSE;
3495 local_player->connected = TRUE;
3496 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3500 for (i = 0; i < MAX_PLAYERS; i++)
3501 stored_player[i].connected = tape.player_participates[i];
3503 else if (game.team_mode && !options.network)
3505 /* try to guess locally connected team mode players (needed for correct
3506 assignment of player figures from level to locally playing players) */
3508 for (i = 0; i < MAX_PLAYERS; i++)
3509 if (setup.input[i].use_joystick ||
3510 setup.input[i].key.left != KSYM_UNDEFINED)
3511 stored_player[i].connected = TRUE;
3514 #if DEBUG_INIT_PLAYER
3517 printf("Player status after level initialization:\n");
3519 for (i = 0; i < MAX_PLAYERS; i++)
3521 struct PlayerInfo *player = &stored_player[i];
3523 printf("- player %d: present == %d, connected == %d, active == %d",
3529 if (local_player == player)
3530 printf(" (local player)");
3537 #if DEBUG_INIT_PLAYER
3539 printf("Reassigning players ...\n");
3542 /* check if any connected player was not found in playfield */
3543 for (i = 0; i < MAX_PLAYERS; i++)
3545 struct PlayerInfo *player = &stored_player[i];
3547 if (player->connected && !player->present)
3549 struct PlayerInfo *field_player = NULL;
3551 #if DEBUG_INIT_PLAYER
3553 printf("- looking for field player for player %d ...\n", i + 1);
3556 /* assign first free player found that is present in the playfield */
3558 /* first try: look for unmapped playfield player that is not connected */
3559 for (j = 0; j < MAX_PLAYERS; j++)
3560 if (field_player == NULL &&
3561 stored_player[j].present &&
3562 !stored_player[j].mapped &&
3563 !stored_player[j].connected)
3564 field_player = &stored_player[j];
3566 /* second try: look for *any* unmapped playfield player */
3567 for (j = 0; j < MAX_PLAYERS; j++)
3568 if (field_player == NULL &&
3569 stored_player[j].present &&
3570 !stored_player[j].mapped)
3571 field_player = &stored_player[j];
3573 if (field_player != NULL)
3575 int jx = field_player->jx, jy = field_player->jy;
3577 #if DEBUG_INIT_PLAYER
3579 printf("- found player %d\n", field_player->index_nr + 1);
3582 player->present = FALSE;
3583 player->active = FALSE;
3585 field_player->present = TRUE;
3586 field_player->active = TRUE;
3589 player->initial_element = field_player->initial_element;
3590 player->artwork_element = field_player->artwork_element;
3592 player->block_last_field = field_player->block_last_field;
3593 player->block_delay_adjustment = field_player->block_delay_adjustment;
3596 StorePlayer[jx][jy] = field_player->element_nr;
3598 field_player->jx = field_player->last_jx = jx;
3599 field_player->jy = field_player->last_jy = jy;
3601 if (local_player == player)
3602 local_player = field_player;
3604 map_player_action[field_player->index_nr] = i;
3606 field_player->mapped = TRUE;
3608 #if DEBUG_INIT_PLAYER
3610 printf("- map_player_action[%d] == %d\n",
3611 field_player->index_nr + 1, i + 1);
3616 if (player->connected && player->present)
3617 player->mapped = TRUE;
3620 #if DEBUG_INIT_PLAYER
3623 printf("Player status after player assignment (first stage):\n");
3625 for (i = 0; i < MAX_PLAYERS; i++)
3627 struct PlayerInfo *player = &stored_player[i];
3629 printf("- player %d: present == %d, connected == %d, active == %d",
3635 if (local_player == player)
3636 printf(" (local player)");
3645 /* check if any connected player was not found in playfield */
3646 for (i = 0; i < MAX_PLAYERS; i++)
3648 struct PlayerInfo *player = &stored_player[i];
3650 if (player->connected && !player->present)
3652 for (j = 0; j < MAX_PLAYERS; j++)
3654 struct PlayerInfo *field_player = &stored_player[j];
3655 int jx = field_player->jx, jy = field_player->jy;
3657 /* assign first free player found that is present in the playfield */
3658 if (field_player->present && !field_player->connected)
3660 player->present = TRUE;
3661 player->active = TRUE;
3663 field_player->present = FALSE;
3664 field_player->active = FALSE;
3666 player->initial_element = field_player->initial_element;
3667 player->artwork_element = field_player->artwork_element;
3669 player->block_last_field = field_player->block_last_field;
3670 player->block_delay_adjustment = field_player->block_delay_adjustment;
3672 StorePlayer[jx][jy] = player->element_nr;
3674 player->jx = player->last_jx = jx;
3675 player->jy = player->last_jy = jy;
3685 printf("::: local_player->present == %d\n", local_player->present);
3690 /* when playing a tape, eliminate all players who do not participate */
3692 #if USE_NEW_PLAYER_ASSIGNMENTS
3694 if (!game.team_mode)
3696 for (i = 0; i < MAX_PLAYERS; i++)
3698 if (stored_player[i].active &&
3699 !tape.player_participates[map_player_action[i]])
3701 struct PlayerInfo *player = &stored_player[i];
3702 int jx = player->jx, jy = player->jy;
3704 #if DEBUG_INIT_PLAYER
3706 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3709 player->active = FALSE;
3710 StorePlayer[jx][jy] = 0;
3711 Feld[jx][jy] = EL_EMPTY;
3718 for (i = 0; i < MAX_PLAYERS; i++)
3720 if (stored_player[i].active &&
3721 !tape.player_participates[i])
3723 struct PlayerInfo *player = &stored_player[i];
3724 int jx = player->jx, jy = player->jy;
3726 player->active = FALSE;
3727 StorePlayer[jx][jy] = 0;
3728 Feld[jx][jy] = EL_EMPTY;
3733 else if (!options.network && !game.team_mode) /* && !tape.playing */
3735 /* when in single player mode, eliminate all but the first active player */
3737 for (i = 0; i < MAX_PLAYERS; i++)
3739 if (stored_player[i].active)
3741 for (j = i + 1; j < MAX_PLAYERS; j++)
3743 if (stored_player[j].active)
3745 struct PlayerInfo *player = &stored_player[j];
3746 int jx = player->jx, jy = player->jy;
3748 player->active = FALSE;
3749 player->present = FALSE;
3751 StorePlayer[jx][jy] = 0;
3752 Feld[jx][jy] = EL_EMPTY;
3759 /* when recording the game, store which players take part in the game */
3762 #if USE_NEW_PLAYER_ASSIGNMENTS
3763 for (i = 0; i < MAX_PLAYERS; i++)
3764 if (stored_player[i].connected)
3765 tape.player_participates[i] = TRUE;
3767 for (i = 0; i < MAX_PLAYERS; i++)
3768 if (stored_player[i].active)
3769 tape.player_participates[i] = TRUE;
3773 #if DEBUG_INIT_PLAYER
3776 printf("Player status after player assignment (final stage):\n");
3778 for (i = 0; i < MAX_PLAYERS; i++)
3780 struct PlayerInfo *player = &stored_player[i];
3782 printf("- player %d: present == %d, connected == %d, active == %d",
3788 if (local_player == player)
3789 printf(" (local player)");
3796 if (BorderElement == EL_EMPTY)
3799 SBX_Right = lev_fieldx - SCR_FIELDX;
3801 SBY_Lower = lev_fieldy - SCR_FIELDY;
3806 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3808 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3811 if (full_lev_fieldx <= SCR_FIELDX)
3812 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3813 if (full_lev_fieldy <= SCR_FIELDY)
3814 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3816 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3818 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3821 /* if local player not found, look for custom element that might create
3822 the player (make some assumptions about the right custom element) */
3823 if (!local_player->present)
3825 int start_x = 0, start_y = 0;
3826 int found_rating = 0;
3827 int found_element = EL_UNDEFINED;
3828 int player_nr = local_player->index_nr;
3830 SCAN_PLAYFIELD(x, y)
3832 int element = Feld[x][y];
3837 if (level.use_start_element[player_nr] &&
3838 level.start_element[player_nr] == element &&
3845 found_element = element;
3848 if (!IS_CUSTOM_ELEMENT(element))
3851 if (CAN_CHANGE(element))
3853 for (i = 0; i < element_info[element].num_change_pages; i++)
3855 /* check for player created from custom element as single target */
3856 content = element_info[element].change_page[i].target_element;
3857 is_player = ELEM_IS_PLAYER(content);
3859 if (is_player && (found_rating < 3 ||
3860 (found_rating == 3 && element < found_element)))
3866 found_element = element;
3871 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3873 /* check for player created from custom element as explosion content */
3874 content = element_info[element].content.e[xx][yy];
3875 is_player = ELEM_IS_PLAYER(content);
3877 if (is_player && (found_rating < 2 ||
3878 (found_rating == 2 && element < found_element)))
3880 start_x = x + xx - 1;
3881 start_y = y + yy - 1;
3884 found_element = element;
3887 if (!CAN_CHANGE(element))
3890 for (i = 0; i < element_info[element].num_change_pages; i++)
3892 /* check for player created from custom element as extended target */
3894 element_info[element].change_page[i].target_content.e[xx][yy];
3896 is_player = ELEM_IS_PLAYER(content);
3898 if (is_player && (found_rating < 1 ||
3899 (found_rating == 1 && element < found_element)))
3901 start_x = x + xx - 1;
3902 start_y = y + yy - 1;
3905 found_element = element;
3911 scroll_x = SCROLL_POSITION_X(start_x);
3912 scroll_y = SCROLL_POSITION_Y(start_y);
3916 scroll_x = SCROLL_POSITION_X(local_player->jx);
3917 scroll_y = SCROLL_POSITION_Y(local_player->jy);
3920 /* !!! FIX THIS (START) !!! */
3921 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3923 InitGameEngine_EM();
3925 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3927 InitGameEngine_SP();
3931 DrawLevel(REDRAW_FIELD);
3934 /* after drawing the level, correct some elements */
3935 if (game.timegate_time_left == 0)
3936 CloseAllOpenTimegates();
3939 /* blit playfield from scroll buffer to normal back buffer for fading in */
3940 BlitScreenToBitmap(backbuffer);
3941 /* !!! FIX THIS (END) !!! */
3943 DrawMaskedBorder(fade_mask);
3948 // full screen redraw is required at this point in the following cases:
3949 // - special editor door undrawn when game was started from level editor
3950 // - drawing area (playfield) was changed and has to be removed completely
3951 redraw_mask = REDRAW_ALL;
3955 if (!game.restart_level)
3957 /* copy default game door content to main double buffer */
3959 /* !!! CHECK AGAIN !!! */
3960 SetPanelBackground();
3961 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3962 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3965 SetPanelBackground();
3966 SetDrawBackgroundMask(REDRAW_DOOR_1);
3968 UpdateAndDisplayGameControlValues();
3970 if (!game.restart_level)
3976 CreateGameButtons();
3978 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3979 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3980 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3985 /* copy actual game door content to door double buffer for OpenDoor() */
3986 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3988 OpenDoor(DOOR_OPEN_ALL);
3990 PlaySound(SND_GAME_STARTING);
3992 if (setup.sound_music)
3995 KeyboardAutoRepeatOffUnlessAutoplay();
3997 #if DEBUG_INIT_PLAYER
4000 printf("Player status (final):\n");
4002 for (i = 0; i < MAX_PLAYERS; i++)
4004 struct PlayerInfo *player = &stored_player[i];
4006 printf("- player %d: present == %d, connected == %d, active == %d",
4012 if (local_player == player)
4013 printf(" (local player)");
4026 if (!game.restart_level && !tape.playing)
4028 LevelStats_incPlayed(level_nr);
4030 SaveLevelSetup_SeriesInfo();
4033 game.restart_level = FALSE;
4035 SaveEngineSnapshotToListInitial();
4038 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4039 int actual_player_x, int actual_player_y)
4041 /* this is used for non-R'n'D game engines to update certain engine values */
4043 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4045 actual_player_x = correctLevelPosX_EM(actual_player_x);
4046 actual_player_y = correctLevelPosY_EM(actual_player_y);
4049 /* needed to determine if sounds are played within the visible screen area */
4050 scroll_x = actual_scroll_x;
4051 scroll_y = actual_scroll_y;
4053 /* needed to get player position for "follow finger" playing input method */
4054 local_player->jx = actual_player_x;
4055 local_player->jy = actual_player_y;
4058 void InitMovDir(int x, int y)
4060 int i, element = Feld[x][y];
4061 static int xy[4][2] =
4068 static int direction[3][4] =
4070 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4071 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4072 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4081 Feld[x][y] = EL_BUG;
4082 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4085 case EL_SPACESHIP_RIGHT:
4086 case EL_SPACESHIP_UP:
4087 case EL_SPACESHIP_LEFT:
4088 case EL_SPACESHIP_DOWN:
4089 Feld[x][y] = EL_SPACESHIP;
4090 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4093 case EL_BD_BUTTERFLY_RIGHT:
4094 case EL_BD_BUTTERFLY_UP:
4095 case EL_BD_BUTTERFLY_LEFT:
4096 case EL_BD_BUTTERFLY_DOWN:
4097 Feld[x][y] = EL_BD_BUTTERFLY;
4098 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4101 case EL_BD_FIREFLY_RIGHT:
4102 case EL_BD_FIREFLY_UP:
4103 case EL_BD_FIREFLY_LEFT:
4104 case EL_BD_FIREFLY_DOWN:
4105 Feld[x][y] = EL_BD_FIREFLY;
4106 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4109 case EL_PACMAN_RIGHT:
4111 case EL_PACMAN_LEFT:
4112 case EL_PACMAN_DOWN:
4113 Feld[x][y] = EL_PACMAN;
4114 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4117 case EL_YAMYAM_LEFT:
4118 case EL_YAMYAM_RIGHT:
4120 case EL_YAMYAM_DOWN:
4121 Feld[x][y] = EL_YAMYAM;
4122 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4125 case EL_SP_SNIKSNAK:
4126 MovDir[x][y] = MV_UP;
4129 case EL_SP_ELECTRON:
4130 MovDir[x][y] = MV_LEFT;
4137 Feld[x][y] = EL_MOLE;
4138 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4142 if (IS_CUSTOM_ELEMENT(element))
4144 struct ElementInfo *ei = &element_info[element];
4145 int move_direction_initial = ei->move_direction_initial;
4146 int move_pattern = ei->move_pattern;
4148 if (move_direction_initial == MV_START_PREVIOUS)
4150 if (MovDir[x][y] != MV_NONE)
4153 move_direction_initial = MV_START_AUTOMATIC;
4156 if (move_direction_initial == MV_START_RANDOM)
4157 MovDir[x][y] = 1 << RND(4);
4158 else if (move_direction_initial & MV_ANY_DIRECTION)
4159 MovDir[x][y] = move_direction_initial;
4160 else if (move_pattern == MV_ALL_DIRECTIONS ||
4161 move_pattern == MV_TURNING_LEFT ||
4162 move_pattern == MV_TURNING_RIGHT ||
4163 move_pattern == MV_TURNING_LEFT_RIGHT ||
4164 move_pattern == MV_TURNING_RIGHT_LEFT ||
4165 move_pattern == MV_TURNING_RANDOM)
4166 MovDir[x][y] = 1 << RND(4);
4167 else if (move_pattern == MV_HORIZONTAL)
4168 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4169 else if (move_pattern == MV_VERTICAL)
4170 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4171 else if (move_pattern & MV_ANY_DIRECTION)
4172 MovDir[x][y] = element_info[element].move_pattern;
4173 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4174 move_pattern == MV_ALONG_RIGHT_SIDE)
4176 /* use random direction as default start direction */
4177 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4178 MovDir[x][y] = 1 << RND(4);
4180 for (i = 0; i < NUM_DIRECTIONS; i++)
4182 int x1 = x + xy[i][0];
4183 int y1 = y + xy[i][1];
4185 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4187 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4188 MovDir[x][y] = direction[0][i];
4190 MovDir[x][y] = direction[1][i];
4199 MovDir[x][y] = 1 << RND(4);
4201 if (element != EL_BUG &&
4202 element != EL_SPACESHIP &&
4203 element != EL_BD_BUTTERFLY &&
4204 element != EL_BD_FIREFLY)
4207 for (i = 0; i < NUM_DIRECTIONS; i++)
4209 int x1 = x + xy[i][0];
4210 int y1 = y + xy[i][1];
4212 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4214 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4216 MovDir[x][y] = direction[0][i];
4219 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4220 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4222 MovDir[x][y] = direction[1][i];
4231 GfxDir[x][y] = MovDir[x][y];
4234 void InitAmoebaNr(int x, int y)
4237 int group_nr = AmoebeNachbarNr(x, y);
4241 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4243 if (AmoebaCnt[i] == 0)
4251 AmoebaNr[x][y] = group_nr;
4252 AmoebaCnt[group_nr]++;
4253 AmoebaCnt2[group_nr]++;
4256 static void PlayerWins(struct PlayerInfo *player)
4258 player->LevelSolved = TRUE;
4259 player->GameOver = TRUE;
4261 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4262 level.native_em_level->lev->score : player->score);
4264 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4266 player->LevelSolved_CountingScore = player->score_final;
4271 static int time, time_final;
4272 static int score, score_final;
4273 static int game_over_delay_1 = 0;
4274 static int game_over_delay_2 = 0;
4275 int game_over_delay_value_1 = 50;
4276 int game_over_delay_value_2 = 50;
4278 if (!local_player->LevelSolved_GameWon)
4282 /* do not start end game actions before the player stops moving (to exit) */
4283 if (local_player->MovPos)
4286 local_player->LevelSolved_GameWon = TRUE;
4287 local_player->LevelSolved_SaveTape = tape.recording;
4288 local_player->LevelSolved_SaveScore = !tape.playing;
4292 LevelStats_incSolved(level_nr);
4294 SaveLevelSetup_SeriesInfo();
4297 if (tape.auto_play) /* tape might already be stopped here */
4298 tape.auto_play_level_solved = TRUE;
4302 game_over_delay_1 = game_over_delay_value_1;
4303 game_over_delay_2 = game_over_delay_value_2;
4305 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4306 score = score_final = local_player->score_final;
4311 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4313 else if (game.no_time_limit && TimePlayed < 999)
4316 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4319 local_player->score_final = score_final;
4321 if (level_editor_test_game)
4324 score = score_final;
4326 local_player->LevelSolved_CountingTime = time;
4327 local_player->LevelSolved_CountingScore = score;
4329 game_panel_controls[GAME_PANEL_TIME].value = time;
4330 game_panel_controls[GAME_PANEL_SCORE].value = score;
4332 DisplayGameControlValues();
4335 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4337 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4339 /* close exit door after last player */
4340 if ((AllPlayersGone &&
4341 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4342 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4343 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4344 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4345 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4347 int element = Feld[ExitX][ExitY];
4349 Feld[ExitX][ExitY] =
4350 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4351 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4352 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4353 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4354 EL_EM_STEEL_EXIT_CLOSING);
4356 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4359 /* player disappears */
4360 DrawLevelField(ExitX, ExitY);
4363 for (i = 0; i < MAX_PLAYERS; i++)
4365 struct PlayerInfo *player = &stored_player[i];
4367 if (player->present)
4369 RemovePlayer(player);
4371 /* player disappears */
4372 DrawLevelField(player->jx, player->jy);
4377 PlaySound(SND_GAME_WINNING);
4380 if (game_over_delay_1 > 0)
4382 game_over_delay_1--;
4387 if (time != time_final)
4389 int time_to_go = ABS(time_final - time);
4390 int time_count_dir = (time < time_final ? +1 : -1);
4391 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4393 time += time_count_steps * time_count_dir;
4394 score += time_count_steps * level.score[SC_TIME_BONUS];
4396 local_player->LevelSolved_CountingTime = time;
4397 local_player->LevelSolved_CountingScore = score;
4399 game_panel_controls[GAME_PANEL_TIME].value = time;
4400 game_panel_controls[GAME_PANEL_SCORE].value = score;
4402 DisplayGameControlValues();
4404 if (time == time_final)
4405 StopSound(SND_GAME_LEVELTIME_BONUS);
4406 else if (setup.sound_loops)
4407 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4409 PlaySound(SND_GAME_LEVELTIME_BONUS);
4414 local_player->LevelSolved_PanelOff = TRUE;
4416 if (game_over_delay_2 > 0)
4418 game_over_delay_2--;
4429 boolean raise_level = FALSE;
4431 local_player->LevelSolved_GameEnd = TRUE;
4433 if (!global.use_envelope_request)
4434 CloseDoor(DOOR_CLOSE_1);
4436 if (local_player->LevelSolved_SaveTape)
4438 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4441 CloseDoor(DOOR_CLOSE_ALL);
4443 if (level_editor_test_game)
4445 SetGameStatus(GAME_MODE_MAIN);
4452 if (!local_player->LevelSolved_SaveScore)
4454 SetGameStatus(GAME_MODE_MAIN);
4461 if (level_nr == leveldir_current->handicap_level)
4463 leveldir_current->handicap_level++;
4465 SaveLevelSetup_SeriesInfo();
4468 if (setup.increment_levels &&
4469 level_nr < leveldir_current->last_level)
4470 raise_level = TRUE; /* advance to next level */
4472 if ((hi_pos = NewHiScore()) >= 0)
4474 SetGameStatus(GAME_MODE_SCORES);
4476 DrawHallOfFame(hi_pos);
4486 SetGameStatus(GAME_MODE_MAIN);
4502 boolean one_score_entry_per_name = !program.many_scores_per_name;
4504 LoadScore(level_nr);
4506 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4507 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4510 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4512 if (local_player->score_final > highscore[k].Score)
4514 /* player has made it to the hall of fame */
4516 if (k < MAX_SCORE_ENTRIES - 1)
4518 int m = MAX_SCORE_ENTRIES - 1;
4520 if (one_score_entry_per_name)
4522 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4523 if (strEqual(setup.player_name, highscore[l].Name))
4526 if (m == k) /* player's new highscore overwrites his old one */
4530 for (l = m; l > k; l--)
4532 strcpy(highscore[l].Name, highscore[l - 1].Name);
4533 highscore[l].Score = highscore[l - 1].Score;
4539 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4540 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4541 highscore[k].Score = local_player->score_final;
4546 else if (one_score_entry_per_name &&
4547 !strncmp(setup.player_name, highscore[k].Name,
4548 MAX_PLAYER_NAME_LEN))
4549 break; /* player already there with a higher score */
4553 SaveScore(level_nr);
4558 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4560 int element = Feld[x][y];
4561 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4562 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4563 int horiz_move = (dx != 0);
4564 int sign = (horiz_move ? dx : dy);
4565 int step = sign * element_info[element].move_stepsize;
4567 /* special values for move stepsize for spring and things on conveyor belt */
4570 if (CAN_FALL(element) &&
4571 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4572 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4573 else if (element == EL_SPRING)
4574 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4580 inline static int getElementMoveStepsize(int x, int y)
4582 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4585 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4587 if (player->GfxAction != action || player->GfxDir != dir)
4589 player->GfxAction = action;
4590 player->GfxDir = dir;
4592 player->StepFrame = 0;
4596 static void ResetGfxFrame(int x, int y)
4598 int element = Feld[x][y];
4599 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4601 if (graphic_info[graphic].anim_global_sync)
4602 GfxFrame[x][y] = FrameCounter;
4603 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4604 GfxFrame[x][y] = CustomValue[x][y];
4605 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4606 GfxFrame[x][y] = element_info[element].collect_score;
4607 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4608 GfxFrame[x][y] = ChangeDelay[x][y];
4611 static void ResetGfxAnimation(int x, int y)
4613 GfxAction[x][y] = ACTION_DEFAULT;
4614 GfxDir[x][y] = MovDir[x][y];
4617 ResetGfxFrame(x, y);
4620 static void ResetRandomAnimationValue(int x, int y)
4622 GfxRandom[x][y] = INIT_GFX_RANDOM();
4625 void InitMovingField(int x, int y, int direction)
4627 int element = Feld[x][y];
4628 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4629 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4632 boolean is_moving_before, is_moving_after;
4634 /* check if element was/is moving or being moved before/after mode change */
4635 is_moving_before = (WasJustMoving[x][y] != 0);
4636 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4638 /* reset animation only for moving elements which change direction of moving
4639 or which just started or stopped moving
4640 (else CEs with property "can move" / "not moving" are reset each frame) */
4641 if (is_moving_before != is_moving_after ||
4642 direction != MovDir[x][y])
4643 ResetGfxAnimation(x, y);
4645 MovDir[x][y] = direction;
4646 GfxDir[x][y] = direction;
4648 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4649 direction == MV_DOWN && CAN_FALL(element) ?
4650 ACTION_FALLING : ACTION_MOVING);
4652 /* this is needed for CEs with property "can move" / "not moving" */
4654 if (is_moving_after)
4656 if (Feld[newx][newy] == EL_EMPTY)
4657 Feld[newx][newy] = EL_BLOCKED;
4659 MovDir[newx][newy] = MovDir[x][y];
4661 CustomValue[newx][newy] = CustomValue[x][y];
4663 GfxFrame[newx][newy] = GfxFrame[x][y];
4664 GfxRandom[newx][newy] = GfxRandom[x][y];
4665 GfxAction[newx][newy] = GfxAction[x][y];
4666 GfxDir[newx][newy] = GfxDir[x][y];
4670 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4672 int direction = MovDir[x][y];
4673 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4674 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4680 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4682 int oldx = x, oldy = y;
4683 int direction = MovDir[x][y];
4685 if (direction == MV_LEFT)
4687 else if (direction == MV_RIGHT)
4689 else if (direction == MV_UP)
4691 else if (direction == MV_DOWN)
4694 *comes_from_x = oldx;
4695 *comes_from_y = oldy;
4698 int MovingOrBlocked2Element(int x, int y)
4700 int element = Feld[x][y];
4702 if (element == EL_BLOCKED)
4706 Blocked2Moving(x, y, &oldx, &oldy);
4707 return Feld[oldx][oldy];
4713 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4715 /* like MovingOrBlocked2Element(), but if element is moving
4716 and (x,y) is the field the moving element is just leaving,
4717 return EL_BLOCKED instead of the element value */
4718 int element = Feld[x][y];
4720 if (IS_MOVING(x, y))
4722 if (element == EL_BLOCKED)
4726 Blocked2Moving(x, y, &oldx, &oldy);
4727 return Feld[oldx][oldy];
4736 static void RemoveField(int x, int y)
4738 Feld[x][y] = EL_EMPTY;
4744 CustomValue[x][y] = 0;
4747 ChangeDelay[x][y] = 0;
4748 ChangePage[x][y] = -1;
4749 Pushed[x][y] = FALSE;
4751 GfxElement[x][y] = EL_UNDEFINED;
4752 GfxAction[x][y] = ACTION_DEFAULT;
4753 GfxDir[x][y] = MV_NONE;
4756 void RemoveMovingField(int x, int y)
4758 int oldx = x, oldy = y, newx = x, newy = y;
4759 int element = Feld[x][y];
4760 int next_element = EL_UNDEFINED;
4762 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4765 if (IS_MOVING(x, y))
4767 Moving2Blocked(x, y, &newx, &newy);
4769 if (Feld[newx][newy] != EL_BLOCKED)
4771 /* element is moving, but target field is not free (blocked), but
4772 already occupied by something different (example: acid pool);
4773 in this case, only remove the moving field, but not the target */
4775 RemoveField(oldx, oldy);
4777 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4779 TEST_DrawLevelField(oldx, oldy);
4784 else if (element == EL_BLOCKED)
4786 Blocked2Moving(x, y, &oldx, &oldy);
4787 if (!IS_MOVING(oldx, oldy))
4791 if (element == EL_BLOCKED &&
4792 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4793 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4794 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4795 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4796 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4797 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4798 next_element = get_next_element(Feld[oldx][oldy]);
4800 RemoveField(oldx, oldy);
4801 RemoveField(newx, newy);
4803 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4805 if (next_element != EL_UNDEFINED)
4806 Feld[oldx][oldy] = next_element;
4808 TEST_DrawLevelField(oldx, oldy);
4809 TEST_DrawLevelField(newx, newy);
4812 void DrawDynamite(int x, int y)
4814 int sx = SCREENX(x), sy = SCREENY(y);
4815 int graphic = el2img(Feld[x][y]);
4818 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4821 if (IS_WALKABLE_INSIDE(Back[x][y]))
4825 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4826 else if (Store[x][y])
4827 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4829 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4831 if (Back[x][y] || Store[x][y])
4832 DrawGraphicThruMask(sx, sy, graphic, frame);
4834 DrawGraphic(sx, sy, graphic, frame);
4837 void CheckDynamite(int x, int y)
4839 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4843 if (MovDelay[x][y] != 0)
4846 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4852 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4857 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4859 boolean num_checked_players = 0;
4862 for (i = 0; i < MAX_PLAYERS; i++)
4864 if (stored_player[i].active)
4866 int sx = stored_player[i].jx;
4867 int sy = stored_player[i].jy;
4869 if (num_checked_players == 0)
4876 *sx1 = MIN(*sx1, sx);
4877 *sy1 = MIN(*sy1, sy);
4878 *sx2 = MAX(*sx2, sx);
4879 *sy2 = MAX(*sy2, sy);
4882 num_checked_players++;
4887 static boolean checkIfAllPlayersFitToScreen_RND()
4889 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4891 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4893 return (sx2 - sx1 < SCR_FIELDX &&
4894 sy2 - sy1 < SCR_FIELDY);
4897 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4899 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4901 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4903 *sx = (sx1 + sx2) / 2;
4904 *sy = (sy1 + sy2) / 2;
4907 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4908 boolean center_screen, boolean quick_relocation)
4910 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4911 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4912 boolean no_delay = (tape.warp_forward);
4913 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4914 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4915 int new_scroll_x, new_scroll_y;
4917 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4919 /* case 1: quick relocation inside visible screen (without scrolling) */
4926 if (!level.shifted_relocation || center_screen)
4928 /* relocation _with_ centering of screen */
4930 new_scroll_x = SCROLL_POSITION_X(x);
4931 new_scroll_y = SCROLL_POSITION_Y(y);
4935 /* relocation _without_ centering of screen */
4937 int center_scroll_x = SCROLL_POSITION_X(old_x);
4938 int center_scroll_y = SCROLL_POSITION_Y(old_y);
4939 int offset_x = x + (scroll_x - center_scroll_x);
4940 int offset_y = y + (scroll_y - center_scroll_y);
4942 /* for new screen position, apply previous offset to center position */
4943 new_scroll_x = SCROLL_POSITION_X(offset_x);
4944 new_scroll_y = SCROLL_POSITION_Y(offset_y);
4947 if (quick_relocation)
4949 /* case 2: quick relocation (redraw without visible scrolling) */
4951 scroll_x = new_scroll_x;
4952 scroll_y = new_scroll_y;
4959 /* case 3: visible relocation (with scrolling to new position) */
4961 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4963 SetVideoFrameDelay(wait_delay_value);
4965 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4968 int fx = FX, fy = FY;
4970 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4971 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4973 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4979 fx += dx * TILEX / 2;
4980 fy += dy * TILEY / 2;
4982 ScrollLevel(dx, dy);
4985 /* scroll in two steps of half tile size to make things smoother */
4986 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4988 /* scroll second step to align at full tile size */
4989 BlitScreenToBitmap(window);
4995 SetVideoFrameDelay(frame_delay_value_old);
4998 void RelocatePlayer(int jx, int jy, int el_player_raw)
5000 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5001 int player_nr = GET_PLAYER_NR(el_player);
5002 struct PlayerInfo *player = &stored_player[player_nr];
5003 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5004 boolean no_delay = (tape.warp_forward);
5005 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5006 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5007 int old_jx = player->jx;
5008 int old_jy = player->jy;
5009 int old_element = Feld[old_jx][old_jy];
5010 int element = Feld[jx][jy];
5011 boolean player_relocated = (old_jx != jx || old_jy != jy);
5013 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5014 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5015 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5016 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5017 int leave_side_horiz = move_dir_horiz;
5018 int leave_side_vert = move_dir_vert;
5019 int enter_side = enter_side_horiz | enter_side_vert;
5020 int leave_side = leave_side_horiz | leave_side_vert;
5022 if (player->GameOver) /* do not reanimate dead player */
5025 if (!player_relocated) /* no need to relocate the player */
5028 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5030 RemoveField(jx, jy); /* temporarily remove newly placed player */
5031 DrawLevelField(jx, jy);
5034 if (player->present)
5036 while (player->MovPos)
5038 ScrollPlayer(player, SCROLL_GO_ON);
5039 ScrollScreen(NULL, SCROLL_GO_ON);
5041 AdvanceFrameAndPlayerCounters(player->index_nr);
5045 BackToFront_WithFrameDelay(wait_delay_value);
5048 DrawPlayer(player); /* needed here only to cleanup last field */
5049 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5051 player->is_moving = FALSE;
5054 if (IS_CUSTOM_ELEMENT(old_element))
5055 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5057 player->index_bit, leave_side);
5059 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5061 player->index_bit, leave_side);
5063 Feld[jx][jy] = el_player;
5064 InitPlayerField(jx, jy, el_player, TRUE);
5066 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5067 possible that the relocation target field did not contain a player element,
5068 but a walkable element, to which the new player was relocated -- in this
5069 case, restore that (already initialized!) element on the player field */
5070 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5072 Feld[jx][jy] = element; /* restore previously existing element */
5075 /* only visually relocate centered player */
5076 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5077 FALSE, level.instant_relocation);
5079 TestIfPlayerTouchesBadThing(jx, jy);
5080 TestIfPlayerTouchesCustomElement(jx, jy);
5082 if (IS_CUSTOM_ELEMENT(element))
5083 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5084 player->index_bit, enter_side);
5086 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5087 player->index_bit, enter_side);
5089 if (player->is_switching)
5091 /* ensure that relocation while still switching an element does not cause
5092 a new element to be treated as also switched directly after relocation
5093 (this is important for teleporter switches that teleport the player to
5094 a place where another teleporter switch is in the same direction, which
5095 would then incorrectly be treated as immediately switched before the
5096 direction key that caused the switch was released) */
5098 player->switch_x += jx - old_jx;
5099 player->switch_y += jy - old_jy;
5103 void Explode(int ex, int ey, int phase, int mode)
5109 /* !!! eliminate this variable !!! */
5110 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5112 if (game.explosions_delayed)
5114 ExplodeField[ex][ey] = mode;
5118 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5120 int center_element = Feld[ex][ey];
5121 int artwork_element, explosion_element; /* set these values later */
5123 /* remove things displayed in background while burning dynamite */
5124 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5127 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5129 /* put moving element to center field (and let it explode there) */
5130 center_element = MovingOrBlocked2Element(ex, ey);
5131 RemoveMovingField(ex, ey);
5132 Feld[ex][ey] = center_element;
5135 /* now "center_element" is finally determined -- set related values now */
5136 artwork_element = center_element; /* for custom player artwork */
5137 explosion_element = center_element; /* for custom player artwork */
5139 if (IS_PLAYER(ex, ey))
5141 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5143 artwork_element = stored_player[player_nr].artwork_element;
5145 if (level.use_explosion_element[player_nr])
5147 explosion_element = level.explosion_element[player_nr];
5148 artwork_element = explosion_element;
5152 if (mode == EX_TYPE_NORMAL ||
5153 mode == EX_TYPE_CENTER ||
5154 mode == EX_TYPE_CROSS)
5155 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5157 last_phase = element_info[explosion_element].explosion_delay + 1;
5159 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5161 int xx = x - ex + 1;
5162 int yy = y - ey + 1;
5165 if (!IN_LEV_FIELD(x, y) ||
5166 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5167 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5170 element = Feld[x][y];
5172 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5174 element = MovingOrBlocked2Element(x, y);
5176 if (!IS_EXPLOSION_PROOF(element))
5177 RemoveMovingField(x, y);
5180 /* indestructible elements can only explode in center (but not flames) */
5181 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5182 mode == EX_TYPE_BORDER)) ||
5183 element == EL_FLAMES)
5186 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5187 behaviour, for example when touching a yamyam that explodes to rocks
5188 with active deadly shield, a rock is created under the player !!! */
5189 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5191 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5192 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5193 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5195 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5198 if (IS_ACTIVE_BOMB(element))
5200 /* re-activate things under the bomb like gate or penguin */
5201 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5208 /* save walkable background elements while explosion on same tile */
5209 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5210 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5211 Back[x][y] = element;
5213 /* ignite explodable elements reached by other explosion */
5214 if (element == EL_EXPLOSION)
5215 element = Store2[x][y];
5217 if (AmoebaNr[x][y] &&
5218 (element == EL_AMOEBA_FULL ||
5219 element == EL_BD_AMOEBA ||
5220 element == EL_AMOEBA_GROWING))
5222 AmoebaCnt[AmoebaNr[x][y]]--;
5223 AmoebaCnt2[AmoebaNr[x][y]]--;
5228 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5230 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5232 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5234 if (PLAYERINFO(ex, ey)->use_murphy)
5235 Store[x][y] = EL_EMPTY;
5238 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5239 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5240 else if (ELEM_IS_PLAYER(center_element))
5241 Store[x][y] = EL_EMPTY;
5242 else if (center_element == EL_YAMYAM)
5243 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5244 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5245 Store[x][y] = element_info[center_element].content.e[xx][yy];
5247 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5248 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5249 otherwise) -- FIX THIS !!! */
5250 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5251 Store[x][y] = element_info[element].content.e[1][1];
5253 else if (!CAN_EXPLODE(element))
5254 Store[x][y] = element_info[element].content.e[1][1];
5257 Store[x][y] = EL_EMPTY;
5259 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5260 center_element == EL_AMOEBA_TO_DIAMOND)
5261 Store2[x][y] = element;
5263 Feld[x][y] = EL_EXPLOSION;
5264 GfxElement[x][y] = artwork_element;
5266 ExplodePhase[x][y] = 1;
5267 ExplodeDelay[x][y] = last_phase;
5272 if (center_element == EL_YAMYAM)
5273 game.yamyam_content_nr =
5274 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5286 GfxFrame[x][y] = 0; /* restart explosion animation */
5288 last_phase = ExplodeDelay[x][y];
5290 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5292 /* this can happen if the player leaves an explosion just in time */
5293 if (GfxElement[x][y] == EL_UNDEFINED)
5294 GfxElement[x][y] = EL_EMPTY;
5296 border_element = Store2[x][y];
5297 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5298 border_element = StorePlayer[x][y];
5300 if (phase == element_info[border_element].ignition_delay ||
5301 phase == last_phase)
5303 boolean border_explosion = FALSE;
5305 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5306 !PLAYER_EXPLOSION_PROTECTED(x, y))
5308 KillPlayerUnlessExplosionProtected(x, y);
5309 border_explosion = TRUE;
5311 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5313 Feld[x][y] = Store2[x][y];
5316 border_explosion = TRUE;
5318 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5320 AmoebeUmwandeln(x, y);
5322 border_explosion = TRUE;
5325 /* if an element just explodes due to another explosion (chain-reaction),
5326 do not immediately end the new explosion when it was the last frame of
5327 the explosion (as it would be done in the following "if"-statement!) */
5328 if (border_explosion && phase == last_phase)
5332 if (phase == last_phase)
5336 element = Feld[x][y] = Store[x][y];
5337 Store[x][y] = Store2[x][y] = 0;
5338 GfxElement[x][y] = EL_UNDEFINED;
5340 /* player can escape from explosions and might therefore be still alive */
5341 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5342 element <= EL_PLAYER_IS_EXPLODING_4)
5344 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5345 int explosion_element = EL_PLAYER_1 + player_nr;
5346 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5347 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5349 if (level.use_explosion_element[player_nr])
5350 explosion_element = level.explosion_element[player_nr];
5352 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5353 element_info[explosion_element].content.e[xx][yy]);
5356 /* restore probably existing indestructible background element */
5357 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5358 element = Feld[x][y] = Back[x][y];
5361 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5362 GfxDir[x][y] = MV_NONE;
5363 ChangeDelay[x][y] = 0;
5364 ChangePage[x][y] = -1;
5366 CustomValue[x][y] = 0;
5368 InitField_WithBug2(x, y, FALSE);
5370 TEST_DrawLevelField(x, y);
5372 TestIfElementTouchesCustomElement(x, y);
5374 if (GFX_CRUMBLED(element))
5375 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5377 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5378 StorePlayer[x][y] = 0;
5380 if (ELEM_IS_PLAYER(element))
5381 RelocatePlayer(x, y, element);
5383 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5385 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5386 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5389 TEST_DrawLevelFieldCrumbled(x, y);
5391 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5393 DrawLevelElement(x, y, Back[x][y]);
5394 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5396 else if (IS_WALKABLE_UNDER(Back[x][y]))
5398 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5399 DrawLevelElementThruMask(x, y, Back[x][y]);
5401 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5402 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5406 void DynaExplode(int ex, int ey)
5409 int dynabomb_element = Feld[ex][ey];
5410 int dynabomb_size = 1;
5411 boolean dynabomb_xl = FALSE;
5412 struct PlayerInfo *player;
5413 static int xy[4][2] =
5421 if (IS_ACTIVE_BOMB(dynabomb_element))
5423 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5424 dynabomb_size = player->dynabomb_size;
5425 dynabomb_xl = player->dynabomb_xl;
5426 player->dynabombs_left++;
5429 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5431 for (i = 0; i < NUM_DIRECTIONS; i++)
5433 for (j = 1; j <= dynabomb_size; j++)
5435 int x = ex + j * xy[i][0];
5436 int y = ey + j * xy[i][1];
5439 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5442 element = Feld[x][y];
5444 /* do not restart explosions of fields with active bombs */
5445 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5448 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5450 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5451 !IS_DIGGABLE(element) && !dynabomb_xl)
5457 void Bang(int x, int y)
5459 int element = MovingOrBlocked2Element(x, y);
5460 int explosion_type = EX_TYPE_NORMAL;
5462 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5464 struct PlayerInfo *player = PLAYERINFO(x, y);
5466 element = Feld[x][y] = player->initial_element;
5468 if (level.use_explosion_element[player->index_nr])
5470 int explosion_element = level.explosion_element[player->index_nr];
5472 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5473 explosion_type = EX_TYPE_CROSS;
5474 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5475 explosion_type = EX_TYPE_CENTER;
5483 case EL_BD_BUTTERFLY:
5486 case EL_DARK_YAMYAM:
5490 RaiseScoreElement(element);
5493 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5494 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5495 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5496 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5497 case EL_DYNABOMB_INCREASE_NUMBER:
5498 case EL_DYNABOMB_INCREASE_SIZE:
5499 case EL_DYNABOMB_INCREASE_POWER:
5500 explosion_type = EX_TYPE_DYNA;
5503 case EL_DC_LANDMINE:
5504 explosion_type = EX_TYPE_CENTER;
5509 case EL_LAMP_ACTIVE:
5510 case EL_AMOEBA_TO_DIAMOND:
5511 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5512 explosion_type = EX_TYPE_CENTER;
5516 if (element_info[element].explosion_type == EXPLODES_CROSS)
5517 explosion_type = EX_TYPE_CROSS;
5518 else if (element_info[element].explosion_type == EXPLODES_1X1)
5519 explosion_type = EX_TYPE_CENTER;
5523 if (explosion_type == EX_TYPE_DYNA)
5526 Explode(x, y, EX_PHASE_START, explosion_type);
5528 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5531 void SplashAcid(int x, int y)
5533 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5534 (!IN_LEV_FIELD(x - 1, y - 2) ||
5535 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5536 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5538 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5539 (!IN_LEV_FIELD(x + 1, y - 2) ||
5540 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5541 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5543 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5546 static void InitBeltMovement()
5548 static int belt_base_element[4] =
5550 EL_CONVEYOR_BELT_1_LEFT,
5551 EL_CONVEYOR_BELT_2_LEFT,
5552 EL_CONVEYOR_BELT_3_LEFT,
5553 EL_CONVEYOR_BELT_4_LEFT
5555 static int belt_base_active_element[4] =
5557 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5558 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5559 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5560 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5565 /* set frame order for belt animation graphic according to belt direction */
5566 for (i = 0; i < NUM_BELTS; i++)
5570 for (j = 0; j < NUM_BELT_PARTS; j++)
5572 int element = belt_base_active_element[belt_nr] + j;
5573 int graphic_1 = el2img(element);
5574 int graphic_2 = el2panelimg(element);
5576 if (game.belt_dir[i] == MV_LEFT)
5578 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5579 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5583 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5584 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5589 SCAN_PLAYFIELD(x, y)
5591 int element = Feld[x][y];
5593 for (i = 0; i < NUM_BELTS; i++)
5595 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5597 int e_belt_nr = getBeltNrFromBeltElement(element);
5600 if (e_belt_nr == belt_nr)
5602 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5604 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5611 static void ToggleBeltSwitch(int x, int y)
5613 static int belt_base_element[4] =
5615 EL_CONVEYOR_BELT_1_LEFT,
5616 EL_CONVEYOR_BELT_2_LEFT,
5617 EL_CONVEYOR_BELT_3_LEFT,
5618 EL_CONVEYOR_BELT_4_LEFT
5620 static int belt_base_active_element[4] =
5622 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5623 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5624 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5625 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5627 static int belt_base_switch_element[4] =
5629 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5630 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5631 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5632 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5634 static int belt_move_dir[4] =
5642 int element = Feld[x][y];
5643 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5644 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5645 int belt_dir = belt_move_dir[belt_dir_nr];
5648 if (!IS_BELT_SWITCH(element))
5651 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5652 game.belt_dir[belt_nr] = belt_dir;
5654 if (belt_dir_nr == 3)
5657 /* set frame order for belt animation graphic according to belt direction */
5658 for (i = 0; i < NUM_BELT_PARTS; i++)
5660 int element = belt_base_active_element[belt_nr] + i;
5661 int graphic_1 = el2img(element);
5662 int graphic_2 = el2panelimg(element);
5664 if (belt_dir == MV_LEFT)
5666 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5667 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5671 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5672 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5676 SCAN_PLAYFIELD(xx, yy)
5678 int element = Feld[xx][yy];
5680 if (IS_BELT_SWITCH(element))
5682 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5684 if (e_belt_nr == belt_nr)
5686 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5687 TEST_DrawLevelField(xx, yy);
5690 else if (IS_BELT(element) && belt_dir != MV_NONE)
5692 int e_belt_nr = getBeltNrFromBeltElement(element);
5694 if (e_belt_nr == belt_nr)
5696 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5698 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5699 TEST_DrawLevelField(xx, yy);
5702 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5704 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5706 if (e_belt_nr == belt_nr)
5708 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5710 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5711 TEST_DrawLevelField(xx, yy);
5717 static void ToggleSwitchgateSwitch(int x, int y)
5721 game.switchgate_pos = !game.switchgate_pos;
5723 SCAN_PLAYFIELD(xx, yy)
5725 int element = Feld[xx][yy];
5727 if (element == EL_SWITCHGATE_SWITCH_UP)
5729 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5730 TEST_DrawLevelField(xx, yy);
5732 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5734 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5735 TEST_DrawLevelField(xx, yy);
5737 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5739 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5740 TEST_DrawLevelField(xx, yy);
5742 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5744 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5745 TEST_DrawLevelField(xx, yy);
5747 else if (element == EL_SWITCHGATE_OPEN ||
5748 element == EL_SWITCHGATE_OPENING)
5750 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5752 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5754 else if (element == EL_SWITCHGATE_CLOSED ||
5755 element == EL_SWITCHGATE_CLOSING)
5757 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5759 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5764 static int getInvisibleActiveFromInvisibleElement(int element)
5766 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5767 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5768 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5772 static int getInvisibleFromInvisibleActiveElement(int element)
5774 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5775 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5776 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5780 static void RedrawAllLightSwitchesAndInvisibleElements()
5784 SCAN_PLAYFIELD(x, y)
5786 int element = Feld[x][y];
5788 if (element == EL_LIGHT_SWITCH &&
5789 game.light_time_left > 0)
5791 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5792 TEST_DrawLevelField(x, y);
5794 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5795 game.light_time_left == 0)
5797 Feld[x][y] = EL_LIGHT_SWITCH;
5798 TEST_DrawLevelField(x, y);
5800 else if (element == EL_EMC_DRIPPER &&
5801 game.light_time_left > 0)
5803 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5804 TEST_DrawLevelField(x, y);
5806 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5807 game.light_time_left == 0)
5809 Feld[x][y] = EL_EMC_DRIPPER;
5810 TEST_DrawLevelField(x, y);
5812 else if (element == EL_INVISIBLE_STEELWALL ||
5813 element == EL_INVISIBLE_WALL ||
5814 element == EL_INVISIBLE_SAND)
5816 if (game.light_time_left > 0)
5817 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5819 TEST_DrawLevelField(x, y);
5821 /* uncrumble neighbour fields, if needed */
5822 if (element == EL_INVISIBLE_SAND)
5823 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5825 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5826 element == EL_INVISIBLE_WALL_ACTIVE ||
5827 element == EL_INVISIBLE_SAND_ACTIVE)
5829 if (game.light_time_left == 0)
5830 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5832 TEST_DrawLevelField(x, y);
5834 /* re-crumble neighbour fields, if needed */
5835 if (element == EL_INVISIBLE_SAND)
5836 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5841 static void RedrawAllInvisibleElementsForLenses()
5845 SCAN_PLAYFIELD(x, y)
5847 int element = Feld[x][y];
5849 if (element == EL_EMC_DRIPPER &&
5850 game.lenses_time_left > 0)
5852 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5853 TEST_DrawLevelField(x, y);
5855 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5856 game.lenses_time_left == 0)
5858 Feld[x][y] = EL_EMC_DRIPPER;
5859 TEST_DrawLevelField(x, y);
5861 else if (element == EL_INVISIBLE_STEELWALL ||
5862 element == EL_INVISIBLE_WALL ||
5863 element == EL_INVISIBLE_SAND)
5865 if (game.lenses_time_left > 0)
5866 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5868 TEST_DrawLevelField(x, y);
5870 /* uncrumble neighbour fields, if needed */
5871 if (element == EL_INVISIBLE_SAND)
5872 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5874 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5875 element == EL_INVISIBLE_WALL_ACTIVE ||
5876 element == EL_INVISIBLE_SAND_ACTIVE)
5878 if (game.lenses_time_left == 0)
5879 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5881 TEST_DrawLevelField(x, y);
5883 /* re-crumble neighbour fields, if needed */
5884 if (element == EL_INVISIBLE_SAND)
5885 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5890 static void RedrawAllInvisibleElementsForMagnifier()
5894 SCAN_PLAYFIELD(x, y)
5896 int element = Feld[x][y];
5898 if (element == EL_EMC_FAKE_GRASS &&
5899 game.magnify_time_left > 0)
5901 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5902 TEST_DrawLevelField(x, y);
5904 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5905 game.magnify_time_left == 0)
5907 Feld[x][y] = EL_EMC_FAKE_GRASS;
5908 TEST_DrawLevelField(x, y);
5910 else if (IS_GATE_GRAY(element) &&
5911 game.magnify_time_left > 0)
5913 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5914 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5915 IS_EM_GATE_GRAY(element) ?
5916 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5917 IS_EMC_GATE_GRAY(element) ?
5918 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5919 IS_DC_GATE_GRAY(element) ?
5920 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5922 TEST_DrawLevelField(x, y);
5924 else if (IS_GATE_GRAY_ACTIVE(element) &&
5925 game.magnify_time_left == 0)
5927 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5928 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5929 IS_EM_GATE_GRAY_ACTIVE(element) ?
5930 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5931 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5932 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5933 IS_DC_GATE_GRAY_ACTIVE(element) ?
5934 EL_DC_GATE_WHITE_GRAY :
5936 TEST_DrawLevelField(x, y);
5941 static void ToggleLightSwitch(int x, int y)
5943 int element = Feld[x][y];
5945 game.light_time_left =
5946 (element == EL_LIGHT_SWITCH ?
5947 level.time_light * FRAMES_PER_SECOND : 0);
5949 RedrawAllLightSwitchesAndInvisibleElements();
5952 static void ActivateTimegateSwitch(int x, int y)
5956 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5958 SCAN_PLAYFIELD(xx, yy)
5960 int element = Feld[xx][yy];
5962 if (element == EL_TIMEGATE_CLOSED ||
5963 element == EL_TIMEGATE_CLOSING)
5965 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5966 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5970 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5972 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5973 TEST_DrawLevelField(xx, yy);
5979 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5980 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5983 void Impact(int x, int y)
5985 boolean last_line = (y == lev_fieldy - 1);
5986 boolean object_hit = FALSE;
5987 boolean impact = (last_line || object_hit);
5988 int element = Feld[x][y];
5989 int smashed = EL_STEELWALL;
5991 if (!last_line) /* check if element below was hit */
5993 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5996 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5997 MovDir[x][y + 1] != MV_DOWN ||
5998 MovPos[x][y + 1] <= TILEY / 2));
6000 /* do not smash moving elements that left the smashed field in time */
6001 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6002 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6005 #if USE_QUICKSAND_IMPACT_BUGFIX
6006 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6008 RemoveMovingField(x, y + 1);
6009 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6010 Feld[x][y + 2] = EL_ROCK;
6011 TEST_DrawLevelField(x, y + 2);
6016 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6018 RemoveMovingField(x, y + 1);
6019 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6020 Feld[x][y + 2] = EL_ROCK;
6021 TEST_DrawLevelField(x, y + 2);
6028 smashed = MovingOrBlocked2Element(x, y + 1);
6030 impact = (last_line || object_hit);
6033 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6035 SplashAcid(x, y + 1);
6039 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6040 /* only reset graphic animation if graphic really changes after impact */
6042 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6044 ResetGfxAnimation(x, y);
6045 TEST_DrawLevelField(x, y);
6048 if (impact && CAN_EXPLODE_IMPACT(element))
6053 else if (impact && element == EL_PEARL &&
6054 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6056 ResetGfxAnimation(x, y);
6058 Feld[x][y] = EL_PEARL_BREAKING;
6059 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6062 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6064 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6069 if (impact && element == EL_AMOEBA_DROP)
6071 if (object_hit && IS_PLAYER(x, y + 1))
6072 KillPlayerUnlessEnemyProtected(x, y + 1);
6073 else if (object_hit && smashed == EL_PENGUIN)
6077 Feld[x][y] = EL_AMOEBA_GROWING;
6078 Store[x][y] = EL_AMOEBA_WET;
6080 ResetRandomAnimationValue(x, y);
6085 if (object_hit) /* check which object was hit */
6087 if ((CAN_PASS_MAGIC_WALL(element) &&
6088 (smashed == EL_MAGIC_WALL ||
6089 smashed == EL_BD_MAGIC_WALL)) ||
6090 (CAN_PASS_DC_MAGIC_WALL(element) &&
6091 smashed == EL_DC_MAGIC_WALL))
6094 int activated_magic_wall =
6095 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6096 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6097 EL_DC_MAGIC_WALL_ACTIVE);
6099 /* activate magic wall / mill */
6100 SCAN_PLAYFIELD(xx, yy)
6102 if (Feld[xx][yy] == smashed)
6103 Feld[xx][yy] = activated_magic_wall;
6106 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6107 game.magic_wall_active = TRUE;
6109 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6110 SND_MAGIC_WALL_ACTIVATING :
6111 smashed == EL_BD_MAGIC_WALL ?
6112 SND_BD_MAGIC_WALL_ACTIVATING :
6113 SND_DC_MAGIC_WALL_ACTIVATING));
6116 if (IS_PLAYER(x, y + 1))
6118 if (CAN_SMASH_PLAYER(element))
6120 KillPlayerUnlessEnemyProtected(x, y + 1);
6124 else if (smashed == EL_PENGUIN)
6126 if (CAN_SMASH_PLAYER(element))
6132 else if (element == EL_BD_DIAMOND)
6134 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6140 else if (((element == EL_SP_INFOTRON ||
6141 element == EL_SP_ZONK) &&
6142 (smashed == EL_SP_SNIKSNAK ||
6143 smashed == EL_SP_ELECTRON ||
6144 smashed == EL_SP_DISK_ORANGE)) ||
6145 (element == EL_SP_INFOTRON &&
6146 smashed == EL_SP_DISK_YELLOW))
6151 else if (CAN_SMASH_EVERYTHING(element))
6153 if (IS_CLASSIC_ENEMY(smashed) ||
6154 CAN_EXPLODE_SMASHED(smashed))
6159 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6161 if (smashed == EL_LAMP ||
6162 smashed == EL_LAMP_ACTIVE)
6167 else if (smashed == EL_NUT)
6169 Feld[x][y + 1] = EL_NUT_BREAKING;
6170 PlayLevelSound(x, y, SND_NUT_BREAKING);
6171 RaiseScoreElement(EL_NUT);
6174 else if (smashed == EL_PEARL)
6176 ResetGfxAnimation(x, y);
6178 Feld[x][y + 1] = EL_PEARL_BREAKING;
6179 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6182 else if (smashed == EL_DIAMOND)
6184 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6185 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6188 else if (IS_BELT_SWITCH(smashed))
6190 ToggleBeltSwitch(x, y + 1);
6192 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6193 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6194 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6195 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6197 ToggleSwitchgateSwitch(x, y + 1);
6199 else if (smashed == EL_LIGHT_SWITCH ||
6200 smashed == EL_LIGHT_SWITCH_ACTIVE)
6202 ToggleLightSwitch(x, y + 1);
6206 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6208 CheckElementChangeBySide(x, y + 1, smashed, element,
6209 CE_SWITCHED, CH_SIDE_TOP);
6210 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6216 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6221 /* play sound of magic wall / mill */
6223 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6224 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6225 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6227 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6228 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6229 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6230 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6231 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6232 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6237 /* play sound of object that hits the ground */
6238 if (last_line || object_hit)
6239 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6242 inline static void TurnRoundExt(int x, int y)
6254 { 0, 0 }, { 0, 0 }, { 0, 0 },
6259 int left, right, back;
6263 { MV_DOWN, MV_UP, MV_RIGHT },
6264 { MV_UP, MV_DOWN, MV_LEFT },
6266 { MV_LEFT, MV_RIGHT, MV_DOWN },
6270 { MV_RIGHT, MV_LEFT, MV_UP }
6273 int element = Feld[x][y];
6274 int move_pattern = element_info[element].move_pattern;
6276 int old_move_dir = MovDir[x][y];
6277 int left_dir = turn[old_move_dir].left;
6278 int right_dir = turn[old_move_dir].right;
6279 int back_dir = turn[old_move_dir].back;
6281 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6282 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6283 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6284 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6286 int left_x = x + left_dx, left_y = y + left_dy;
6287 int right_x = x + right_dx, right_y = y + right_dy;
6288 int move_x = x + move_dx, move_y = y + move_dy;
6292 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6294 TestIfBadThingTouchesOtherBadThing(x, y);
6296 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6297 MovDir[x][y] = right_dir;
6298 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6299 MovDir[x][y] = left_dir;
6301 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6303 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6306 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6308 TestIfBadThingTouchesOtherBadThing(x, y);
6310 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6311 MovDir[x][y] = left_dir;
6312 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6313 MovDir[x][y] = right_dir;
6315 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6317 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6320 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6322 TestIfBadThingTouchesOtherBadThing(x, y);
6324 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6325 MovDir[x][y] = left_dir;
6326 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6327 MovDir[x][y] = right_dir;
6329 if (MovDir[x][y] != old_move_dir)
6332 else if (element == EL_YAMYAM)
6334 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6335 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6337 if (can_turn_left && can_turn_right)
6338 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6339 else if (can_turn_left)
6340 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6341 else if (can_turn_right)
6342 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6344 MovDir[x][y] = back_dir;
6346 MovDelay[x][y] = 16 + 16 * RND(3);
6348 else if (element == EL_DARK_YAMYAM)
6350 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6352 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6355 if (can_turn_left && can_turn_right)
6356 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6357 else if (can_turn_left)
6358 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6359 else if (can_turn_right)
6360 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6362 MovDir[x][y] = back_dir;
6364 MovDelay[x][y] = 16 + 16 * RND(3);
6366 else if (element == EL_PACMAN)
6368 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6369 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6371 if (can_turn_left && can_turn_right)
6372 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6373 else if (can_turn_left)
6374 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6375 else if (can_turn_right)
6376 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6378 MovDir[x][y] = back_dir;
6380 MovDelay[x][y] = 6 + RND(40);
6382 else if (element == EL_PIG)
6384 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6385 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6386 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6387 boolean should_turn_left, should_turn_right, should_move_on;
6389 int rnd = RND(rnd_value);
6391 should_turn_left = (can_turn_left &&
6393 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6394 y + back_dy + left_dy)));
6395 should_turn_right = (can_turn_right &&
6397 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6398 y + back_dy + right_dy)));
6399 should_move_on = (can_move_on &&
6402 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6403 y + move_dy + left_dy) ||
6404 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6405 y + move_dy + right_dy)));
6407 if (should_turn_left || should_turn_right || should_move_on)
6409 if (should_turn_left && should_turn_right && should_move_on)
6410 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6411 rnd < 2 * rnd_value / 3 ? right_dir :
6413 else if (should_turn_left && should_turn_right)
6414 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6415 else if (should_turn_left && should_move_on)
6416 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6417 else if (should_turn_right && should_move_on)
6418 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6419 else if (should_turn_left)
6420 MovDir[x][y] = left_dir;
6421 else if (should_turn_right)
6422 MovDir[x][y] = right_dir;
6423 else if (should_move_on)
6424 MovDir[x][y] = old_move_dir;
6426 else if (can_move_on && rnd > rnd_value / 8)
6427 MovDir[x][y] = old_move_dir;
6428 else if (can_turn_left && can_turn_right)
6429 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6430 else if (can_turn_left && rnd > rnd_value / 8)
6431 MovDir[x][y] = left_dir;
6432 else if (can_turn_right && rnd > rnd_value/8)
6433 MovDir[x][y] = right_dir;
6435 MovDir[x][y] = back_dir;
6437 xx = x + move_xy[MovDir[x][y]].dx;
6438 yy = y + move_xy[MovDir[x][y]].dy;
6440 if (!IN_LEV_FIELD(xx, yy) ||
6441 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6442 MovDir[x][y] = old_move_dir;
6446 else if (element == EL_DRAGON)
6448 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6449 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6450 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6452 int rnd = RND(rnd_value);
6454 if (can_move_on && rnd > rnd_value / 8)
6455 MovDir[x][y] = old_move_dir;
6456 else if (can_turn_left && can_turn_right)
6457 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6458 else if (can_turn_left && rnd > rnd_value / 8)
6459 MovDir[x][y] = left_dir;
6460 else if (can_turn_right && rnd > rnd_value / 8)
6461 MovDir[x][y] = right_dir;
6463 MovDir[x][y] = back_dir;
6465 xx = x + move_xy[MovDir[x][y]].dx;
6466 yy = y + move_xy[MovDir[x][y]].dy;
6468 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6469 MovDir[x][y] = old_move_dir;
6473 else if (element == EL_MOLE)
6475 boolean can_move_on =
6476 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6477 IS_AMOEBOID(Feld[move_x][move_y]) ||
6478 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6481 boolean can_turn_left =
6482 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6483 IS_AMOEBOID(Feld[left_x][left_y])));
6485 boolean can_turn_right =
6486 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6487 IS_AMOEBOID(Feld[right_x][right_y])));
6489 if (can_turn_left && can_turn_right)
6490 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6491 else if (can_turn_left)
6492 MovDir[x][y] = left_dir;
6494 MovDir[x][y] = right_dir;
6497 if (MovDir[x][y] != old_move_dir)
6500 else if (element == EL_BALLOON)
6502 MovDir[x][y] = game.wind_direction;
6505 else if (element == EL_SPRING)
6507 if (MovDir[x][y] & MV_HORIZONTAL)
6509 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6510 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6512 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6513 ResetGfxAnimation(move_x, move_y);
6514 TEST_DrawLevelField(move_x, move_y);
6516 MovDir[x][y] = back_dir;
6518 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6519 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6520 MovDir[x][y] = MV_NONE;
6525 else if (element == EL_ROBOT ||
6526 element == EL_SATELLITE ||
6527 element == EL_PENGUIN ||
6528 element == EL_EMC_ANDROID)
6530 int attr_x = -1, attr_y = -1;
6541 for (i = 0; i < MAX_PLAYERS; i++)
6543 struct PlayerInfo *player = &stored_player[i];
6544 int jx = player->jx, jy = player->jy;
6546 if (!player->active)
6550 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6558 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6559 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6560 game.engine_version < VERSION_IDENT(3,1,0,0)))
6566 if (element == EL_PENGUIN)
6569 static int xy[4][2] =
6577 for (i = 0; i < NUM_DIRECTIONS; i++)
6579 int ex = x + xy[i][0];
6580 int ey = y + xy[i][1];
6582 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6583 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6584 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6585 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6594 MovDir[x][y] = MV_NONE;
6596 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6597 else if (attr_x > x)
6598 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6600 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6601 else if (attr_y > y)
6602 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6604 if (element == EL_ROBOT)
6608 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6609 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6610 Moving2Blocked(x, y, &newx, &newy);
6612 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6613 MovDelay[x][y] = 8 + 8 * !RND(3);
6615 MovDelay[x][y] = 16;
6617 else if (element == EL_PENGUIN)
6623 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6625 boolean first_horiz = RND(2);
6626 int new_move_dir = MovDir[x][y];
6629 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6630 Moving2Blocked(x, y, &newx, &newy);
6632 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6636 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6637 Moving2Blocked(x, y, &newx, &newy);
6639 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6642 MovDir[x][y] = old_move_dir;
6646 else if (element == EL_SATELLITE)
6652 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6654 boolean first_horiz = RND(2);
6655 int new_move_dir = MovDir[x][y];
6658 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6659 Moving2Blocked(x, y, &newx, &newy);
6661 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6665 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6666 Moving2Blocked(x, y, &newx, &newy);
6668 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6671 MovDir[x][y] = old_move_dir;
6675 else if (element == EL_EMC_ANDROID)
6677 static int check_pos[16] =
6679 -1, /* 0 => (invalid) */
6680 7, /* 1 => MV_LEFT */
6681 3, /* 2 => MV_RIGHT */
6682 -1, /* 3 => (invalid) */
6684 0, /* 5 => MV_LEFT | MV_UP */
6685 2, /* 6 => MV_RIGHT | MV_UP */
6686 -1, /* 7 => (invalid) */
6687 5, /* 8 => MV_DOWN */
6688 6, /* 9 => MV_LEFT | MV_DOWN */
6689 4, /* 10 => MV_RIGHT | MV_DOWN */
6690 -1, /* 11 => (invalid) */
6691 -1, /* 12 => (invalid) */
6692 -1, /* 13 => (invalid) */
6693 -1, /* 14 => (invalid) */
6694 -1, /* 15 => (invalid) */
6702 { -1, -1, MV_LEFT | MV_UP },
6704 { +1, -1, MV_RIGHT | MV_UP },
6705 { +1, 0, MV_RIGHT },
6706 { +1, +1, MV_RIGHT | MV_DOWN },
6708 { -1, +1, MV_LEFT | MV_DOWN },
6711 int start_pos, check_order;
6712 boolean can_clone = FALSE;
6715 /* check if there is any free field around current position */
6716 for (i = 0; i < 8; i++)
6718 int newx = x + check_xy[i].dx;
6719 int newy = y + check_xy[i].dy;
6721 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6729 if (can_clone) /* randomly find an element to clone */
6733 start_pos = check_pos[RND(8)];
6734 check_order = (RND(2) ? -1 : +1);
6736 for (i = 0; i < 8; i++)
6738 int pos_raw = start_pos + i * check_order;
6739 int pos = (pos_raw + 8) % 8;
6740 int newx = x + check_xy[pos].dx;
6741 int newy = y + check_xy[pos].dy;
6743 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6745 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6746 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6748 Store[x][y] = Feld[newx][newy];
6757 if (can_clone) /* randomly find a direction to move */
6761 start_pos = check_pos[RND(8)];
6762 check_order = (RND(2) ? -1 : +1);
6764 for (i = 0; i < 8; i++)
6766 int pos_raw = start_pos + i * check_order;
6767 int pos = (pos_raw + 8) % 8;
6768 int newx = x + check_xy[pos].dx;
6769 int newy = y + check_xy[pos].dy;
6770 int new_move_dir = check_xy[pos].dir;
6772 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6774 MovDir[x][y] = new_move_dir;
6775 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6784 if (can_clone) /* cloning and moving successful */
6787 /* cannot clone -- try to move towards player */
6789 start_pos = check_pos[MovDir[x][y] & 0x0f];
6790 check_order = (RND(2) ? -1 : +1);
6792 for (i = 0; i < 3; i++)
6794 /* first check start_pos, then previous/next or (next/previous) pos */
6795 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6796 int pos = (pos_raw + 8) % 8;
6797 int newx = x + check_xy[pos].dx;
6798 int newy = y + check_xy[pos].dy;
6799 int new_move_dir = check_xy[pos].dir;
6801 if (IS_PLAYER(newx, newy))
6804 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6806 MovDir[x][y] = new_move_dir;
6807 MovDelay[x][y] = level.android_move_time * 8 + 1;
6814 else if (move_pattern == MV_TURNING_LEFT ||
6815 move_pattern == MV_TURNING_RIGHT ||
6816 move_pattern == MV_TURNING_LEFT_RIGHT ||
6817 move_pattern == MV_TURNING_RIGHT_LEFT ||
6818 move_pattern == MV_TURNING_RANDOM ||
6819 move_pattern == MV_ALL_DIRECTIONS)
6821 boolean can_turn_left =
6822 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6823 boolean can_turn_right =
6824 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6826 if (element_info[element].move_stepsize == 0) /* "not moving" */
6829 if (move_pattern == MV_TURNING_LEFT)
6830 MovDir[x][y] = left_dir;
6831 else if (move_pattern == MV_TURNING_RIGHT)
6832 MovDir[x][y] = right_dir;
6833 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6834 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6835 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6836 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6837 else if (move_pattern == MV_TURNING_RANDOM)
6838 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6839 can_turn_right && !can_turn_left ? right_dir :
6840 RND(2) ? left_dir : right_dir);
6841 else if (can_turn_left && can_turn_right)
6842 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6843 else if (can_turn_left)
6844 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6845 else if (can_turn_right)
6846 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6848 MovDir[x][y] = back_dir;
6850 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6852 else if (move_pattern == MV_HORIZONTAL ||
6853 move_pattern == MV_VERTICAL)
6855 if (move_pattern & old_move_dir)
6856 MovDir[x][y] = back_dir;
6857 else if (move_pattern == MV_HORIZONTAL)
6858 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6859 else if (move_pattern == MV_VERTICAL)
6860 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6862 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6864 else if (move_pattern & MV_ANY_DIRECTION)
6866 MovDir[x][y] = move_pattern;
6867 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6869 else if (move_pattern & MV_WIND_DIRECTION)
6871 MovDir[x][y] = game.wind_direction;
6872 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6874 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6876 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6877 MovDir[x][y] = left_dir;
6878 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6879 MovDir[x][y] = right_dir;
6881 if (MovDir[x][y] != old_move_dir)
6882 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6886 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6887 MovDir[x][y] = right_dir;
6888 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6889 MovDir[x][y] = left_dir;
6891 if (MovDir[x][y] != old_move_dir)
6892 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894 else if (move_pattern == MV_TOWARDS_PLAYER ||
6895 move_pattern == MV_AWAY_FROM_PLAYER)
6897 int attr_x = -1, attr_y = -1;
6899 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6910 for (i = 0; i < MAX_PLAYERS; i++)
6912 struct PlayerInfo *player = &stored_player[i];
6913 int jx = player->jx, jy = player->jy;
6915 if (!player->active)
6919 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6927 MovDir[x][y] = MV_NONE;
6929 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6930 else if (attr_x > x)
6931 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6933 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6934 else if (attr_y > y)
6935 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6937 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6939 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6941 boolean first_horiz = RND(2);
6942 int new_move_dir = MovDir[x][y];
6944 if (element_info[element].move_stepsize == 0) /* "not moving" */
6946 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6947 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6953 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6954 Moving2Blocked(x, y, &newx, &newy);
6956 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6960 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6961 Moving2Blocked(x, y, &newx, &newy);
6963 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6966 MovDir[x][y] = old_move_dir;
6969 else if (move_pattern == MV_WHEN_PUSHED ||
6970 move_pattern == MV_WHEN_DROPPED)
6972 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6973 MovDir[x][y] = MV_NONE;
6977 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6979 static int test_xy[7][2] =
6989 static int test_dir[7] =
6999 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7000 int move_preference = -1000000; /* start with very low preference */
7001 int new_move_dir = MV_NONE;
7002 int start_test = RND(4);
7005 for (i = 0; i < NUM_DIRECTIONS; i++)
7007 int move_dir = test_dir[start_test + i];
7008 int move_dir_preference;
7010 xx = x + test_xy[start_test + i][0];
7011 yy = y + test_xy[start_test + i][1];
7013 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7014 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7016 new_move_dir = move_dir;
7021 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7024 move_dir_preference = -1 * RunnerVisit[xx][yy];
7025 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7026 move_dir_preference = PlayerVisit[xx][yy];
7028 if (move_dir_preference > move_preference)
7030 /* prefer field that has not been visited for the longest time */
7031 move_preference = move_dir_preference;
7032 new_move_dir = move_dir;
7034 else if (move_dir_preference == move_preference &&
7035 move_dir == old_move_dir)
7037 /* prefer last direction when all directions are preferred equally */
7038 move_preference = move_dir_preference;
7039 new_move_dir = move_dir;
7043 MovDir[x][y] = new_move_dir;
7044 if (old_move_dir != new_move_dir)
7045 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7049 static void TurnRound(int x, int y)
7051 int direction = MovDir[x][y];
7055 GfxDir[x][y] = MovDir[x][y];
7057 if (direction != MovDir[x][y])
7061 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7063 ResetGfxFrame(x, y);
7066 static boolean JustBeingPushed(int x, int y)
7070 for (i = 0; i < MAX_PLAYERS; i++)
7072 struct PlayerInfo *player = &stored_player[i];
7074 if (player->active && player->is_pushing && player->MovPos)
7076 int next_jx = player->jx + (player->jx - player->last_jx);
7077 int next_jy = player->jy + (player->jy - player->last_jy);
7079 if (x == next_jx && y == next_jy)
7087 void StartMoving(int x, int y)
7089 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7090 int element = Feld[x][y];
7095 if (MovDelay[x][y] == 0)
7096 GfxAction[x][y] = ACTION_DEFAULT;
7098 if (CAN_FALL(element) && y < lev_fieldy - 1)
7100 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7101 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7102 if (JustBeingPushed(x, y))
7105 if (element == EL_QUICKSAND_FULL)
7107 if (IS_FREE(x, y + 1))
7109 InitMovingField(x, y, MV_DOWN);
7110 started_moving = TRUE;
7112 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7113 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7114 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7115 Store[x][y] = EL_ROCK;
7117 Store[x][y] = EL_ROCK;
7120 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7122 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7124 if (!MovDelay[x][y])
7126 MovDelay[x][y] = TILEY + 1;
7128 ResetGfxAnimation(x, y);
7129 ResetGfxAnimation(x, y + 1);
7134 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7135 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7142 Feld[x][y] = EL_QUICKSAND_EMPTY;
7143 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7144 Store[x][y + 1] = Store[x][y];
7147 PlayLevelSoundAction(x, y, ACTION_FILLING);
7149 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7151 if (!MovDelay[x][y])
7153 MovDelay[x][y] = TILEY + 1;
7155 ResetGfxAnimation(x, y);
7156 ResetGfxAnimation(x, y + 1);
7161 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7162 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7169 Feld[x][y] = EL_QUICKSAND_EMPTY;
7170 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7171 Store[x][y + 1] = Store[x][y];
7174 PlayLevelSoundAction(x, y, ACTION_FILLING);
7177 else if (element == EL_QUICKSAND_FAST_FULL)
7179 if (IS_FREE(x, y + 1))
7181 InitMovingField(x, y, MV_DOWN);
7182 started_moving = TRUE;
7184 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7185 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7186 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7187 Store[x][y] = EL_ROCK;
7189 Store[x][y] = EL_ROCK;
7192 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7194 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7196 if (!MovDelay[x][y])
7198 MovDelay[x][y] = TILEY + 1;
7200 ResetGfxAnimation(x, y);
7201 ResetGfxAnimation(x, y + 1);
7206 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7207 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7214 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7215 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7216 Store[x][y + 1] = Store[x][y];
7219 PlayLevelSoundAction(x, y, ACTION_FILLING);
7221 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7223 if (!MovDelay[x][y])
7225 MovDelay[x][y] = TILEY + 1;
7227 ResetGfxAnimation(x, y);
7228 ResetGfxAnimation(x, y + 1);
7233 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7234 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7241 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7242 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7243 Store[x][y + 1] = Store[x][y];
7246 PlayLevelSoundAction(x, y, ACTION_FILLING);
7249 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7250 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7252 InitMovingField(x, y, MV_DOWN);
7253 started_moving = TRUE;
7255 Feld[x][y] = EL_QUICKSAND_FILLING;
7256 Store[x][y] = element;
7258 PlayLevelSoundAction(x, y, ACTION_FILLING);
7260 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7261 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7263 InitMovingField(x, y, MV_DOWN);
7264 started_moving = TRUE;
7266 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7267 Store[x][y] = element;
7269 PlayLevelSoundAction(x, y, ACTION_FILLING);
7271 else if (element == EL_MAGIC_WALL_FULL)
7273 if (IS_FREE(x, y + 1))
7275 InitMovingField(x, y, MV_DOWN);
7276 started_moving = TRUE;
7278 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7279 Store[x][y] = EL_CHANGED(Store[x][y]);
7281 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7283 if (!MovDelay[x][y])
7284 MovDelay[x][y] = TILEY / 4 + 1;
7293 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7294 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7295 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7299 else if (element == EL_BD_MAGIC_WALL_FULL)
7301 if (IS_FREE(x, y + 1))
7303 InitMovingField(x, y, MV_DOWN);
7304 started_moving = TRUE;
7306 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7307 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7309 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7311 if (!MovDelay[x][y])
7312 MovDelay[x][y] = TILEY / 4 + 1;
7321 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7322 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7323 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7327 else if (element == EL_DC_MAGIC_WALL_FULL)
7329 if (IS_FREE(x, y + 1))
7331 InitMovingField(x, y, MV_DOWN);
7332 started_moving = TRUE;
7334 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7335 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7337 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7339 if (!MovDelay[x][y])
7340 MovDelay[x][y] = TILEY / 4 + 1;
7349 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7350 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7351 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7355 else if ((CAN_PASS_MAGIC_WALL(element) &&
7356 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7357 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7358 (CAN_PASS_DC_MAGIC_WALL(element) &&
7359 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7362 InitMovingField(x, y, MV_DOWN);
7363 started_moving = TRUE;
7366 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7367 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7368 EL_DC_MAGIC_WALL_FILLING);
7369 Store[x][y] = element;
7371 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7373 SplashAcid(x, y + 1);
7375 InitMovingField(x, y, MV_DOWN);
7376 started_moving = TRUE;
7378 Store[x][y] = EL_ACID;
7381 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7382 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7383 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7384 CAN_FALL(element) && WasJustFalling[x][y] &&
7385 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7387 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7388 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7389 (Feld[x][y + 1] == EL_BLOCKED)))
7391 /* this is needed for a special case not covered by calling "Impact()"
7392 from "ContinueMoving()": if an element moves to a tile directly below
7393 another element which was just falling on that tile (which was empty
7394 in the previous frame), the falling element above would just stop
7395 instead of smashing the element below (in previous version, the above
7396 element was just checked for "moving" instead of "falling", resulting
7397 in incorrect smashes caused by horizontal movement of the above
7398 element; also, the case of the player being the element to smash was
7399 simply not covered here... :-/ ) */
7401 CheckCollision[x][y] = 0;
7402 CheckImpact[x][y] = 0;
7406 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7408 if (MovDir[x][y] == MV_NONE)
7410 InitMovingField(x, y, MV_DOWN);
7411 started_moving = TRUE;
7414 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7416 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7417 MovDir[x][y] = MV_DOWN;
7419 InitMovingField(x, y, MV_DOWN);
7420 started_moving = TRUE;
7422 else if (element == EL_AMOEBA_DROP)
7424 Feld[x][y] = EL_AMOEBA_GROWING;
7425 Store[x][y] = EL_AMOEBA_WET;
7427 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7428 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7429 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7430 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7432 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7433 (IS_FREE(x - 1, y + 1) ||
7434 Feld[x - 1][y + 1] == EL_ACID));
7435 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7436 (IS_FREE(x + 1, y + 1) ||
7437 Feld[x + 1][y + 1] == EL_ACID));
7438 boolean can_fall_any = (can_fall_left || can_fall_right);
7439 boolean can_fall_both = (can_fall_left && can_fall_right);
7440 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7442 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7444 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7445 can_fall_right = FALSE;
7446 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7447 can_fall_left = FALSE;
7448 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7449 can_fall_right = FALSE;
7450 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7451 can_fall_left = FALSE;
7453 can_fall_any = (can_fall_left || can_fall_right);
7454 can_fall_both = FALSE;
7459 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7460 can_fall_right = FALSE; /* slip down on left side */
7462 can_fall_left = !(can_fall_right = RND(2));
7464 can_fall_both = FALSE;
7469 /* if not determined otherwise, prefer left side for slipping down */
7470 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7471 started_moving = TRUE;
7474 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7476 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7477 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7478 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7479 int belt_dir = game.belt_dir[belt_nr];
7481 if ((belt_dir == MV_LEFT && left_is_free) ||
7482 (belt_dir == MV_RIGHT && right_is_free))
7484 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7486 InitMovingField(x, y, belt_dir);
7487 started_moving = TRUE;
7489 Pushed[x][y] = TRUE;
7490 Pushed[nextx][y] = TRUE;
7492 GfxAction[x][y] = ACTION_DEFAULT;
7496 MovDir[x][y] = 0; /* if element was moving, stop it */
7501 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7502 if (CAN_MOVE(element) && !started_moving)
7504 int move_pattern = element_info[element].move_pattern;
7507 Moving2Blocked(x, y, &newx, &newy);
7509 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7512 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7513 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7515 WasJustMoving[x][y] = 0;
7516 CheckCollision[x][y] = 0;
7518 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7520 if (Feld[x][y] != element) /* element has changed */
7524 if (!MovDelay[x][y]) /* start new movement phase */
7526 /* all objects that can change their move direction after each step
7527 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7529 if (element != EL_YAMYAM &&
7530 element != EL_DARK_YAMYAM &&
7531 element != EL_PACMAN &&
7532 !(move_pattern & MV_ANY_DIRECTION) &&
7533 move_pattern != MV_TURNING_LEFT &&
7534 move_pattern != MV_TURNING_RIGHT &&
7535 move_pattern != MV_TURNING_LEFT_RIGHT &&
7536 move_pattern != MV_TURNING_RIGHT_LEFT &&
7537 move_pattern != MV_TURNING_RANDOM)
7541 if (MovDelay[x][y] && (element == EL_BUG ||
7542 element == EL_SPACESHIP ||
7543 element == EL_SP_SNIKSNAK ||
7544 element == EL_SP_ELECTRON ||
7545 element == EL_MOLE))
7546 TEST_DrawLevelField(x, y);
7550 if (MovDelay[x][y]) /* wait some time before next movement */
7554 if (element == EL_ROBOT ||
7555 element == EL_YAMYAM ||
7556 element == EL_DARK_YAMYAM)
7558 DrawLevelElementAnimationIfNeeded(x, y, element);
7559 PlayLevelSoundAction(x, y, ACTION_WAITING);
7561 else if (element == EL_SP_ELECTRON)
7562 DrawLevelElementAnimationIfNeeded(x, y, element);
7563 else if (element == EL_DRAGON)
7566 int dir = MovDir[x][y];
7567 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7568 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7569 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7570 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7571 dir == MV_UP ? IMG_FLAMES_1_UP :
7572 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7573 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7575 GfxAction[x][y] = ACTION_ATTACKING;
7577 if (IS_PLAYER(x, y))
7578 DrawPlayerField(x, y);
7580 TEST_DrawLevelField(x, y);
7582 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7584 for (i = 1; i <= 3; i++)
7586 int xx = x + i * dx;
7587 int yy = y + i * dy;
7588 int sx = SCREENX(xx);
7589 int sy = SCREENY(yy);
7590 int flame_graphic = graphic + (i - 1);
7592 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7597 int flamed = MovingOrBlocked2Element(xx, yy);
7599 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7602 RemoveMovingField(xx, yy);
7604 ChangeDelay[xx][yy] = 0;
7606 Feld[xx][yy] = EL_FLAMES;
7608 if (IN_SCR_FIELD(sx, sy))
7610 TEST_DrawLevelFieldCrumbled(xx, yy);
7611 DrawGraphic(sx, sy, flame_graphic, frame);
7616 if (Feld[xx][yy] == EL_FLAMES)
7617 Feld[xx][yy] = EL_EMPTY;
7618 TEST_DrawLevelField(xx, yy);
7623 if (MovDelay[x][y]) /* element still has to wait some time */
7625 PlayLevelSoundAction(x, y, ACTION_WAITING);
7631 /* now make next step */
7633 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7635 if (DONT_COLLIDE_WITH(element) &&
7636 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7637 !PLAYER_ENEMY_PROTECTED(newx, newy))
7639 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7644 else if (CAN_MOVE_INTO_ACID(element) &&
7645 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7646 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7647 (MovDir[x][y] == MV_DOWN ||
7648 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7650 SplashAcid(newx, newy);
7651 Store[x][y] = EL_ACID;
7653 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7655 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7656 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7657 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7658 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7661 TEST_DrawLevelField(x, y);
7663 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7664 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7665 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7667 local_player->friends_still_needed--;
7668 if (!local_player->friends_still_needed &&
7669 !local_player->GameOver && AllPlayersGone)
7670 PlayerWins(local_player);
7674 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7676 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7677 TEST_DrawLevelField(newx, newy);
7679 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7681 else if (!IS_FREE(newx, newy))
7683 GfxAction[x][y] = ACTION_WAITING;
7685 if (IS_PLAYER(x, y))
7686 DrawPlayerField(x, y);
7688 TEST_DrawLevelField(x, y);
7693 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7695 if (IS_FOOD_PIG(Feld[newx][newy]))
7697 if (IS_MOVING(newx, newy))
7698 RemoveMovingField(newx, newy);
7701 Feld[newx][newy] = EL_EMPTY;
7702 TEST_DrawLevelField(newx, newy);
7705 PlayLevelSound(x, y, SND_PIG_DIGGING);
7707 else if (!IS_FREE(newx, newy))
7709 if (IS_PLAYER(x, y))
7710 DrawPlayerField(x, y);
7712 TEST_DrawLevelField(x, y);
7717 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7719 if (Store[x][y] != EL_EMPTY)
7721 boolean can_clone = FALSE;
7724 /* check if element to clone is still there */
7725 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7727 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7735 /* cannot clone or target field not free anymore -- do not clone */
7736 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7737 Store[x][y] = EL_EMPTY;
7740 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7742 if (IS_MV_DIAGONAL(MovDir[x][y]))
7744 int diagonal_move_dir = MovDir[x][y];
7745 int stored = Store[x][y];
7746 int change_delay = 8;
7749 /* android is moving diagonally */
7751 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7753 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7754 GfxElement[x][y] = EL_EMC_ANDROID;
7755 GfxAction[x][y] = ACTION_SHRINKING;
7756 GfxDir[x][y] = diagonal_move_dir;
7757 ChangeDelay[x][y] = change_delay;
7759 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7762 DrawLevelGraphicAnimation(x, y, graphic);
7763 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7765 if (Feld[newx][newy] == EL_ACID)
7767 SplashAcid(newx, newy);
7772 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7774 Store[newx][newy] = EL_EMC_ANDROID;
7775 GfxElement[newx][newy] = EL_EMC_ANDROID;
7776 GfxAction[newx][newy] = ACTION_GROWING;
7777 GfxDir[newx][newy] = diagonal_move_dir;
7778 ChangeDelay[newx][newy] = change_delay;
7780 graphic = el_act_dir2img(GfxElement[newx][newy],
7781 GfxAction[newx][newy], GfxDir[newx][newy]);
7783 DrawLevelGraphicAnimation(newx, newy, graphic);
7784 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7790 Feld[newx][newy] = EL_EMPTY;
7791 TEST_DrawLevelField(newx, newy);
7793 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7796 else if (!IS_FREE(newx, newy))
7801 else if (IS_CUSTOM_ELEMENT(element) &&
7802 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7804 if (!DigFieldByCE(newx, newy, element))
7807 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7809 RunnerVisit[x][y] = FrameCounter;
7810 PlayerVisit[x][y] /= 8; /* expire player visit path */
7813 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7815 if (!IS_FREE(newx, newy))
7817 if (IS_PLAYER(x, y))
7818 DrawPlayerField(x, y);
7820 TEST_DrawLevelField(x, y);
7826 boolean wanna_flame = !RND(10);
7827 int dx = newx - x, dy = newy - y;
7828 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7829 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7830 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7831 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7832 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7833 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7836 IS_CLASSIC_ENEMY(element1) ||
7837 IS_CLASSIC_ENEMY(element2)) &&
7838 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7839 element1 != EL_FLAMES && element2 != EL_FLAMES)
7841 ResetGfxAnimation(x, y);
7842 GfxAction[x][y] = ACTION_ATTACKING;
7844 if (IS_PLAYER(x, y))
7845 DrawPlayerField(x, y);
7847 TEST_DrawLevelField(x, y);
7849 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7851 MovDelay[x][y] = 50;
7853 Feld[newx][newy] = EL_FLAMES;
7854 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7855 Feld[newx1][newy1] = EL_FLAMES;
7856 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7857 Feld[newx2][newy2] = EL_FLAMES;
7863 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7864 Feld[newx][newy] == EL_DIAMOND)
7866 if (IS_MOVING(newx, newy))
7867 RemoveMovingField(newx, newy);
7870 Feld[newx][newy] = EL_EMPTY;
7871 TEST_DrawLevelField(newx, newy);
7874 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7876 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7877 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7879 if (AmoebaNr[newx][newy])
7881 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7882 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7883 Feld[newx][newy] == EL_BD_AMOEBA)
7884 AmoebaCnt[AmoebaNr[newx][newy]]--;
7887 if (IS_MOVING(newx, newy))
7889 RemoveMovingField(newx, newy);
7893 Feld[newx][newy] = EL_EMPTY;
7894 TEST_DrawLevelField(newx, newy);
7897 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7899 else if ((element == EL_PACMAN || element == EL_MOLE)
7900 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7902 if (AmoebaNr[newx][newy])
7904 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7905 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7906 Feld[newx][newy] == EL_BD_AMOEBA)
7907 AmoebaCnt[AmoebaNr[newx][newy]]--;
7910 if (element == EL_MOLE)
7912 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7913 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7915 ResetGfxAnimation(x, y);
7916 GfxAction[x][y] = ACTION_DIGGING;
7917 TEST_DrawLevelField(x, y);
7919 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7921 return; /* wait for shrinking amoeba */
7923 else /* element == EL_PACMAN */
7925 Feld[newx][newy] = EL_EMPTY;
7926 TEST_DrawLevelField(newx, newy);
7927 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7930 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7931 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7932 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7934 /* wait for shrinking amoeba to completely disappear */
7937 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7939 /* object was running against a wall */
7943 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7944 DrawLevelElementAnimation(x, y, element);
7946 if (DONT_TOUCH(element))
7947 TestIfBadThingTouchesPlayer(x, y);
7952 InitMovingField(x, y, MovDir[x][y]);
7954 PlayLevelSoundAction(x, y, ACTION_MOVING);
7958 ContinueMoving(x, y);
7961 void ContinueMoving(int x, int y)
7963 int element = Feld[x][y];
7964 struct ElementInfo *ei = &element_info[element];
7965 int direction = MovDir[x][y];
7966 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7967 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7968 int newx = x + dx, newy = y + dy;
7969 int stored = Store[x][y];
7970 int stored_new = Store[newx][newy];
7971 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7972 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7973 boolean last_line = (newy == lev_fieldy - 1);
7975 MovPos[x][y] += getElementMoveStepsize(x, y);
7977 if (pushed_by_player) /* special case: moving object pushed by player */
7978 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7980 if (ABS(MovPos[x][y]) < TILEX)
7982 TEST_DrawLevelField(x, y);
7984 return; /* element is still moving */
7987 /* element reached destination field */
7989 Feld[x][y] = EL_EMPTY;
7990 Feld[newx][newy] = element;
7991 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
7993 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
7995 element = Feld[newx][newy] = EL_ACID;
7997 else if (element == EL_MOLE)
7999 Feld[x][y] = EL_SAND;
8001 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8003 else if (element == EL_QUICKSAND_FILLING)
8005 element = Feld[newx][newy] = get_next_element(element);
8006 Store[newx][newy] = Store[x][y];
8008 else if (element == EL_QUICKSAND_EMPTYING)
8010 Feld[x][y] = get_next_element(element);
8011 element = Feld[newx][newy] = Store[x][y];
8013 else if (element == EL_QUICKSAND_FAST_FILLING)
8015 element = Feld[newx][newy] = get_next_element(element);
8016 Store[newx][newy] = Store[x][y];
8018 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8020 Feld[x][y] = get_next_element(element);
8021 element = Feld[newx][newy] = Store[x][y];
8023 else if (element == EL_MAGIC_WALL_FILLING)
8025 element = Feld[newx][newy] = get_next_element(element);
8026 if (!game.magic_wall_active)
8027 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8028 Store[newx][newy] = Store[x][y];
8030 else if (element == EL_MAGIC_WALL_EMPTYING)
8032 Feld[x][y] = get_next_element(element);
8033 if (!game.magic_wall_active)
8034 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8035 element = Feld[newx][newy] = Store[x][y];
8037 InitField(newx, newy, FALSE);
8039 else if (element == EL_BD_MAGIC_WALL_FILLING)
8041 element = Feld[newx][newy] = get_next_element(element);
8042 if (!game.magic_wall_active)
8043 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8044 Store[newx][newy] = Store[x][y];
8046 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8048 Feld[x][y] = get_next_element(element);
8049 if (!game.magic_wall_active)
8050 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8051 element = Feld[newx][newy] = Store[x][y];
8053 InitField(newx, newy, FALSE);
8055 else if (element == EL_DC_MAGIC_WALL_FILLING)
8057 element = Feld[newx][newy] = get_next_element(element);
8058 if (!game.magic_wall_active)
8059 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8060 Store[newx][newy] = Store[x][y];
8062 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8064 Feld[x][y] = get_next_element(element);
8065 if (!game.magic_wall_active)
8066 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8067 element = Feld[newx][newy] = Store[x][y];
8069 InitField(newx, newy, FALSE);
8071 else if (element == EL_AMOEBA_DROPPING)
8073 Feld[x][y] = get_next_element(element);
8074 element = Feld[newx][newy] = Store[x][y];
8076 else if (element == EL_SOKOBAN_OBJECT)
8079 Feld[x][y] = Back[x][y];
8081 if (Back[newx][newy])
8082 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8084 Back[x][y] = Back[newx][newy] = 0;
8087 Store[x][y] = EL_EMPTY;
8092 MovDelay[newx][newy] = 0;
8094 if (CAN_CHANGE_OR_HAS_ACTION(element))
8096 /* copy element change control values to new field */
8097 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8098 ChangePage[newx][newy] = ChangePage[x][y];
8099 ChangeCount[newx][newy] = ChangeCount[x][y];
8100 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8103 CustomValue[newx][newy] = CustomValue[x][y];
8105 ChangeDelay[x][y] = 0;
8106 ChangePage[x][y] = -1;
8107 ChangeCount[x][y] = 0;
8108 ChangeEvent[x][y] = -1;
8110 CustomValue[x][y] = 0;
8112 /* copy animation control values to new field */
8113 GfxFrame[newx][newy] = GfxFrame[x][y];
8114 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8115 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8116 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8118 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8120 /* some elements can leave other elements behind after moving */
8121 if (ei->move_leave_element != EL_EMPTY &&
8122 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8123 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8125 int move_leave_element = ei->move_leave_element;
8127 /* this makes it possible to leave the removed element again */
8128 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8129 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8131 Feld[x][y] = move_leave_element;
8133 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8134 MovDir[x][y] = direction;
8136 InitField(x, y, FALSE);
8138 if (GFX_CRUMBLED(Feld[x][y]))
8139 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8141 if (ELEM_IS_PLAYER(move_leave_element))
8142 RelocatePlayer(x, y, move_leave_element);
8145 /* do this after checking for left-behind element */
8146 ResetGfxAnimation(x, y); /* reset animation values for old field */
8148 if (!CAN_MOVE(element) ||
8149 (CAN_FALL(element) && direction == MV_DOWN &&
8150 (element == EL_SPRING ||
8151 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8152 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8153 GfxDir[x][y] = MovDir[newx][newy] = 0;
8155 TEST_DrawLevelField(x, y);
8156 TEST_DrawLevelField(newx, newy);
8158 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8160 /* prevent pushed element from moving on in pushed direction */
8161 if (pushed_by_player && CAN_MOVE(element) &&
8162 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8163 !(element_info[element].move_pattern & direction))
8164 TurnRound(newx, newy);
8166 /* prevent elements on conveyor belt from moving on in last direction */
8167 if (pushed_by_conveyor && CAN_FALL(element) &&
8168 direction & MV_HORIZONTAL)
8169 MovDir[newx][newy] = 0;
8171 if (!pushed_by_player)
8173 int nextx = newx + dx, nexty = newy + dy;
8174 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8176 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8178 if (CAN_FALL(element) && direction == MV_DOWN)
8179 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8181 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8182 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8184 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8185 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8188 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8190 TestIfBadThingTouchesPlayer(newx, newy);
8191 TestIfBadThingTouchesFriend(newx, newy);
8193 if (!IS_CUSTOM_ELEMENT(element))
8194 TestIfBadThingTouchesOtherBadThing(newx, newy);
8196 else if (element == EL_PENGUIN)
8197 TestIfFriendTouchesBadThing(newx, newy);
8199 if (DONT_GET_HIT_BY(element))
8201 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8204 /* give the player one last chance (one more frame) to move away */
8205 if (CAN_FALL(element) && direction == MV_DOWN &&
8206 (last_line || (!IS_FREE(x, newy + 1) &&
8207 (!IS_PLAYER(x, newy + 1) ||
8208 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8211 if (pushed_by_player && !game.use_change_when_pushing_bug)
8213 int push_side = MV_DIR_OPPOSITE(direction);
8214 struct PlayerInfo *player = PLAYERINFO(x, y);
8216 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8217 player->index_bit, push_side);
8218 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8219 player->index_bit, push_side);
8222 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8223 MovDelay[newx][newy] = 1;
8225 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8227 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8228 TestIfElementHitsCustomElement(newx, newy, direction);
8229 TestIfPlayerTouchesCustomElement(newx, newy);
8230 TestIfElementTouchesCustomElement(newx, newy);
8232 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8233 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8234 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8235 MV_DIR_OPPOSITE(direction));
8238 int AmoebeNachbarNr(int ax, int ay)
8241 int element = Feld[ax][ay];
8243 static int xy[4][2] =
8251 for (i = 0; i < NUM_DIRECTIONS; i++)
8253 int x = ax + xy[i][0];
8254 int y = ay + xy[i][1];
8256 if (!IN_LEV_FIELD(x, y))
8259 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8260 group_nr = AmoebaNr[x][y];
8266 void AmoebenVereinigen(int ax, int ay)
8268 int i, x, y, xx, yy;
8269 int new_group_nr = AmoebaNr[ax][ay];
8270 static int xy[4][2] =
8278 if (new_group_nr == 0)
8281 for (i = 0; i < NUM_DIRECTIONS; i++)
8286 if (!IN_LEV_FIELD(x, y))
8289 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8290 Feld[x][y] == EL_BD_AMOEBA ||
8291 Feld[x][y] == EL_AMOEBA_DEAD) &&
8292 AmoebaNr[x][y] != new_group_nr)
8294 int old_group_nr = AmoebaNr[x][y];
8296 if (old_group_nr == 0)
8299 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8300 AmoebaCnt[old_group_nr] = 0;
8301 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8302 AmoebaCnt2[old_group_nr] = 0;
8304 SCAN_PLAYFIELD(xx, yy)
8306 if (AmoebaNr[xx][yy] == old_group_nr)
8307 AmoebaNr[xx][yy] = new_group_nr;
8313 void AmoebeUmwandeln(int ax, int ay)
8317 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8319 int group_nr = AmoebaNr[ax][ay];
8324 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8325 printf("AmoebeUmwandeln(): This should never happen!\n");
8330 SCAN_PLAYFIELD(x, y)
8332 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8335 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8339 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8340 SND_AMOEBA_TURNING_TO_GEM :
8341 SND_AMOEBA_TURNING_TO_ROCK));
8346 static int xy[4][2] =
8354 for (i = 0; i < NUM_DIRECTIONS; i++)
8359 if (!IN_LEV_FIELD(x, y))
8362 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8364 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8365 SND_AMOEBA_TURNING_TO_GEM :
8366 SND_AMOEBA_TURNING_TO_ROCK));
8373 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8376 int group_nr = AmoebaNr[ax][ay];
8377 boolean done = FALSE;
8382 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8383 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8388 SCAN_PLAYFIELD(x, y)
8390 if (AmoebaNr[x][y] == group_nr &&
8391 (Feld[x][y] == EL_AMOEBA_DEAD ||
8392 Feld[x][y] == EL_BD_AMOEBA ||
8393 Feld[x][y] == EL_AMOEBA_GROWING))
8396 Feld[x][y] = new_element;
8397 InitField(x, y, FALSE);
8398 TEST_DrawLevelField(x, y);
8404 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8405 SND_BD_AMOEBA_TURNING_TO_ROCK :
8406 SND_BD_AMOEBA_TURNING_TO_GEM));
8409 void AmoebeWaechst(int x, int y)
8411 static unsigned int sound_delay = 0;
8412 static unsigned int sound_delay_value = 0;
8414 if (!MovDelay[x][y]) /* start new growing cycle */
8418 if (DelayReached(&sound_delay, sound_delay_value))
8420 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8421 sound_delay_value = 30;
8425 if (MovDelay[x][y]) /* wait some time before growing bigger */
8428 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8430 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8431 6 - MovDelay[x][y]);
8433 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8436 if (!MovDelay[x][y])
8438 Feld[x][y] = Store[x][y];
8440 TEST_DrawLevelField(x, y);
8445 void AmoebaDisappearing(int x, int y)
8447 static unsigned int sound_delay = 0;
8448 static unsigned int sound_delay_value = 0;
8450 if (!MovDelay[x][y]) /* start new shrinking cycle */
8454 if (DelayReached(&sound_delay, sound_delay_value))
8455 sound_delay_value = 30;
8458 if (MovDelay[x][y]) /* wait some time before shrinking */
8461 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8463 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8464 6 - MovDelay[x][y]);
8466 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8469 if (!MovDelay[x][y])
8471 Feld[x][y] = EL_EMPTY;
8472 TEST_DrawLevelField(x, y);
8474 /* don't let mole enter this field in this cycle;
8475 (give priority to objects falling to this field from above) */
8481 void AmoebeAbleger(int ax, int ay)
8484 int element = Feld[ax][ay];
8485 int graphic = el2img(element);
8486 int newax = ax, neway = ay;
8487 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8488 static int xy[4][2] =
8496 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8498 Feld[ax][ay] = EL_AMOEBA_DEAD;
8499 TEST_DrawLevelField(ax, ay);
8503 if (IS_ANIMATED(graphic))
8504 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8506 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8507 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8509 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8512 if (MovDelay[ax][ay])
8516 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8519 int x = ax + xy[start][0];
8520 int y = ay + xy[start][1];
8522 if (!IN_LEV_FIELD(x, y))
8525 if (IS_FREE(x, y) ||
8526 CAN_GROW_INTO(Feld[x][y]) ||
8527 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8528 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8534 if (newax == ax && neway == ay)
8537 else /* normal or "filled" (BD style) amoeba */
8540 boolean waiting_for_player = FALSE;
8542 for (i = 0; i < NUM_DIRECTIONS; i++)
8544 int j = (start + i) % 4;
8545 int x = ax + xy[j][0];
8546 int y = ay + xy[j][1];
8548 if (!IN_LEV_FIELD(x, y))
8551 if (IS_FREE(x, y) ||
8552 CAN_GROW_INTO(Feld[x][y]) ||
8553 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8554 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8560 else if (IS_PLAYER(x, y))
8561 waiting_for_player = TRUE;
8564 if (newax == ax && neway == ay) /* amoeba cannot grow */
8566 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8568 Feld[ax][ay] = EL_AMOEBA_DEAD;
8569 TEST_DrawLevelField(ax, ay);
8570 AmoebaCnt[AmoebaNr[ax][ay]]--;
8572 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8574 if (element == EL_AMOEBA_FULL)
8575 AmoebeUmwandeln(ax, ay);
8576 else if (element == EL_BD_AMOEBA)
8577 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8582 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8584 /* amoeba gets larger by growing in some direction */
8586 int new_group_nr = AmoebaNr[ax][ay];
8589 if (new_group_nr == 0)
8591 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8592 printf("AmoebeAbleger(): This should never happen!\n");
8597 AmoebaNr[newax][neway] = new_group_nr;
8598 AmoebaCnt[new_group_nr]++;
8599 AmoebaCnt2[new_group_nr]++;
8601 /* if amoeba touches other amoeba(s) after growing, unify them */
8602 AmoebenVereinigen(newax, neway);
8604 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8606 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8612 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8613 (neway == lev_fieldy - 1 && newax != ax))
8615 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8616 Store[newax][neway] = element;
8618 else if (neway == ay || element == EL_EMC_DRIPPER)
8620 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8622 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8626 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8627 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8628 Store[ax][ay] = EL_AMOEBA_DROP;
8629 ContinueMoving(ax, ay);
8633 TEST_DrawLevelField(newax, neway);
8636 void Life(int ax, int ay)
8640 int element = Feld[ax][ay];
8641 int graphic = el2img(element);
8642 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8644 boolean changed = FALSE;
8646 if (IS_ANIMATED(graphic))
8647 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8652 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8653 MovDelay[ax][ay] = life_time;
8655 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8658 if (MovDelay[ax][ay])
8662 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8664 int xx = ax+x1, yy = ay+y1;
8667 if (!IN_LEV_FIELD(xx, yy))
8670 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8672 int x = xx+x2, y = yy+y2;
8674 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8677 if (((Feld[x][y] == element ||
8678 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8680 (IS_FREE(x, y) && Stop[x][y]))
8684 if (xx == ax && yy == ay) /* field in the middle */
8686 if (nachbarn < life_parameter[0] ||
8687 nachbarn > life_parameter[1])
8689 Feld[xx][yy] = EL_EMPTY;
8691 TEST_DrawLevelField(xx, yy);
8692 Stop[xx][yy] = TRUE;
8696 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8697 { /* free border field */
8698 if (nachbarn >= life_parameter[2] &&
8699 nachbarn <= life_parameter[3])
8701 Feld[xx][yy] = element;
8702 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8704 TEST_DrawLevelField(xx, yy);
8705 Stop[xx][yy] = TRUE;
8712 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8713 SND_GAME_OF_LIFE_GROWING);
8716 static void InitRobotWheel(int x, int y)
8718 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8721 static void RunRobotWheel(int x, int y)
8723 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8726 static void StopRobotWheel(int x, int y)
8728 if (ZX == x && ZY == y)
8732 game.robot_wheel_active = FALSE;
8736 static void InitTimegateWheel(int x, int y)
8738 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8741 static void RunTimegateWheel(int x, int y)
8743 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8746 static void InitMagicBallDelay(int x, int y)
8748 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8751 static void ActivateMagicBall(int bx, int by)
8755 if (level.ball_random)
8757 int pos_border = RND(8); /* select one of the eight border elements */
8758 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8759 int xx = pos_content % 3;
8760 int yy = pos_content / 3;
8765 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8766 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8770 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8772 int xx = x - bx + 1;
8773 int yy = y - by + 1;
8775 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8776 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8780 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8783 void CheckExit(int x, int y)
8785 if (local_player->gems_still_needed > 0 ||
8786 local_player->sokobanfields_still_needed > 0 ||
8787 local_player->lights_still_needed > 0)
8789 int element = Feld[x][y];
8790 int graphic = el2img(element);
8792 if (IS_ANIMATED(graphic))
8793 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8798 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8801 Feld[x][y] = EL_EXIT_OPENING;
8803 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8806 void CheckExitEM(int x, int y)
8808 if (local_player->gems_still_needed > 0 ||
8809 local_player->sokobanfields_still_needed > 0 ||
8810 local_player->lights_still_needed > 0)
8812 int element = Feld[x][y];
8813 int graphic = el2img(element);
8815 if (IS_ANIMATED(graphic))
8816 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8821 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8824 Feld[x][y] = EL_EM_EXIT_OPENING;
8826 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8829 void CheckExitSteel(int x, int y)
8831 if (local_player->gems_still_needed > 0 ||
8832 local_player->sokobanfields_still_needed > 0 ||
8833 local_player->lights_still_needed > 0)
8835 int element = Feld[x][y];
8836 int graphic = el2img(element);
8838 if (IS_ANIMATED(graphic))
8839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8844 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8847 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8849 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8852 void CheckExitSteelEM(int x, int y)
8854 if (local_player->gems_still_needed > 0 ||
8855 local_player->sokobanfields_still_needed > 0 ||
8856 local_player->lights_still_needed > 0)
8858 int element = Feld[x][y];
8859 int graphic = el2img(element);
8861 if (IS_ANIMATED(graphic))
8862 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8867 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8870 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8872 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8875 void CheckExitSP(int x, int y)
8877 if (local_player->gems_still_needed > 0)
8879 int element = Feld[x][y];
8880 int graphic = el2img(element);
8882 if (IS_ANIMATED(graphic))
8883 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8888 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8891 Feld[x][y] = EL_SP_EXIT_OPENING;
8893 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8896 static void CloseAllOpenTimegates()
8900 SCAN_PLAYFIELD(x, y)
8902 int element = Feld[x][y];
8904 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8906 Feld[x][y] = EL_TIMEGATE_CLOSING;
8908 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8913 void DrawTwinkleOnField(int x, int y)
8915 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8918 if (Feld[x][y] == EL_BD_DIAMOND)
8921 if (MovDelay[x][y] == 0) /* next animation frame */
8922 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8924 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8928 DrawLevelElementAnimation(x, y, Feld[x][y]);
8930 if (MovDelay[x][y] != 0)
8932 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8933 10 - MovDelay[x][y]);
8935 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8940 void MauerWaechst(int x, int y)
8944 if (!MovDelay[x][y]) /* next animation frame */
8945 MovDelay[x][y] = 3 * delay;
8947 if (MovDelay[x][y]) /* wait some time before next frame */
8951 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8953 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8954 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8956 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8959 if (!MovDelay[x][y])
8961 if (MovDir[x][y] == MV_LEFT)
8963 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8964 TEST_DrawLevelField(x - 1, y);
8966 else if (MovDir[x][y] == MV_RIGHT)
8968 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8969 TEST_DrawLevelField(x + 1, y);
8971 else if (MovDir[x][y] == MV_UP)
8973 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8974 TEST_DrawLevelField(x, y - 1);
8978 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8979 TEST_DrawLevelField(x, y + 1);
8982 Feld[x][y] = Store[x][y];
8984 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8985 TEST_DrawLevelField(x, y);
8990 void MauerAbleger(int ax, int ay)
8992 int element = Feld[ax][ay];
8993 int graphic = el2img(element);
8994 boolean oben_frei = FALSE, unten_frei = FALSE;
8995 boolean links_frei = FALSE, rechts_frei = FALSE;
8996 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8997 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8998 boolean new_wall = FALSE;
9000 if (IS_ANIMATED(graphic))
9001 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9003 if (!MovDelay[ax][ay]) /* start building new wall */
9004 MovDelay[ax][ay] = 6;
9006 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9009 if (MovDelay[ax][ay])
9013 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9015 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9017 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9019 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9022 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9023 element == EL_EXPANDABLE_WALL_ANY)
9027 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9028 Store[ax][ay-1] = element;
9029 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9030 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9031 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9032 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9037 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9038 Store[ax][ay+1] = element;
9039 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9040 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9041 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9042 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9047 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9048 element == EL_EXPANDABLE_WALL_ANY ||
9049 element == EL_EXPANDABLE_WALL ||
9050 element == EL_BD_EXPANDABLE_WALL)
9054 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9055 Store[ax-1][ay] = element;
9056 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9057 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9058 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9059 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9065 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9066 Store[ax+1][ay] = element;
9067 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9068 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9069 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9070 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9075 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9076 TEST_DrawLevelField(ax, ay);
9078 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9080 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9081 unten_massiv = TRUE;
9082 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9083 links_massiv = TRUE;
9084 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9085 rechts_massiv = TRUE;
9087 if (((oben_massiv && unten_massiv) ||
9088 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9089 element == EL_EXPANDABLE_WALL) &&
9090 ((links_massiv && rechts_massiv) ||
9091 element == EL_EXPANDABLE_WALL_VERTICAL))
9092 Feld[ax][ay] = EL_WALL;
9095 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9098 void MauerAblegerStahl(int ax, int ay)
9100 int element = Feld[ax][ay];
9101 int graphic = el2img(element);
9102 boolean oben_frei = FALSE, unten_frei = FALSE;
9103 boolean links_frei = FALSE, rechts_frei = FALSE;
9104 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9105 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9106 boolean new_wall = FALSE;
9108 if (IS_ANIMATED(graphic))
9109 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9111 if (!MovDelay[ax][ay]) /* start building new wall */
9112 MovDelay[ax][ay] = 6;
9114 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9117 if (MovDelay[ax][ay])
9121 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9123 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9125 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9127 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9130 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9131 element == EL_EXPANDABLE_STEELWALL_ANY)
9135 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9136 Store[ax][ay-1] = element;
9137 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9138 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9139 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9140 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9145 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9146 Store[ax][ay+1] = element;
9147 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9148 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9149 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9150 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9155 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9156 element == EL_EXPANDABLE_STEELWALL_ANY)
9160 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9161 Store[ax-1][ay] = element;
9162 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9163 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9164 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9165 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9171 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9172 Store[ax+1][ay] = element;
9173 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9174 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9175 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9176 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9181 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9183 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9184 unten_massiv = TRUE;
9185 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9186 links_massiv = TRUE;
9187 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9188 rechts_massiv = TRUE;
9190 if (((oben_massiv && unten_massiv) ||
9191 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9192 ((links_massiv && rechts_massiv) ||
9193 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9194 Feld[ax][ay] = EL_STEELWALL;
9197 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9200 void CheckForDragon(int x, int y)
9203 boolean dragon_found = FALSE;
9204 static int xy[4][2] =
9212 for (i = 0; i < NUM_DIRECTIONS; i++)
9214 for (j = 0; j < 4; j++)
9216 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9218 if (IN_LEV_FIELD(xx, yy) &&
9219 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9221 if (Feld[xx][yy] == EL_DRAGON)
9222 dragon_found = TRUE;
9231 for (i = 0; i < NUM_DIRECTIONS; i++)
9233 for (j = 0; j < 3; j++)
9235 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9237 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9239 Feld[xx][yy] = EL_EMPTY;
9240 TEST_DrawLevelField(xx, yy);
9249 static void InitBuggyBase(int x, int y)
9251 int element = Feld[x][y];
9252 int activating_delay = FRAMES_PER_SECOND / 4;
9255 (element == EL_SP_BUGGY_BASE ?
9256 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9257 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9259 element == EL_SP_BUGGY_BASE_ACTIVE ?
9260 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9263 static void WarnBuggyBase(int x, int y)
9266 static int xy[4][2] =
9274 for (i = 0; i < NUM_DIRECTIONS; i++)
9276 int xx = x + xy[i][0];
9277 int yy = y + xy[i][1];
9279 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9281 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9288 static void InitTrap(int x, int y)
9290 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9293 static void ActivateTrap(int x, int y)
9295 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9298 static void ChangeActiveTrap(int x, int y)
9300 int graphic = IMG_TRAP_ACTIVE;
9302 /* if new animation frame was drawn, correct crumbled sand border */
9303 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9304 TEST_DrawLevelFieldCrumbled(x, y);
9307 static int getSpecialActionElement(int element, int number, int base_element)
9309 return (element != EL_EMPTY ? element :
9310 number != -1 ? base_element + number - 1 :
9314 static int getModifiedActionNumber(int value_old, int operator, int operand,
9315 int value_min, int value_max)
9317 int value_new = (operator == CA_MODE_SET ? operand :
9318 operator == CA_MODE_ADD ? value_old + operand :
9319 operator == CA_MODE_SUBTRACT ? value_old - operand :
9320 operator == CA_MODE_MULTIPLY ? value_old * operand :
9321 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9322 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9325 return (value_new < value_min ? value_min :
9326 value_new > value_max ? value_max :
9330 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9332 struct ElementInfo *ei = &element_info[element];
9333 struct ElementChangeInfo *change = &ei->change_page[page];
9334 int target_element = change->target_element;
9335 int action_type = change->action_type;
9336 int action_mode = change->action_mode;
9337 int action_arg = change->action_arg;
9338 int action_element = change->action_element;
9341 if (!change->has_action)
9344 /* ---------- determine action paramater values -------------------------- */
9346 int level_time_value =
9347 (level.time > 0 ? TimeLeft :
9350 int action_arg_element_raw =
9351 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9352 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9353 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9354 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9355 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9356 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9357 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9359 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9361 int action_arg_direction =
9362 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9363 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9364 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9365 change->actual_trigger_side :
9366 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9367 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9370 int action_arg_number_min =
9371 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9374 int action_arg_number_max =
9375 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9376 action_type == CA_SET_LEVEL_GEMS ? 999 :
9377 action_type == CA_SET_LEVEL_TIME ? 9999 :
9378 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9379 action_type == CA_SET_CE_VALUE ? 9999 :
9380 action_type == CA_SET_CE_SCORE ? 9999 :
9383 int action_arg_number_reset =
9384 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9385 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9386 action_type == CA_SET_LEVEL_TIME ? level.time :
9387 action_type == CA_SET_LEVEL_SCORE ? 0 :
9388 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9389 action_type == CA_SET_CE_SCORE ? 0 :
9392 int action_arg_number =
9393 (action_arg <= CA_ARG_MAX ? action_arg :
9394 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9395 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9396 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9397 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9398 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9399 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9400 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9401 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9402 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9403 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9404 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9405 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9406 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9407 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9408 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9409 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9410 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9411 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9412 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9413 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9414 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9417 int action_arg_number_old =
9418 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9419 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9420 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9421 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9422 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9425 int action_arg_number_new =
9426 getModifiedActionNumber(action_arg_number_old,
9427 action_mode, action_arg_number,
9428 action_arg_number_min, action_arg_number_max);
9430 int trigger_player_bits =
9431 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9432 change->actual_trigger_player_bits : change->trigger_player);
9434 int action_arg_player_bits =
9435 (action_arg >= CA_ARG_PLAYER_1 &&
9436 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9437 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9438 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9441 /* ---------- execute action -------------------------------------------- */
9443 switch (action_type)
9450 /* ---------- level actions ------------------------------------------- */
9452 case CA_RESTART_LEVEL:
9454 game.restart_level = TRUE;
9459 case CA_SHOW_ENVELOPE:
9461 int element = getSpecialActionElement(action_arg_element,
9462 action_arg_number, EL_ENVELOPE_1);
9464 if (IS_ENVELOPE(element))
9465 local_player->show_envelope = element;
9470 case CA_SET_LEVEL_TIME:
9472 if (level.time > 0) /* only modify limited time value */
9474 TimeLeft = action_arg_number_new;
9476 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9478 DisplayGameControlValues();
9480 if (!TimeLeft && setup.time_limit)
9481 for (i = 0; i < MAX_PLAYERS; i++)
9482 KillPlayer(&stored_player[i]);
9488 case CA_SET_LEVEL_SCORE:
9490 local_player->score = action_arg_number_new;
9492 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9494 DisplayGameControlValues();
9499 case CA_SET_LEVEL_GEMS:
9501 local_player->gems_still_needed = action_arg_number_new;
9503 game.snapshot.collected_item = TRUE;
9505 game_panel_controls[GAME_PANEL_GEMS].value =
9506 local_player->gems_still_needed;
9508 DisplayGameControlValues();
9513 case CA_SET_LEVEL_WIND:
9515 game.wind_direction = action_arg_direction;
9520 case CA_SET_LEVEL_RANDOM_SEED:
9522 /* ensure that setting a new random seed while playing is predictable */
9523 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9528 /* ---------- player actions ------------------------------------------ */
9530 case CA_MOVE_PLAYER:
9532 /* automatically move to the next field in specified direction */
9533 for (i = 0; i < MAX_PLAYERS; i++)
9534 if (trigger_player_bits & (1 << i))
9535 stored_player[i].programmed_action = action_arg_direction;
9540 case CA_EXIT_PLAYER:
9542 for (i = 0; i < MAX_PLAYERS; i++)
9543 if (action_arg_player_bits & (1 << i))
9544 PlayerWins(&stored_player[i]);
9549 case CA_KILL_PLAYER:
9551 for (i = 0; i < MAX_PLAYERS; i++)
9552 if (action_arg_player_bits & (1 << i))
9553 KillPlayer(&stored_player[i]);
9558 case CA_SET_PLAYER_KEYS:
9560 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9561 int element = getSpecialActionElement(action_arg_element,
9562 action_arg_number, EL_KEY_1);
9564 if (IS_KEY(element))
9566 for (i = 0; i < MAX_PLAYERS; i++)
9568 if (trigger_player_bits & (1 << i))
9570 stored_player[i].key[KEY_NR(element)] = key_state;
9572 DrawGameDoorValues();
9580 case CA_SET_PLAYER_SPEED:
9582 for (i = 0; i < MAX_PLAYERS; i++)
9584 if (trigger_player_bits & (1 << i))
9586 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9588 if (action_arg == CA_ARG_SPEED_FASTER &&
9589 stored_player[i].cannot_move)
9591 action_arg_number = STEPSIZE_VERY_SLOW;
9593 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9594 action_arg == CA_ARG_SPEED_FASTER)
9596 action_arg_number = 2;
9597 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9600 else if (action_arg == CA_ARG_NUMBER_RESET)
9602 action_arg_number = level.initial_player_stepsize[i];
9606 getModifiedActionNumber(move_stepsize,
9609 action_arg_number_min,
9610 action_arg_number_max);
9612 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9619 case CA_SET_PLAYER_SHIELD:
9621 for (i = 0; i < MAX_PLAYERS; i++)
9623 if (trigger_player_bits & (1 << i))
9625 if (action_arg == CA_ARG_SHIELD_OFF)
9627 stored_player[i].shield_normal_time_left = 0;
9628 stored_player[i].shield_deadly_time_left = 0;
9630 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9632 stored_player[i].shield_normal_time_left = 999999;
9634 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9636 stored_player[i].shield_normal_time_left = 999999;
9637 stored_player[i].shield_deadly_time_left = 999999;
9645 case CA_SET_PLAYER_GRAVITY:
9647 for (i = 0; i < MAX_PLAYERS; i++)
9649 if (trigger_player_bits & (1 << i))
9651 stored_player[i].gravity =
9652 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9653 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9654 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9655 stored_player[i].gravity);
9662 case CA_SET_PLAYER_ARTWORK:
9664 for (i = 0; i < MAX_PLAYERS; i++)
9666 if (trigger_player_bits & (1 << i))
9668 int artwork_element = action_arg_element;
9670 if (action_arg == CA_ARG_ELEMENT_RESET)
9672 (level.use_artwork_element[i] ? level.artwork_element[i] :
9673 stored_player[i].element_nr);
9675 if (stored_player[i].artwork_element != artwork_element)
9676 stored_player[i].Frame = 0;
9678 stored_player[i].artwork_element = artwork_element;
9680 SetPlayerWaiting(&stored_player[i], FALSE);
9682 /* set number of special actions for bored and sleeping animation */
9683 stored_player[i].num_special_action_bored =
9684 get_num_special_action(artwork_element,
9685 ACTION_BORING_1, ACTION_BORING_LAST);
9686 stored_player[i].num_special_action_sleeping =
9687 get_num_special_action(artwork_element,
9688 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9695 case CA_SET_PLAYER_INVENTORY:
9697 for (i = 0; i < MAX_PLAYERS; i++)
9699 struct PlayerInfo *player = &stored_player[i];
9702 if (trigger_player_bits & (1 << i))
9704 int inventory_element = action_arg_element;
9706 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9707 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9708 action_arg == CA_ARG_ELEMENT_ACTION)
9710 int element = inventory_element;
9711 int collect_count = element_info[element].collect_count_initial;
9713 if (!IS_CUSTOM_ELEMENT(element))
9716 if (collect_count == 0)
9717 player->inventory_infinite_element = element;
9719 for (k = 0; k < collect_count; k++)
9720 if (player->inventory_size < MAX_INVENTORY_SIZE)
9721 player->inventory_element[player->inventory_size++] =
9724 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9725 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9726 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9728 if (player->inventory_infinite_element != EL_UNDEFINED &&
9729 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9730 action_arg_element_raw))
9731 player->inventory_infinite_element = EL_UNDEFINED;
9733 for (k = 0, j = 0; j < player->inventory_size; j++)
9735 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9736 action_arg_element_raw))
9737 player->inventory_element[k++] = player->inventory_element[j];
9740 player->inventory_size = k;
9742 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9744 if (player->inventory_size > 0)
9746 for (j = 0; j < player->inventory_size - 1; j++)
9747 player->inventory_element[j] = player->inventory_element[j + 1];
9749 player->inventory_size--;
9752 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9754 if (player->inventory_size > 0)
9755 player->inventory_size--;
9757 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9759 player->inventory_infinite_element = EL_UNDEFINED;
9760 player->inventory_size = 0;
9762 else if (action_arg == CA_ARG_INVENTORY_RESET)
9764 player->inventory_infinite_element = EL_UNDEFINED;
9765 player->inventory_size = 0;
9767 if (level.use_initial_inventory[i])
9769 for (j = 0; j < level.initial_inventory_size[i]; j++)
9771 int element = level.initial_inventory_content[i][j];
9772 int collect_count = element_info[element].collect_count_initial;
9774 if (!IS_CUSTOM_ELEMENT(element))
9777 if (collect_count == 0)
9778 player->inventory_infinite_element = element;
9780 for (k = 0; k < collect_count; k++)
9781 if (player->inventory_size < MAX_INVENTORY_SIZE)
9782 player->inventory_element[player->inventory_size++] =
9793 /* ---------- CE actions ---------------------------------------------- */
9795 case CA_SET_CE_VALUE:
9797 int last_ce_value = CustomValue[x][y];
9799 CustomValue[x][y] = action_arg_number_new;
9801 if (CustomValue[x][y] != last_ce_value)
9803 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9804 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9806 if (CustomValue[x][y] == 0)
9808 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9809 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9816 case CA_SET_CE_SCORE:
9818 int last_ce_score = ei->collect_score;
9820 ei->collect_score = action_arg_number_new;
9822 if (ei->collect_score != last_ce_score)
9824 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9825 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9827 if (ei->collect_score == 0)
9831 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9832 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9835 This is a very special case that seems to be a mixture between
9836 CheckElementChange() and CheckTriggeredElementChange(): while
9837 the first one only affects single elements that are triggered
9838 directly, the second one affects multiple elements in the playfield
9839 that are triggered indirectly by another element. This is a third
9840 case: Changing the CE score always affects multiple identical CEs,
9841 so every affected CE must be checked, not only the single CE for
9842 which the CE score was changed in the first place (as every instance
9843 of that CE shares the same CE score, and therefore also can change)!
9845 SCAN_PLAYFIELD(xx, yy)
9847 if (Feld[xx][yy] == element)
9848 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9849 CE_SCORE_GETS_ZERO);
9857 case CA_SET_CE_ARTWORK:
9859 int artwork_element = action_arg_element;
9860 boolean reset_frame = FALSE;
9863 if (action_arg == CA_ARG_ELEMENT_RESET)
9864 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9867 if (ei->gfx_element != artwork_element)
9870 ei->gfx_element = artwork_element;
9872 SCAN_PLAYFIELD(xx, yy)
9874 if (Feld[xx][yy] == element)
9878 ResetGfxAnimation(xx, yy);
9879 ResetRandomAnimationValue(xx, yy);
9882 TEST_DrawLevelField(xx, yy);
9889 /* ---------- engine actions ------------------------------------------ */
9891 case CA_SET_ENGINE_SCAN_MODE:
9893 InitPlayfieldScanMode(action_arg);
9903 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9905 int old_element = Feld[x][y];
9906 int new_element = GetElementFromGroupElement(element);
9907 int previous_move_direction = MovDir[x][y];
9908 int last_ce_value = CustomValue[x][y];
9909 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9910 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9911 boolean add_player_onto_element = (new_element_is_player &&
9912 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9913 IS_WALKABLE(old_element));
9915 if (!add_player_onto_element)
9917 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9918 RemoveMovingField(x, y);
9922 Feld[x][y] = new_element;
9924 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9925 MovDir[x][y] = previous_move_direction;
9927 if (element_info[new_element].use_last_ce_value)
9928 CustomValue[x][y] = last_ce_value;
9930 InitField_WithBug1(x, y, FALSE);
9932 new_element = Feld[x][y]; /* element may have changed */
9934 ResetGfxAnimation(x, y);
9935 ResetRandomAnimationValue(x, y);
9937 TEST_DrawLevelField(x, y);
9939 if (GFX_CRUMBLED(new_element))
9940 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9943 /* check if element under the player changes from accessible to unaccessible
9944 (needed for special case of dropping element which then changes) */
9945 /* (must be checked after creating new element for walkable group elements) */
9946 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9947 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9954 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9955 if (new_element_is_player)
9956 RelocatePlayer(x, y, new_element);
9959 ChangeCount[x][y]++; /* count number of changes in the same frame */
9961 TestIfBadThingTouchesPlayer(x, y);
9962 TestIfPlayerTouchesCustomElement(x, y);
9963 TestIfElementTouchesCustomElement(x, y);
9966 static void CreateField(int x, int y, int element)
9968 CreateFieldExt(x, y, element, FALSE);
9971 static void CreateElementFromChange(int x, int y, int element)
9973 element = GET_VALID_RUNTIME_ELEMENT(element);
9975 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9977 int old_element = Feld[x][y];
9979 /* prevent changed element from moving in same engine frame
9980 unless both old and new element can either fall or move */
9981 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9982 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9986 CreateFieldExt(x, y, element, TRUE);
9989 static boolean ChangeElement(int x, int y, int element, int page)
9991 struct ElementInfo *ei = &element_info[element];
9992 struct ElementChangeInfo *change = &ei->change_page[page];
9993 int ce_value = CustomValue[x][y];
9994 int ce_score = ei->collect_score;
9996 int old_element = Feld[x][y];
9998 /* always use default change event to prevent running into a loop */
9999 if (ChangeEvent[x][y] == -1)
10000 ChangeEvent[x][y] = CE_DELAY;
10002 if (ChangeEvent[x][y] == CE_DELAY)
10004 /* reset actual trigger element, trigger player and action element */
10005 change->actual_trigger_element = EL_EMPTY;
10006 change->actual_trigger_player = EL_EMPTY;
10007 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10008 change->actual_trigger_side = CH_SIDE_NONE;
10009 change->actual_trigger_ce_value = 0;
10010 change->actual_trigger_ce_score = 0;
10013 /* do not change elements more than a specified maximum number of changes */
10014 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10017 ChangeCount[x][y]++; /* count number of changes in the same frame */
10019 if (change->explode)
10026 if (change->use_target_content)
10028 boolean complete_replace = TRUE;
10029 boolean can_replace[3][3];
10032 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10035 boolean is_walkable;
10036 boolean is_diggable;
10037 boolean is_collectible;
10038 boolean is_removable;
10039 boolean is_destructible;
10040 int ex = x + xx - 1;
10041 int ey = y + yy - 1;
10042 int content_element = change->target_content.e[xx][yy];
10045 can_replace[xx][yy] = TRUE;
10047 if (ex == x && ey == y) /* do not check changing element itself */
10050 if (content_element == EL_EMPTY_SPACE)
10052 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10057 if (!IN_LEV_FIELD(ex, ey))
10059 can_replace[xx][yy] = FALSE;
10060 complete_replace = FALSE;
10067 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10068 e = MovingOrBlocked2Element(ex, ey);
10070 is_empty = (IS_FREE(ex, ey) ||
10071 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10073 is_walkable = (is_empty || IS_WALKABLE(e));
10074 is_diggable = (is_empty || IS_DIGGABLE(e));
10075 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10076 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10077 is_removable = (is_diggable || is_collectible);
10079 can_replace[xx][yy] =
10080 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10081 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10082 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10083 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10084 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10085 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10086 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10088 if (!can_replace[xx][yy])
10089 complete_replace = FALSE;
10092 if (!change->only_if_complete || complete_replace)
10094 boolean something_has_changed = FALSE;
10096 if (change->only_if_complete && change->use_random_replace &&
10097 RND(100) < change->random_percentage)
10100 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10102 int ex = x + xx - 1;
10103 int ey = y + yy - 1;
10104 int content_element;
10106 if (can_replace[xx][yy] && (!change->use_random_replace ||
10107 RND(100) < change->random_percentage))
10109 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10110 RemoveMovingField(ex, ey);
10112 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10114 content_element = change->target_content.e[xx][yy];
10115 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10116 ce_value, ce_score);
10118 CreateElementFromChange(ex, ey, target_element);
10120 something_has_changed = TRUE;
10122 /* for symmetry reasons, freeze newly created border elements */
10123 if (ex != x || ey != y)
10124 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10128 if (something_has_changed)
10130 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10131 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10137 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10138 ce_value, ce_score);
10140 if (element == EL_DIAGONAL_GROWING ||
10141 element == EL_DIAGONAL_SHRINKING)
10143 target_element = Store[x][y];
10145 Store[x][y] = EL_EMPTY;
10148 CreateElementFromChange(x, y, target_element);
10150 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10151 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10154 /* this uses direct change before indirect change */
10155 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10160 static void HandleElementChange(int x, int y, int page)
10162 int element = MovingOrBlocked2Element(x, y);
10163 struct ElementInfo *ei = &element_info[element];
10164 struct ElementChangeInfo *change = &ei->change_page[page];
10165 boolean handle_action_before_change = FALSE;
10168 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10169 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10172 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10173 x, y, element, element_info[element].token_name);
10174 printf("HandleElementChange(): This should never happen!\n");
10179 /* this can happen with classic bombs on walkable, changing elements */
10180 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10185 if (ChangeDelay[x][y] == 0) /* initialize element change */
10187 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10189 if (change->can_change)
10191 /* !!! not clear why graphic animation should be reset at all here !!! */
10192 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10193 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10196 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10198 When using an animation frame delay of 1 (this only happens with
10199 "sp_zonk.moving.left/right" in the classic graphics), the default
10200 (non-moving) animation shows wrong animation frames (while the
10201 moving animation, like "sp_zonk.moving.left/right", is correct,
10202 so this graphical bug never shows up with the classic graphics).
10203 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10204 be drawn instead of the correct frames 0,1,2,3. This is caused by
10205 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10206 an element change: First when the change delay ("ChangeDelay[][]")
10207 counter has reached zero after decrementing, then a second time in
10208 the next frame (after "GfxFrame[][]" was already incremented) when
10209 "ChangeDelay[][]" is reset to the initial delay value again.
10211 This causes frame 0 to be drawn twice, while the last frame won't
10212 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10214 As some animations may already be cleverly designed around this bug
10215 (at least the "Snake Bite" snake tail animation does this), it cannot
10216 simply be fixed here without breaking such existing animations.
10217 Unfortunately, it cannot easily be detected if a graphics set was
10218 designed "before" or "after" the bug was fixed. As a workaround,
10219 a new graphics set option "game.graphics_engine_version" was added
10220 to be able to specify the game's major release version for which the
10221 graphics set was designed, which can then be used to decide if the
10222 bugfix should be used (version 4 and above) or not (version 3 or
10223 below, or if no version was specified at all, as with old sets).
10225 (The wrong/fixed animation frames can be tested with the test level set
10226 "test_gfxframe" and level "000", which contains a specially prepared
10227 custom element at level position (x/y) == (11/9) which uses the zonk
10228 animation mentioned above. Using "game.graphics_engine_version: 4"
10229 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10230 This can also be seen from the debug output for this test element.)
10233 /* when a custom element is about to change (for example by change delay),
10234 do not reset graphic animation when the custom element is moving */
10235 if (game.graphics_engine_version < 4 &&
10238 ResetGfxAnimation(x, y);
10239 ResetRandomAnimationValue(x, y);
10242 if (change->pre_change_function)
10243 change->pre_change_function(x, y);
10247 ChangeDelay[x][y]--;
10249 if (ChangeDelay[x][y] != 0) /* continue element change */
10251 if (change->can_change)
10253 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255 if (IS_ANIMATED(graphic))
10256 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10258 if (change->change_function)
10259 change->change_function(x, y);
10262 else /* finish element change */
10264 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10266 page = ChangePage[x][y];
10267 ChangePage[x][y] = -1;
10269 change = &ei->change_page[page];
10272 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10274 ChangeDelay[x][y] = 1; /* try change after next move step */
10275 ChangePage[x][y] = page; /* remember page to use for change */
10280 /* special case: set new level random seed before changing element */
10281 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10282 handle_action_before_change = TRUE;
10284 if (change->has_action && handle_action_before_change)
10285 ExecuteCustomElementAction(x, y, element, page);
10287 if (change->can_change)
10289 if (ChangeElement(x, y, element, page))
10291 if (change->post_change_function)
10292 change->post_change_function(x, y);
10296 if (change->has_action && !handle_action_before_change)
10297 ExecuteCustomElementAction(x, y, element, page);
10301 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10302 int trigger_element,
10304 int trigger_player,
10308 boolean change_done_any = FALSE;
10309 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10312 if (!(trigger_events[trigger_element][trigger_event]))
10315 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10317 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10319 int element = EL_CUSTOM_START + i;
10320 boolean change_done = FALSE;
10323 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10324 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10327 for (p = 0; p < element_info[element].num_change_pages; p++)
10329 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10331 if (change->can_change_or_has_action &&
10332 change->has_event[trigger_event] &&
10333 change->trigger_side & trigger_side &&
10334 change->trigger_player & trigger_player &&
10335 change->trigger_page & trigger_page_bits &&
10336 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10338 change->actual_trigger_element = trigger_element;
10339 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10340 change->actual_trigger_player_bits = trigger_player;
10341 change->actual_trigger_side = trigger_side;
10342 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10343 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10345 if ((change->can_change && !change_done) || change->has_action)
10349 SCAN_PLAYFIELD(x, y)
10351 if (Feld[x][y] == element)
10353 if (change->can_change && !change_done)
10355 /* if element already changed in this frame, not only prevent
10356 another element change (checked in ChangeElement()), but
10357 also prevent additional element actions for this element */
10359 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10360 !level.use_action_after_change_bug)
10363 ChangeDelay[x][y] = 1;
10364 ChangeEvent[x][y] = trigger_event;
10366 HandleElementChange(x, y, p);
10368 else if (change->has_action)
10370 /* if element already changed in this frame, not only prevent
10371 another element change (checked in ChangeElement()), but
10372 also prevent additional element actions for this element */
10374 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10375 !level.use_action_after_change_bug)
10378 ExecuteCustomElementAction(x, y, element, p);
10379 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10384 if (change->can_change)
10386 change_done = TRUE;
10387 change_done_any = TRUE;
10394 RECURSION_LOOP_DETECTION_END();
10396 return change_done_any;
10399 static boolean CheckElementChangeExt(int x, int y,
10401 int trigger_element,
10403 int trigger_player,
10406 boolean change_done = FALSE;
10409 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10410 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10413 if (Feld[x][y] == EL_BLOCKED)
10415 Blocked2Moving(x, y, &x, &y);
10416 element = Feld[x][y];
10419 /* check if element has already changed or is about to change after moving */
10420 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10421 Feld[x][y] != element) ||
10423 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10424 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10425 ChangePage[x][y] != -1)))
10428 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10430 for (p = 0; p < element_info[element].num_change_pages; p++)
10432 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10434 /* check trigger element for all events where the element that is checked
10435 for changing interacts with a directly adjacent element -- this is
10436 different to element changes that affect other elements to change on the
10437 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10438 boolean check_trigger_element =
10439 (trigger_event == CE_TOUCHING_X ||
10440 trigger_event == CE_HITTING_X ||
10441 trigger_event == CE_HIT_BY_X ||
10442 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10444 if (change->can_change_or_has_action &&
10445 change->has_event[trigger_event] &&
10446 change->trigger_side & trigger_side &&
10447 change->trigger_player & trigger_player &&
10448 (!check_trigger_element ||
10449 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10451 change->actual_trigger_element = trigger_element;
10452 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10453 change->actual_trigger_player_bits = trigger_player;
10454 change->actual_trigger_side = trigger_side;
10455 change->actual_trigger_ce_value = CustomValue[x][y];
10456 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10458 /* special case: trigger element not at (x,y) position for some events */
10459 if (check_trigger_element)
10471 { 0, 0 }, { 0, 0 }, { 0, 0 },
10475 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10476 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10478 change->actual_trigger_ce_value = CustomValue[xx][yy];
10479 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10482 if (change->can_change && !change_done)
10484 ChangeDelay[x][y] = 1;
10485 ChangeEvent[x][y] = trigger_event;
10487 HandleElementChange(x, y, p);
10489 change_done = TRUE;
10491 else if (change->has_action)
10493 ExecuteCustomElementAction(x, y, element, p);
10494 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10499 RECURSION_LOOP_DETECTION_END();
10501 return change_done;
10504 static void PlayPlayerSound(struct PlayerInfo *player)
10506 int jx = player->jx, jy = player->jy;
10507 int sound_element = player->artwork_element;
10508 int last_action = player->last_action_waiting;
10509 int action = player->action_waiting;
10511 if (player->is_waiting)
10513 if (action != last_action)
10514 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10516 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10520 if (action != last_action)
10521 StopSound(element_info[sound_element].sound[last_action]);
10523 if (last_action == ACTION_SLEEPING)
10524 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10528 static void PlayAllPlayersSound()
10532 for (i = 0; i < MAX_PLAYERS; i++)
10533 if (stored_player[i].active)
10534 PlayPlayerSound(&stored_player[i]);
10537 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10539 boolean last_waiting = player->is_waiting;
10540 int move_dir = player->MovDir;
10542 player->dir_waiting = move_dir;
10543 player->last_action_waiting = player->action_waiting;
10547 if (!last_waiting) /* not waiting -> waiting */
10549 player->is_waiting = TRUE;
10551 player->frame_counter_bored =
10553 game.player_boring_delay_fixed +
10554 GetSimpleRandom(game.player_boring_delay_random);
10555 player->frame_counter_sleeping =
10557 game.player_sleeping_delay_fixed +
10558 GetSimpleRandom(game.player_sleeping_delay_random);
10560 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10563 if (game.player_sleeping_delay_fixed +
10564 game.player_sleeping_delay_random > 0 &&
10565 player->anim_delay_counter == 0 &&
10566 player->post_delay_counter == 0 &&
10567 FrameCounter >= player->frame_counter_sleeping)
10568 player->is_sleeping = TRUE;
10569 else if (game.player_boring_delay_fixed +
10570 game.player_boring_delay_random > 0 &&
10571 FrameCounter >= player->frame_counter_bored)
10572 player->is_bored = TRUE;
10574 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10575 player->is_bored ? ACTION_BORING :
10578 if (player->is_sleeping && player->use_murphy)
10580 /* special case for sleeping Murphy when leaning against non-free tile */
10582 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10583 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10584 !IS_MOVING(player->jx - 1, player->jy)))
10585 move_dir = MV_LEFT;
10586 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10587 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10588 !IS_MOVING(player->jx + 1, player->jy)))
10589 move_dir = MV_RIGHT;
10591 player->is_sleeping = FALSE;
10593 player->dir_waiting = move_dir;
10596 if (player->is_sleeping)
10598 if (player->num_special_action_sleeping > 0)
10600 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10602 int last_special_action = player->special_action_sleeping;
10603 int num_special_action = player->num_special_action_sleeping;
10604 int special_action =
10605 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10606 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10607 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10608 last_special_action + 1 : ACTION_SLEEPING);
10609 int special_graphic =
10610 el_act_dir2img(player->artwork_element, special_action, move_dir);
10612 player->anim_delay_counter =
10613 graphic_info[special_graphic].anim_delay_fixed +
10614 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10615 player->post_delay_counter =
10616 graphic_info[special_graphic].post_delay_fixed +
10617 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10619 player->special_action_sleeping = special_action;
10622 if (player->anim_delay_counter > 0)
10624 player->action_waiting = player->special_action_sleeping;
10625 player->anim_delay_counter--;
10627 else if (player->post_delay_counter > 0)
10629 player->post_delay_counter--;
10633 else if (player->is_bored)
10635 if (player->num_special_action_bored > 0)
10637 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10639 int special_action =
10640 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10641 int special_graphic =
10642 el_act_dir2img(player->artwork_element, special_action, move_dir);
10644 player->anim_delay_counter =
10645 graphic_info[special_graphic].anim_delay_fixed +
10646 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10647 player->post_delay_counter =
10648 graphic_info[special_graphic].post_delay_fixed +
10649 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10651 player->special_action_bored = special_action;
10654 if (player->anim_delay_counter > 0)
10656 player->action_waiting = player->special_action_bored;
10657 player->anim_delay_counter--;
10659 else if (player->post_delay_counter > 0)
10661 player->post_delay_counter--;
10666 else if (last_waiting) /* waiting -> not waiting */
10668 player->is_waiting = FALSE;
10669 player->is_bored = FALSE;
10670 player->is_sleeping = FALSE;
10672 player->frame_counter_bored = -1;
10673 player->frame_counter_sleeping = -1;
10675 player->anim_delay_counter = 0;
10676 player->post_delay_counter = 0;
10678 player->dir_waiting = player->MovDir;
10679 player->action_waiting = ACTION_DEFAULT;
10681 player->special_action_bored = ACTION_DEFAULT;
10682 player->special_action_sleeping = ACTION_DEFAULT;
10686 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10688 if ((!player->is_moving && player->was_moving) ||
10689 (player->MovPos == 0 && player->was_moving) ||
10690 (player->is_snapping && !player->was_snapping) ||
10691 (player->is_dropping && !player->was_dropping))
10693 if (!CheckSaveEngineSnapshotToList())
10696 player->was_moving = FALSE;
10697 player->was_snapping = TRUE;
10698 player->was_dropping = TRUE;
10702 if (player->is_moving)
10703 player->was_moving = TRUE;
10705 if (!player->is_snapping)
10706 player->was_snapping = FALSE;
10708 if (!player->is_dropping)
10709 player->was_dropping = FALSE;
10713 static void CheckSingleStepMode(struct PlayerInfo *player)
10715 if (tape.single_step && tape.recording && !tape.pausing)
10717 /* as it is called "single step mode", just return to pause mode when the
10718 player stopped moving after one tile (or never starts moving at all) */
10719 if (!player->is_moving &&
10720 !player->is_pushing &&
10721 !player->is_dropping_pressed)
10723 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10724 SnapField(player, 0, 0); /* stop snapping */
10728 CheckSaveEngineSnapshot(player);
10731 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10733 int left = player_action & JOY_LEFT;
10734 int right = player_action & JOY_RIGHT;
10735 int up = player_action & JOY_UP;
10736 int down = player_action & JOY_DOWN;
10737 int button1 = player_action & JOY_BUTTON_1;
10738 int button2 = player_action & JOY_BUTTON_2;
10739 int dx = (left ? -1 : right ? 1 : 0);
10740 int dy = (up ? -1 : down ? 1 : 0);
10742 if (!player->active || tape.pausing)
10748 SnapField(player, dx, dy);
10752 DropElement(player);
10754 MovePlayer(player, dx, dy);
10757 CheckSingleStepMode(player);
10759 SetPlayerWaiting(player, FALSE);
10761 return player_action;
10765 /* no actions for this player (no input at player's configured device) */
10767 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10768 SnapField(player, 0, 0);
10769 CheckGravityMovementWhenNotMoving(player);
10771 if (player->MovPos == 0)
10772 SetPlayerWaiting(player, TRUE);
10774 if (player->MovPos == 0) /* needed for tape.playing */
10775 player->is_moving = FALSE;
10777 player->is_dropping = FALSE;
10778 player->is_dropping_pressed = FALSE;
10779 player->drop_pressed_delay = 0;
10781 CheckSingleStepMode(player);
10787 static void CheckLevelTime()
10791 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10792 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10794 if (level.native_em_level->lev->home == 0) /* all players at home */
10796 PlayerWins(local_player);
10798 AllPlayersGone = TRUE;
10800 level.native_em_level->lev->home = -1;
10803 if (level.native_em_level->ply[0]->alive == 0 &&
10804 level.native_em_level->ply[1]->alive == 0 &&
10805 level.native_em_level->ply[2]->alive == 0 &&
10806 level.native_em_level->ply[3]->alive == 0) /* all dead */
10807 AllPlayersGone = TRUE;
10809 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10811 if (game_sp.LevelSolved &&
10812 !game_sp.GameOver) /* game won */
10814 PlayerWins(local_player);
10816 game_sp.GameOver = TRUE;
10818 AllPlayersGone = TRUE;
10821 if (game_sp.GameOver) /* game lost */
10822 AllPlayersGone = TRUE;
10825 if (TimeFrames >= FRAMES_PER_SECOND)
10830 for (i = 0; i < MAX_PLAYERS; i++)
10832 struct PlayerInfo *player = &stored_player[i];
10834 if (SHIELD_ON(player))
10836 player->shield_normal_time_left--;
10838 if (player->shield_deadly_time_left > 0)
10839 player->shield_deadly_time_left--;
10843 if (!local_player->LevelSolved && !level.use_step_counter)
10851 if (TimeLeft <= 10 && setup.time_limit)
10852 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10854 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10855 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10857 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10859 if (!TimeLeft && setup.time_limit)
10861 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10862 level.native_em_level->lev->killed_out_of_time = TRUE;
10864 for (i = 0; i < MAX_PLAYERS; i++)
10865 KillPlayer(&stored_player[i]);
10868 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10870 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10873 level.native_em_level->lev->time =
10874 (game.no_time_limit ? TimePlayed : TimeLeft);
10877 if (tape.recording || tape.playing)
10878 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10881 if (tape.recording || tape.playing)
10882 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10884 UpdateAndDisplayGameControlValues();
10887 void AdvanceFrameAndPlayerCounters(int player_nr)
10891 /* advance frame counters (global frame counter and time frame counter) */
10895 /* advance player counters (counters for move delay, move animation etc.) */
10896 for (i = 0; i < MAX_PLAYERS; i++)
10898 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10899 int move_delay_value = stored_player[i].move_delay_value;
10900 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10902 if (!advance_player_counters) /* not all players may be affected */
10905 if (move_frames == 0) /* less than one move per game frame */
10907 int stepsize = TILEX / move_delay_value;
10908 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10909 int count = (stored_player[i].is_moving ?
10910 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10912 if (count % delay == 0)
10916 stored_player[i].Frame += move_frames;
10918 if (stored_player[i].MovPos != 0)
10919 stored_player[i].StepFrame += move_frames;
10921 if (stored_player[i].move_delay > 0)
10922 stored_player[i].move_delay--;
10924 /* due to bugs in previous versions, counter must count up, not down */
10925 if (stored_player[i].push_delay != -1)
10926 stored_player[i].push_delay++;
10928 if (stored_player[i].drop_delay > 0)
10929 stored_player[i].drop_delay--;
10931 if (stored_player[i].is_dropping_pressed)
10932 stored_player[i].drop_pressed_delay++;
10936 void StartGameActions(boolean init_network_game, boolean record_tape,
10939 unsigned int new_random_seed = InitRND(random_seed);
10942 TapeStartRecording(new_random_seed);
10944 #if defined(NETWORK_AVALIABLE)
10945 if (init_network_game)
10947 SendToServer_StartPlaying();
10956 void GameActionsExt()
10959 static unsigned int game_frame_delay = 0;
10961 unsigned int game_frame_delay_value;
10962 byte *recorded_player_action;
10963 byte summarized_player_action = 0;
10964 byte tape_action[MAX_PLAYERS];
10967 /* detect endless loops, caused by custom element programming */
10968 if (recursion_loop_detected && recursion_loop_depth == 0)
10970 char *message = getStringCat3("Internal Error! Element ",
10971 EL_NAME(recursion_loop_element),
10972 " caused endless loop! Quit the game?");
10974 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10975 EL_NAME(recursion_loop_element));
10977 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10979 recursion_loop_detected = FALSE; /* if game should be continued */
10986 if (game.restart_level)
10987 StartGameActions(options.network, setup.autorecord, level.random_seed);
10989 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10990 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10992 if (level.native_em_level->lev->home == 0) /* all players at home */
10994 PlayerWins(local_player);
10996 AllPlayersGone = TRUE;
10998 level.native_em_level->lev->home = -1;
11001 if (level.native_em_level->ply[0]->alive == 0 &&
11002 level.native_em_level->ply[1]->alive == 0 &&
11003 level.native_em_level->ply[2]->alive == 0 &&
11004 level.native_em_level->ply[3]->alive == 0) /* all dead */
11005 AllPlayersGone = TRUE;
11007 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11009 if (game_sp.LevelSolved &&
11010 !game_sp.GameOver) /* game won */
11012 PlayerWins(local_player);
11014 game_sp.GameOver = TRUE;
11016 AllPlayersGone = TRUE;
11019 if (game_sp.GameOver) /* game lost */
11020 AllPlayersGone = TRUE;
11023 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11026 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11029 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11032 game_frame_delay_value =
11033 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11035 if (tape.playing && tape.warp_forward && !tape.pausing)
11036 game_frame_delay_value = 0;
11038 SetVideoFrameDelay(game_frame_delay_value);
11042 /* ---------- main game synchronization point ---------- */
11044 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11046 printf("::: skip == %d\n", skip);
11049 /* ---------- main game synchronization point ---------- */
11051 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11055 if (network_playing && !network_player_action_received)
11057 /* try to get network player actions in time */
11059 #if defined(NETWORK_AVALIABLE)
11060 /* last chance to get network player actions without main loop delay */
11061 HandleNetworking();
11064 /* game was quit by network peer */
11065 if (game_status != GAME_MODE_PLAYING)
11068 if (!network_player_action_received)
11069 return; /* failed to get network player actions in time */
11071 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11077 /* at this point we know that we really continue executing the game */
11079 network_player_action_received = FALSE;
11081 /* when playing tape, read previously recorded player input from tape data */
11082 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11084 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11088 if (tape.set_centered_player)
11090 game.centered_player_nr_next = tape.centered_player_nr_next;
11091 game.set_centered_player = TRUE;
11094 for (i = 0; i < MAX_PLAYERS; i++)
11096 summarized_player_action |= stored_player[i].action;
11098 if (!network_playing && (game.team_mode || tape.playing))
11099 stored_player[i].effective_action = stored_player[i].action;
11102 #if defined(NETWORK_AVALIABLE)
11103 if (network_playing)
11104 SendToServer_MovePlayer(summarized_player_action);
11107 // summarize all actions at local players mapped input device position
11108 // (this allows using different input devices in single player mode)
11109 if (!options.network && !game.team_mode)
11110 stored_player[map_player_action[local_player->index_nr]].effective_action =
11111 summarized_player_action;
11113 if (tape.recording &&
11115 setup.input_on_focus &&
11116 game.centered_player_nr != -1)
11118 for (i = 0; i < MAX_PLAYERS; i++)
11119 stored_player[i].effective_action =
11120 (i == game.centered_player_nr ? summarized_player_action : 0);
11123 if (recorded_player_action != NULL)
11124 for (i = 0; i < MAX_PLAYERS; i++)
11125 stored_player[i].effective_action = recorded_player_action[i];
11127 for (i = 0; i < MAX_PLAYERS; i++)
11129 tape_action[i] = stored_player[i].effective_action;
11131 /* (this may happen in the RND game engine if a player was not present on
11132 the playfield on level start, but appeared later from a custom element */
11133 if (setup.team_mode &&
11136 !tape.player_participates[i])
11137 tape.player_participates[i] = TRUE;
11140 /* only record actions from input devices, but not programmed actions */
11141 if (tape.recording)
11142 TapeRecordAction(tape_action);
11144 #if USE_NEW_PLAYER_ASSIGNMENTS
11145 // !!! also map player actions in single player mode !!!
11146 // if (game.team_mode)
11149 byte mapped_action[MAX_PLAYERS];
11151 #if DEBUG_PLAYER_ACTIONS
11153 for (i = 0; i < MAX_PLAYERS; i++)
11154 printf(" %d, ", stored_player[i].effective_action);
11157 for (i = 0; i < MAX_PLAYERS; i++)
11158 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11160 for (i = 0; i < MAX_PLAYERS; i++)
11161 stored_player[i].effective_action = mapped_action[i];
11163 #if DEBUG_PLAYER_ACTIONS
11165 for (i = 0; i < MAX_PLAYERS; i++)
11166 printf(" %d, ", stored_player[i].effective_action);
11170 #if DEBUG_PLAYER_ACTIONS
11174 for (i = 0; i < MAX_PLAYERS; i++)
11175 printf(" %d, ", stored_player[i].effective_action);
11181 for (i = 0; i < MAX_PLAYERS; i++)
11183 // allow engine snapshot in case of changed movement attempt
11184 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11185 (stored_player[i].effective_action & KEY_MOTION))
11186 game.snapshot.changed_action = TRUE;
11188 // allow engine snapshot in case of snapping/dropping attempt
11189 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11190 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11191 game.snapshot.changed_action = TRUE;
11193 game.snapshot.last_action[i] = stored_player[i].effective_action;
11196 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11198 GameActions_EM_Main();
11200 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11202 GameActions_SP_Main();
11206 GameActions_RND_Main();
11209 BlitScreenToBitmap(backbuffer);
11213 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11215 if (global.show_frames_per_second)
11217 static unsigned int fps_counter = 0;
11218 static int fps_frames = 0;
11219 unsigned int fps_delay_ms = Counter() - fps_counter;
11223 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11225 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11228 fps_counter = Counter();
11230 /* always draw FPS to screen after FPS value was updated */
11231 redraw_mask |= REDRAW_FPS;
11234 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11235 if (GetDrawDeactivationMask() == REDRAW_NONE)
11236 redraw_mask |= REDRAW_FPS;
11240 static void GameActions_CheckSaveEngineSnapshot()
11242 if (!game.snapshot.save_snapshot)
11245 // clear flag for saving snapshot _before_ saving snapshot
11246 game.snapshot.save_snapshot = FALSE;
11248 SaveEngineSnapshotToList();
11255 GameActions_CheckSaveEngineSnapshot();
11258 void GameActions_EM_Main()
11260 byte effective_action[MAX_PLAYERS];
11261 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11264 for (i = 0; i < MAX_PLAYERS; i++)
11265 effective_action[i] = stored_player[i].effective_action;
11267 GameActions_EM(effective_action, warp_mode);
11270 void GameActions_SP_Main()
11272 byte effective_action[MAX_PLAYERS];
11273 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11276 for (i = 0; i < MAX_PLAYERS; i++)
11277 effective_action[i] = stored_player[i].effective_action;
11279 GameActions_SP(effective_action, warp_mode);
11281 for (i = 0; i < MAX_PLAYERS; i++)
11283 if (stored_player[i].force_dropping)
11284 stored_player[i].action |= KEY_BUTTON_DROP;
11286 stored_player[i].force_dropping = FALSE;
11290 void GameActions_RND_Main()
11295 void GameActions_RND()
11297 int magic_wall_x = 0, magic_wall_y = 0;
11298 int i, x, y, element, graphic, last_gfx_frame;
11300 InitPlayfieldScanModeVars();
11302 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11304 SCAN_PLAYFIELD(x, y)
11306 ChangeCount[x][y] = 0;
11307 ChangeEvent[x][y] = -1;
11311 if (game.set_centered_player)
11313 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11315 /* switching to "all players" only possible if all players fit to screen */
11316 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11318 game.centered_player_nr_next = game.centered_player_nr;
11319 game.set_centered_player = FALSE;
11322 /* do not switch focus to non-existing (or non-active) player */
11323 if (game.centered_player_nr_next >= 0 &&
11324 !stored_player[game.centered_player_nr_next].active)
11326 game.centered_player_nr_next = game.centered_player_nr;
11327 game.set_centered_player = FALSE;
11331 if (game.set_centered_player &&
11332 ScreenMovPos == 0) /* screen currently aligned at tile position */
11336 if (game.centered_player_nr_next == -1)
11338 setScreenCenteredToAllPlayers(&sx, &sy);
11342 sx = stored_player[game.centered_player_nr_next].jx;
11343 sy = stored_player[game.centered_player_nr_next].jy;
11346 game.centered_player_nr = game.centered_player_nr_next;
11347 game.set_centered_player = FALSE;
11349 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11350 DrawGameDoorValues();
11353 for (i = 0; i < MAX_PLAYERS; i++)
11355 int actual_player_action = stored_player[i].effective_action;
11358 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11359 - rnd_equinox_tetrachloride 048
11360 - rnd_equinox_tetrachloride_ii 096
11361 - rnd_emanuel_schmieg 002
11362 - doctor_sloan_ww 001, 020
11364 if (stored_player[i].MovPos == 0)
11365 CheckGravityMovement(&stored_player[i]);
11368 /* overwrite programmed action with tape action */
11369 if (stored_player[i].programmed_action)
11370 actual_player_action = stored_player[i].programmed_action;
11372 PlayerActions(&stored_player[i], actual_player_action);
11374 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11377 ScrollScreen(NULL, SCROLL_GO_ON);
11379 /* for backwards compatibility, the following code emulates a fixed bug that
11380 occured when pushing elements (causing elements that just made their last
11381 pushing step to already (if possible) make their first falling step in the
11382 same game frame, which is bad); this code is also needed to use the famous
11383 "spring push bug" which is used in older levels and might be wanted to be
11384 used also in newer levels, but in this case the buggy pushing code is only
11385 affecting the "spring" element and no other elements */
11387 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11389 for (i = 0; i < MAX_PLAYERS; i++)
11391 struct PlayerInfo *player = &stored_player[i];
11392 int x = player->jx;
11393 int y = player->jy;
11395 if (player->active && player->is_pushing && player->is_moving &&
11397 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11398 Feld[x][y] == EL_SPRING))
11400 ContinueMoving(x, y);
11402 /* continue moving after pushing (this is actually a bug) */
11403 if (!IS_MOVING(x, y))
11404 Stop[x][y] = FALSE;
11409 SCAN_PLAYFIELD(x, y)
11411 ChangeCount[x][y] = 0;
11412 ChangeEvent[x][y] = -1;
11414 /* this must be handled before main playfield loop */
11415 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11418 if (MovDelay[x][y] <= 0)
11422 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11425 if (MovDelay[x][y] <= 0)
11428 TEST_DrawLevelField(x, y);
11430 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11435 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11437 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11438 printf("GameActions(): This should never happen!\n");
11440 ChangePage[x][y] = -1;
11444 Stop[x][y] = FALSE;
11445 if (WasJustMoving[x][y] > 0)
11446 WasJustMoving[x][y]--;
11447 if (WasJustFalling[x][y] > 0)
11448 WasJustFalling[x][y]--;
11449 if (CheckCollision[x][y] > 0)
11450 CheckCollision[x][y]--;
11451 if (CheckImpact[x][y] > 0)
11452 CheckImpact[x][y]--;
11456 /* reset finished pushing action (not done in ContinueMoving() to allow
11457 continuous pushing animation for elements with zero push delay) */
11458 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11460 ResetGfxAnimation(x, y);
11461 TEST_DrawLevelField(x, y);
11465 if (IS_BLOCKED(x, y))
11469 Blocked2Moving(x, y, &oldx, &oldy);
11470 if (!IS_MOVING(oldx, oldy))
11472 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11473 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11474 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11475 printf("GameActions(): This should never happen!\n");
11481 SCAN_PLAYFIELD(x, y)
11483 element = Feld[x][y];
11484 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11485 last_gfx_frame = GfxFrame[x][y];
11487 ResetGfxFrame(x, y);
11489 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11490 DrawLevelGraphicAnimation(x, y, graphic);
11492 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11493 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11494 ResetRandomAnimationValue(x, y);
11496 SetRandomAnimationValue(x, y);
11498 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11500 if (IS_INACTIVE(element))
11502 if (IS_ANIMATED(graphic))
11503 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11508 /* this may take place after moving, so 'element' may have changed */
11509 if (IS_CHANGING(x, y) &&
11510 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11512 int page = element_info[element].event_page_nr[CE_DELAY];
11514 HandleElementChange(x, y, page);
11516 element = Feld[x][y];
11517 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11520 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11524 element = Feld[x][y];
11525 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11527 if (IS_ANIMATED(graphic) &&
11528 !IS_MOVING(x, y) &&
11530 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11532 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11533 TEST_DrawTwinkleOnField(x, y);
11535 else if (element == EL_ACID)
11538 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11540 else if ((element == EL_EXIT_OPEN ||
11541 element == EL_EM_EXIT_OPEN ||
11542 element == EL_SP_EXIT_OPEN ||
11543 element == EL_STEEL_EXIT_OPEN ||
11544 element == EL_EM_STEEL_EXIT_OPEN ||
11545 element == EL_SP_TERMINAL ||
11546 element == EL_SP_TERMINAL_ACTIVE ||
11547 element == EL_EXTRA_TIME ||
11548 element == EL_SHIELD_NORMAL ||
11549 element == EL_SHIELD_DEADLY) &&
11550 IS_ANIMATED(graphic))
11551 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11552 else if (IS_MOVING(x, y))
11553 ContinueMoving(x, y);
11554 else if (IS_ACTIVE_BOMB(element))
11555 CheckDynamite(x, y);
11556 else if (element == EL_AMOEBA_GROWING)
11557 AmoebeWaechst(x, y);
11558 else if (element == EL_AMOEBA_SHRINKING)
11559 AmoebaDisappearing(x, y);
11561 #if !USE_NEW_AMOEBA_CODE
11562 else if (IS_AMOEBALIVE(element))
11563 AmoebeAbleger(x, y);
11566 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11568 else if (element == EL_EXIT_CLOSED)
11570 else if (element == EL_EM_EXIT_CLOSED)
11572 else if (element == EL_STEEL_EXIT_CLOSED)
11573 CheckExitSteel(x, y);
11574 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11575 CheckExitSteelEM(x, y);
11576 else if (element == EL_SP_EXIT_CLOSED)
11578 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11579 element == EL_EXPANDABLE_STEELWALL_GROWING)
11580 MauerWaechst(x, y);
11581 else if (element == EL_EXPANDABLE_WALL ||
11582 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11583 element == EL_EXPANDABLE_WALL_VERTICAL ||
11584 element == EL_EXPANDABLE_WALL_ANY ||
11585 element == EL_BD_EXPANDABLE_WALL)
11586 MauerAbleger(x, y);
11587 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11588 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11589 element == EL_EXPANDABLE_STEELWALL_ANY)
11590 MauerAblegerStahl(x, y);
11591 else if (element == EL_FLAMES)
11592 CheckForDragon(x, y);
11593 else if (element == EL_EXPLOSION)
11594 ; /* drawing of correct explosion animation is handled separately */
11595 else if (element == EL_ELEMENT_SNAPPING ||
11596 element == EL_DIAGONAL_SHRINKING ||
11597 element == EL_DIAGONAL_GROWING)
11599 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11601 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11603 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11604 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11606 if (IS_BELT_ACTIVE(element))
11607 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11609 if (game.magic_wall_active)
11611 int jx = local_player->jx, jy = local_player->jy;
11613 /* play the element sound at the position nearest to the player */
11614 if ((element == EL_MAGIC_WALL_FULL ||
11615 element == EL_MAGIC_WALL_ACTIVE ||
11616 element == EL_MAGIC_WALL_EMPTYING ||
11617 element == EL_BD_MAGIC_WALL_FULL ||
11618 element == EL_BD_MAGIC_WALL_ACTIVE ||
11619 element == EL_BD_MAGIC_WALL_EMPTYING ||
11620 element == EL_DC_MAGIC_WALL_FULL ||
11621 element == EL_DC_MAGIC_WALL_ACTIVE ||
11622 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11623 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11631 #if USE_NEW_AMOEBA_CODE
11632 /* new experimental amoeba growth stuff */
11633 if (!(FrameCounter % 8))
11635 static unsigned int random = 1684108901;
11637 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11639 x = RND(lev_fieldx);
11640 y = RND(lev_fieldy);
11641 element = Feld[x][y];
11643 if (!IS_PLAYER(x,y) &&
11644 (element == EL_EMPTY ||
11645 CAN_GROW_INTO(element) ||
11646 element == EL_QUICKSAND_EMPTY ||
11647 element == EL_QUICKSAND_FAST_EMPTY ||
11648 element == EL_ACID_SPLASH_LEFT ||
11649 element == EL_ACID_SPLASH_RIGHT))
11651 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11652 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11653 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11654 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11655 Feld[x][y] = EL_AMOEBA_DROP;
11658 random = random * 129 + 1;
11663 game.explosions_delayed = FALSE;
11665 SCAN_PLAYFIELD(x, y)
11667 element = Feld[x][y];
11669 if (ExplodeField[x][y])
11670 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11671 else if (element == EL_EXPLOSION)
11672 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11674 ExplodeField[x][y] = EX_TYPE_NONE;
11677 game.explosions_delayed = TRUE;
11679 if (game.magic_wall_active)
11681 if (!(game.magic_wall_time_left % 4))
11683 int element = Feld[magic_wall_x][magic_wall_y];
11685 if (element == EL_BD_MAGIC_WALL_FULL ||
11686 element == EL_BD_MAGIC_WALL_ACTIVE ||
11687 element == EL_BD_MAGIC_WALL_EMPTYING)
11688 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11689 else if (element == EL_DC_MAGIC_WALL_FULL ||
11690 element == EL_DC_MAGIC_WALL_ACTIVE ||
11691 element == EL_DC_MAGIC_WALL_EMPTYING)
11692 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11694 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11697 if (game.magic_wall_time_left > 0)
11699 game.magic_wall_time_left--;
11701 if (!game.magic_wall_time_left)
11703 SCAN_PLAYFIELD(x, y)
11705 element = Feld[x][y];
11707 if (element == EL_MAGIC_WALL_ACTIVE ||
11708 element == EL_MAGIC_WALL_FULL)
11710 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11711 TEST_DrawLevelField(x, y);
11713 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11714 element == EL_BD_MAGIC_WALL_FULL)
11716 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11717 TEST_DrawLevelField(x, y);
11719 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11720 element == EL_DC_MAGIC_WALL_FULL)
11722 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11723 TEST_DrawLevelField(x, y);
11727 game.magic_wall_active = FALSE;
11732 if (game.light_time_left > 0)
11734 game.light_time_left--;
11736 if (game.light_time_left == 0)
11737 RedrawAllLightSwitchesAndInvisibleElements();
11740 if (game.timegate_time_left > 0)
11742 game.timegate_time_left--;
11744 if (game.timegate_time_left == 0)
11745 CloseAllOpenTimegates();
11748 if (game.lenses_time_left > 0)
11750 game.lenses_time_left--;
11752 if (game.lenses_time_left == 0)
11753 RedrawAllInvisibleElementsForLenses();
11756 if (game.magnify_time_left > 0)
11758 game.magnify_time_left--;
11760 if (game.magnify_time_left == 0)
11761 RedrawAllInvisibleElementsForMagnifier();
11764 for (i = 0; i < MAX_PLAYERS; i++)
11766 struct PlayerInfo *player = &stored_player[i];
11768 if (SHIELD_ON(player))
11770 if (player->shield_deadly_time_left)
11771 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11772 else if (player->shield_normal_time_left)
11773 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11777 #if USE_DELAYED_GFX_REDRAW
11778 SCAN_PLAYFIELD(x, y)
11780 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11782 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11783 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11785 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11786 DrawLevelField(x, y);
11788 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11789 DrawLevelFieldCrumbled(x, y);
11791 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11792 DrawLevelFieldCrumbledNeighbours(x, y);
11794 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11795 DrawTwinkleOnField(x, y);
11798 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11803 PlayAllPlayersSound();
11805 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11807 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11809 local_player->show_envelope = 0;
11812 /* use random number generator in every frame to make it less predictable */
11813 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11817 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11819 int min_x = x, min_y = y, max_x = x, max_y = y;
11822 for (i = 0; i < MAX_PLAYERS; i++)
11824 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11826 if (!stored_player[i].active || &stored_player[i] == player)
11829 min_x = MIN(min_x, jx);
11830 min_y = MIN(min_y, jy);
11831 max_x = MAX(max_x, jx);
11832 max_y = MAX(max_y, jy);
11835 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11838 static boolean AllPlayersInVisibleScreen()
11842 for (i = 0; i < MAX_PLAYERS; i++)
11844 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11846 if (!stored_player[i].active)
11849 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11856 void ScrollLevel(int dx, int dy)
11858 int scroll_offset = 2 * TILEX_VAR;
11861 BlitBitmap(drawto_field, drawto_field,
11862 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11863 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11864 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11865 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11866 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11867 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11871 x = (dx == 1 ? BX1 : BX2);
11872 for (y = BY1; y <= BY2; y++)
11873 DrawScreenField(x, y);
11878 y = (dy == 1 ? BY1 : BY2);
11879 for (x = BX1; x <= BX2; x++)
11880 DrawScreenField(x, y);
11883 redraw_mask |= REDRAW_FIELD;
11886 static boolean canFallDown(struct PlayerInfo *player)
11888 int jx = player->jx, jy = player->jy;
11890 return (IN_LEV_FIELD(jx, jy + 1) &&
11891 (IS_FREE(jx, jy + 1) ||
11892 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11893 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11894 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11897 static boolean canPassField(int x, int y, int move_dir)
11899 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11900 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11901 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11902 int nextx = x + dx;
11903 int nexty = y + dy;
11904 int element = Feld[x][y];
11906 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11907 !CAN_MOVE(element) &&
11908 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11909 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11910 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11913 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11915 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11916 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11917 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11921 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11922 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11923 (IS_DIGGABLE(Feld[newx][newy]) ||
11924 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11925 canPassField(newx, newy, move_dir)));
11928 static void CheckGravityMovement(struct PlayerInfo *player)
11930 if (player->gravity && !player->programmed_action)
11932 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11933 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11934 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11935 int jx = player->jx, jy = player->jy;
11936 boolean player_is_moving_to_valid_field =
11937 (!player_is_snapping &&
11938 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11939 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11940 boolean player_can_fall_down = canFallDown(player);
11942 if (player_can_fall_down &&
11943 !player_is_moving_to_valid_field)
11944 player->programmed_action = MV_DOWN;
11948 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11950 return CheckGravityMovement(player);
11952 if (player->gravity && !player->programmed_action)
11954 int jx = player->jx, jy = player->jy;
11955 boolean field_under_player_is_free =
11956 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11957 boolean player_is_standing_on_valid_field =
11958 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11959 (IS_WALKABLE(Feld[jx][jy]) &&
11960 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11962 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11963 player->programmed_action = MV_DOWN;
11968 MovePlayerOneStep()
11969 -----------------------------------------------------------------------------
11970 dx, dy: direction (non-diagonal) to try to move the player to
11971 real_dx, real_dy: direction as read from input device (can be diagonal)
11974 boolean MovePlayerOneStep(struct PlayerInfo *player,
11975 int dx, int dy, int real_dx, int real_dy)
11977 int jx = player->jx, jy = player->jy;
11978 int new_jx = jx + dx, new_jy = jy + dy;
11980 boolean player_can_move = !player->cannot_move;
11982 if (!player->active || (!dx && !dy))
11983 return MP_NO_ACTION;
11985 player->MovDir = (dx < 0 ? MV_LEFT :
11986 dx > 0 ? MV_RIGHT :
11988 dy > 0 ? MV_DOWN : MV_NONE);
11990 if (!IN_LEV_FIELD(new_jx, new_jy))
11991 return MP_NO_ACTION;
11993 if (!player_can_move)
11995 if (player->MovPos == 0)
11997 player->is_moving = FALSE;
11998 player->is_digging = FALSE;
11999 player->is_collecting = FALSE;
12000 player->is_snapping = FALSE;
12001 player->is_pushing = FALSE;
12005 if (!options.network && game.centered_player_nr == -1 &&
12006 !AllPlayersInSight(player, new_jx, new_jy))
12007 return MP_NO_ACTION;
12009 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12010 if (can_move != MP_MOVING)
12013 /* check if DigField() has caused relocation of the player */
12014 if (player->jx != jx || player->jy != jy)
12015 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12017 StorePlayer[jx][jy] = 0;
12018 player->last_jx = jx;
12019 player->last_jy = jy;
12020 player->jx = new_jx;
12021 player->jy = new_jy;
12022 StorePlayer[new_jx][new_jy] = player->element_nr;
12024 if (player->move_delay_value_next != -1)
12026 player->move_delay_value = player->move_delay_value_next;
12027 player->move_delay_value_next = -1;
12031 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12033 player->step_counter++;
12035 PlayerVisit[jx][jy] = FrameCounter;
12037 player->is_moving = TRUE;
12040 /* should better be called in MovePlayer(), but this breaks some tapes */
12041 ScrollPlayer(player, SCROLL_INIT);
12047 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12049 int jx = player->jx, jy = player->jy;
12050 int old_jx = jx, old_jy = jy;
12051 int moved = MP_NO_ACTION;
12053 if (!player->active)
12058 if (player->MovPos == 0)
12060 player->is_moving = FALSE;
12061 player->is_digging = FALSE;
12062 player->is_collecting = FALSE;
12063 player->is_snapping = FALSE;
12064 player->is_pushing = FALSE;
12070 if (player->move_delay > 0)
12073 player->move_delay = -1; /* set to "uninitialized" value */
12075 /* store if player is automatically moved to next field */
12076 player->is_auto_moving = (player->programmed_action != MV_NONE);
12078 /* remove the last programmed player action */
12079 player->programmed_action = 0;
12081 if (player->MovPos)
12083 /* should only happen if pre-1.2 tape recordings are played */
12084 /* this is only for backward compatibility */
12086 int original_move_delay_value = player->move_delay_value;
12089 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12093 /* scroll remaining steps with finest movement resolution */
12094 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12096 while (player->MovPos)
12098 ScrollPlayer(player, SCROLL_GO_ON);
12099 ScrollScreen(NULL, SCROLL_GO_ON);
12101 AdvanceFrameAndPlayerCounters(player->index_nr);
12104 BackToFront_WithFrameDelay(0);
12107 player->move_delay_value = original_move_delay_value;
12110 player->is_active = FALSE;
12112 if (player->last_move_dir & MV_HORIZONTAL)
12114 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12115 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12119 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12120 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12123 if (!moved && !player->is_active)
12125 player->is_moving = FALSE;
12126 player->is_digging = FALSE;
12127 player->is_collecting = FALSE;
12128 player->is_snapping = FALSE;
12129 player->is_pushing = FALSE;
12135 if (moved & MP_MOVING && !ScreenMovPos &&
12136 (player->index_nr == game.centered_player_nr ||
12137 game.centered_player_nr == -1))
12139 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12140 int offset = game.scroll_delay_value;
12142 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12144 /* actual player has left the screen -- scroll in that direction */
12145 if (jx != old_jx) /* player has moved horizontally */
12146 scroll_x += (jx - old_jx);
12147 else /* player has moved vertically */
12148 scroll_y += (jy - old_jy);
12152 if (jx != old_jx) /* player has moved horizontally */
12154 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12155 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12156 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12158 /* don't scroll over playfield boundaries */
12159 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12160 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12162 /* don't scroll more than one field at a time */
12163 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12165 /* don't scroll against the player's moving direction */
12166 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12167 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12168 scroll_x = old_scroll_x;
12170 else /* player has moved vertically */
12172 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12173 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12174 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12176 /* don't scroll over playfield boundaries */
12177 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12178 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12180 /* don't scroll more than one field at a time */
12181 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12183 /* don't scroll against the player's moving direction */
12184 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12185 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12186 scroll_y = old_scroll_y;
12190 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12192 if (!options.network && game.centered_player_nr == -1 &&
12193 !AllPlayersInVisibleScreen())
12195 scroll_x = old_scroll_x;
12196 scroll_y = old_scroll_y;
12200 ScrollScreen(player, SCROLL_INIT);
12201 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12206 player->StepFrame = 0;
12208 if (moved & MP_MOVING)
12210 if (old_jx != jx && old_jy == jy)
12211 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12212 else if (old_jx == jx && old_jy != jy)
12213 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12215 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12217 player->last_move_dir = player->MovDir;
12218 player->is_moving = TRUE;
12219 player->is_snapping = FALSE;
12220 player->is_switching = FALSE;
12221 player->is_dropping = FALSE;
12222 player->is_dropping_pressed = FALSE;
12223 player->drop_pressed_delay = 0;
12226 /* should better be called here than above, but this breaks some tapes */
12227 ScrollPlayer(player, SCROLL_INIT);
12232 CheckGravityMovementWhenNotMoving(player);
12234 player->is_moving = FALSE;
12236 /* at this point, the player is allowed to move, but cannot move right now
12237 (e.g. because of something blocking the way) -- ensure that the player
12238 is also allowed to move in the next frame (in old versions before 3.1.1,
12239 the player was forced to wait again for eight frames before next try) */
12241 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12242 player->move_delay = 0; /* allow direct movement in the next frame */
12245 if (player->move_delay == -1) /* not yet initialized by DigField() */
12246 player->move_delay = player->move_delay_value;
12248 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12250 TestIfPlayerTouchesBadThing(jx, jy);
12251 TestIfPlayerTouchesCustomElement(jx, jy);
12254 if (!player->active)
12255 RemovePlayer(player);
12260 void ScrollPlayer(struct PlayerInfo *player, int mode)
12262 int jx = player->jx, jy = player->jy;
12263 int last_jx = player->last_jx, last_jy = player->last_jy;
12264 int move_stepsize = TILEX / player->move_delay_value;
12266 if (!player->active)
12269 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12272 if (mode == SCROLL_INIT)
12274 player->actual_frame_counter = FrameCounter;
12275 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12277 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12278 Feld[last_jx][last_jy] == EL_EMPTY)
12280 int last_field_block_delay = 0; /* start with no blocking at all */
12281 int block_delay_adjustment = player->block_delay_adjustment;
12283 /* if player blocks last field, add delay for exactly one move */
12284 if (player->block_last_field)
12286 last_field_block_delay += player->move_delay_value;
12288 /* when blocking enabled, prevent moving up despite gravity */
12289 if (player->gravity && player->MovDir == MV_UP)
12290 block_delay_adjustment = -1;
12293 /* add block delay adjustment (also possible when not blocking) */
12294 last_field_block_delay += block_delay_adjustment;
12296 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12297 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12300 if (player->MovPos != 0) /* player has not yet reached destination */
12303 else if (!FrameReached(&player->actual_frame_counter, 1))
12306 if (player->MovPos != 0)
12308 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12309 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12311 /* before DrawPlayer() to draw correct player graphic for this case */
12312 if (player->MovPos == 0)
12313 CheckGravityMovement(player);
12316 if (player->MovPos == 0) /* player reached destination field */
12318 if (player->move_delay_reset_counter > 0)
12320 player->move_delay_reset_counter--;
12322 if (player->move_delay_reset_counter == 0)
12324 /* continue with normal speed after quickly moving through gate */
12325 HALVE_PLAYER_SPEED(player);
12327 /* be able to make the next move without delay */
12328 player->move_delay = 0;
12332 player->last_jx = jx;
12333 player->last_jy = jy;
12335 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12336 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12337 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12338 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12339 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12340 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12341 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12342 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12344 DrawPlayer(player); /* needed here only to cleanup last field */
12345 RemovePlayer(player);
12347 if (local_player->friends_still_needed == 0 ||
12348 IS_SP_ELEMENT(Feld[jx][jy]))
12349 PlayerWins(player);
12352 /* this breaks one level: "machine", level 000 */
12354 int move_direction = player->MovDir;
12355 int enter_side = MV_DIR_OPPOSITE(move_direction);
12356 int leave_side = move_direction;
12357 int old_jx = last_jx;
12358 int old_jy = last_jy;
12359 int old_element = Feld[old_jx][old_jy];
12360 int new_element = Feld[jx][jy];
12362 if (IS_CUSTOM_ELEMENT(old_element))
12363 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12365 player->index_bit, leave_side);
12367 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12368 CE_PLAYER_LEAVES_X,
12369 player->index_bit, leave_side);
12371 if (IS_CUSTOM_ELEMENT(new_element))
12372 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12373 player->index_bit, enter_side);
12375 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12376 CE_PLAYER_ENTERS_X,
12377 player->index_bit, enter_side);
12379 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12380 CE_MOVE_OF_X, move_direction);
12383 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12385 TestIfPlayerTouchesBadThing(jx, jy);
12386 TestIfPlayerTouchesCustomElement(jx, jy);
12388 /* needed because pushed element has not yet reached its destination,
12389 so it would trigger a change event at its previous field location */
12390 if (!player->is_pushing)
12391 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12393 if (!player->active)
12394 RemovePlayer(player);
12397 if (!local_player->LevelSolved && level.use_step_counter)
12407 if (TimeLeft <= 10 && setup.time_limit)
12408 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12410 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12412 DisplayGameControlValues();
12414 if (!TimeLeft && setup.time_limit)
12415 for (i = 0; i < MAX_PLAYERS; i++)
12416 KillPlayer(&stored_player[i]);
12418 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12420 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12422 DisplayGameControlValues();
12426 if (tape.single_step && tape.recording && !tape.pausing &&
12427 !player->programmed_action)
12428 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12430 if (!player->programmed_action)
12431 CheckSaveEngineSnapshot(player);
12435 void ScrollScreen(struct PlayerInfo *player, int mode)
12437 static unsigned int screen_frame_counter = 0;
12439 if (mode == SCROLL_INIT)
12441 /* set scrolling step size according to actual player's moving speed */
12442 ScrollStepSize = TILEX / player->move_delay_value;
12444 screen_frame_counter = FrameCounter;
12445 ScreenMovDir = player->MovDir;
12446 ScreenMovPos = player->MovPos;
12447 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12450 else if (!FrameReached(&screen_frame_counter, 1))
12455 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12456 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12457 redraw_mask |= REDRAW_FIELD;
12460 ScreenMovDir = MV_NONE;
12463 void TestIfPlayerTouchesCustomElement(int x, int y)
12465 static int xy[4][2] =
12472 static int trigger_sides[4][2] =
12474 /* center side border side */
12475 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12476 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12477 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12478 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12480 static int touch_dir[4] =
12482 MV_LEFT | MV_RIGHT,
12487 int center_element = Feld[x][y]; /* should always be non-moving! */
12490 for (i = 0; i < NUM_DIRECTIONS; i++)
12492 int xx = x + xy[i][0];
12493 int yy = y + xy[i][1];
12494 int center_side = trigger_sides[i][0];
12495 int border_side = trigger_sides[i][1];
12496 int border_element;
12498 if (!IN_LEV_FIELD(xx, yy))
12501 if (IS_PLAYER(x, y)) /* player found at center element */
12503 struct PlayerInfo *player = PLAYERINFO(x, y);
12505 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12506 border_element = Feld[xx][yy]; /* may be moving! */
12507 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12508 border_element = Feld[xx][yy];
12509 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12510 border_element = MovingOrBlocked2Element(xx, yy);
12512 continue; /* center and border element do not touch */
12514 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12515 player->index_bit, border_side);
12516 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12517 CE_PLAYER_TOUCHES_X,
12518 player->index_bit, border_side);
12521 /* use player element that is initially defined in the level playfield,
12522 not the player element that corresponds to the runtime player number
12523 (example: a level that contains EL_PLAYER_3 as the only player would
12524 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12525 int player_element = PLAYERINFO(x, y)->initial_element;
12527 CheckElementChangeBySide(xx, yy, border_element, player_element,
12528 CE_TOUCHING_X, border_side);
12531 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12533 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12535 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12537 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12538 continue; /* center and border element do not touch */
12541 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12542 player->index_bit, center_side);
12543 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12544 CE_PLAYER_TOUCHES_X,
12545 player->index_bit, center_side);
12548 /* use player element that is initially defined in the level playfield,
12549 not the player element that corresponds to the runtime player number
12550 (example: a level that contains EL_PLAYER_3 as the only player would
12551 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12552 int player_element = PLAYERINFO(xx, yy)->initial_element;
12554 CheckElementChangeBySide(x, y, center_element, player_element,
12555 CE_TOUCHING_X, center_side);
12563 void TestIfElementTouchesCustomElement(int x, int y)
12565 static int xy[4][2] =
12572 static int trigger_sides[4][2] =
12574 /* center side border side */
12575 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12576 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12577 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12578 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12580 static int touch_dir[4] =
12582 MV_LEFT | MV_RIGHT,
12587 boolean change_center_element = FALSE;
12588 int center_element = Feld[x][y]; /* should always be non-moving! */
12589 int border_element_old[NUM_DIRECTIONS];
12592 for (i = 0; i < NUM_DIRECTIONS; i++)
12594 int xx = x + xy[i][0];
12595 int yy = y + xy[i][1];
12596 int border_element;
12598 border_element_old[i] = -1;
12600 if (!IN_LEV_FIELD(xx, yy))
12603 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12604 border_element = Feld[xx][yy]; /* may be moving! */
12605 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12606 border_element = Feld[xx][yy];
12607 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12608 border_element = MovingOrBlocked2Element(xx, yy);
12610 continue; /* center and border element do not touch */
12612 border_element_old[i] = border_element;
12615 for (i = 0; i < NUM_DIRECTIONS; i++)
12617 int xx = x + xy[i][0];
12618 int yy = y + xy[i][1];
12619 int center_side = trigger_sides[i][0];
12620 int border_element = border_element_old[i];
12622 if (border_element == -1)
12625 /* check for change of border element */
12626 CheckElementChangeBySide(xx, yy, border_element, center_element,
12627 CE_TOUCHING_X, center_side);
12629 /* (center element cannot be player, so we dont have to check this here) */
12632 for (i = 0; i < NUM_DIRECTIONS; i++)
12634 int xx = x + xy[i][0];
12635 int yy = y + xy[i][1];
12636 int border_side = trigger_sides[i][1];
12637 int border_element = border_element_old[i];
12639 if (border_element == -1)
12642 /* check for change of center element (but change it only once) */
12643 if (!change_center_element)
12644 change_center_element =
12645 CheckElementChangeBySide(x, y, center_element, border_element,
12646 CE_TOUCHING_X, border_side);
12648 if (IS_PLAYER(xx, yy))
12650 /* use player element that is initially defined in the level playfield,
12651 not the player element that corresponds to the runtime player number
12652 (example: a level that contains EL_PLAYER_3 as the only player would
12653 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12654 int player_element = PLAYERINFO(xx, yy)->initial_element;
12656 CheckElementChangeBySide(x, y, center_element, player_element,
12657 CE_TOUCHING_X, border_side);
12662 void TestIfElementHitsCustomElement(int x, int y, int direction)
12664 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12665 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12666 int hitx = x + dx, hity = y + dy;
12667 int hitting_element = Feld[x][y];
12668 int touched_element;
12670 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12673 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12674 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12676 if (IN_LEV_FIELD(hitx, hity))
12678 int opposite_direction = MV_DIR_OPPOSITE(direction);
12679 int hitting_side = direction;
12680 int touched_side = opposite_direction;
12681 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12682 MovDir[hitx][hity] != direction ||
12683 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12689 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12690 CE_HITTING_X, touched_side);
12692 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12693 CE_HIT_BY_X, hitting_side);
12695 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12696 CE_HIT_BY_SOMETHING, opposite_direction);
12698 if (IS_PLAYER(hitx, hity))
12700 /* use player element that is initially defined in the level playfield,
12701 not the player element that corresponds to the runtime player number
12702 (example: a level that contains EL_PLAYER_3 as the only player would
12703 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12704 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12706 CheckElementChangeBySide(x, y, hitting_element, player_element,
12707 CE_HITTING_X, touched_side);
12712 /* "hitting something" is also true when hitting the playfield border */
12713 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12714 CE_HITTING_SOMETHING, direction);
12717 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12719 int i, kill_x = -1, kill_y = -1;
12721 int bad_element = -1;
12722 static int test_xy[4][2] =
12729 static int test_dir[4] =
12737 for (i = 0; i < NUM_DIRECTIONS; i++)
12739 int test_x, test_y, test_move_dir, test_element;
12741 test_x = good_x + test_xy[i][0];
12742 test_y = good_y + test_xy[i][1];
12744 if (!IN_LEV_FIELD(test_x, test_y))
12748 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12750 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12752 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12753 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12755 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12756 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12760 bad_element = test_element;
12766 if (kill_x != -1 || kill_y != -1)
12768 if (IS_PLAYER(good_x, good_y))
12770 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12772 if (player->shield_deadly_time_left > 0 &&
12773 !IS_INDESTRUCTIBLE(bad_element))
12774 Bang(kill_x, kill_y);
12775 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12776 KillPlayer(player);
12779 Bang(good_x, good_y);
12783 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12785 int i, kill_x = -1, kill_y = -1;
12786 int bad_element = Feld[bad_x][bad_y];
12787 static int test_xy[4][2] =
12794 static int touch_dir[4] =
12796 MV_LEFT | MV_RIGHT,
12801 static int test_dir[4] =
12809 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12812 for (i = 0; i < NUM_DIRECTIONS; i++)
12814 int test_x, test_y, test_move_dir, test_element;
12816 test_x = bad_x + test_xy[i][0];
12817 test_y = bad_y + test_xy[i][1];
12819 if (!IN_LEV_FIELD(test_x, test_y))
12823 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12825 test_element = Feld[test_x][test_y];
12827 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12828 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12830 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12831 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12833 /* good thing is player or penguin that does not move away */
12834 if (IS_PLAYER(test_x, test_y))
12836 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12838 if (bad_element == EL_ROBOT && player->is_moving)
12839 continue; /* robot does not kill player if he is moving */
12841 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12843 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12844 continue; /* center and border element do not touch */
12852 else if (test_element == EL_PENGUIN)
12862 if (kill_x != -1 || kill_y != -1)
12864 if (IS_PLAYER(kill_x, kill_y))
12866 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12868 if (player->shield_deadly_time_left > 0 &&
12869 !IS_INDESTRUCTIBLE(bad_element))
12870 Bang(bad_x, bad_y);
12871 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12872 KillPlayer(player);
12875 Bang(kill_x, kill_y);
12879 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12881 int bad_element = Feld[bad_x][bad_y];
12882 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12883 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12884 int test_x = bad_x + dx, test_y = bad_y + dy;
12885 int test_move_dir, test_element;
12886 int kill_x = -1, kill_y = -1;
12888 if (!IN_LEV_FIELD(test_x, test_y))
12892 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12894 test_element = Feld[test_x][test_y];
12896 if (test_move_dir != bad_move_dir)
12898 /* good thing can be player or penguin that does not move away */
12899 if (IS_PLAYER(test_x, test_y))
12901 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12903 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12904 player as being hit when he is moving towards the bad thing, because
12905 the "get hit by" condition would be lost after the player stops) */
12906 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12907 return; /* player moves away from bad thing */
12912 else if (test_element == EL_PENGUIN)
12919 if (kill_x != -1 || kill_y != -1)
12921 if (IS_PLAYER(kill_x, kill_y))
12923 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12925 if (player->shield_deadly_time_left > 0 &&
12926 !IS_INDESTRUCTIBLE(bad_element))
12927 Bang(bad_x, bad_y);
12928 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12929 KillPlayer(player);
12932 Bang(kill_x, kill_y);
12936 void TestIfPlayerTouchesBadThing(int x, int y)
12938 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12941 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12943 TestIfGoodThingHitsBadThing(x, y, move_dir);
12946 void TestIfBadThingTouchesPlayer(int x, int y)
12948 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12951 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12953 TestIfBadThingHitsGoodThing(x, y, move_dir);
12956 void TestIfFriendTouchesBadThing(int x, int y)
12958 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12961 void TestIfBadThingTouchesFriend(int x, int y)
12963 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12966 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12968 int i, kill_x = bad_x, kill_y = bad_y;
12969 static int xy[4][2] =
12977 for (i = 0; i < NUM_DIRECTIONS; i++)
12981 x = bad_x + xy[i][0];
12982 y = bad_y + xy[i][1];
12983 if (!IN_LEV_FIELD(x, y))
12986 element = Feld[x][y];
12987 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12988 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12996 if (kill_x != bad_x || kill_y != bad_y)
12997 Bang(bad_x, bad_y);
13000 void KillPlayer(struct PlayerInfo *player)
13002 int jx = player->jx, jy = player->jy;
13004 if (!player->active)
13008 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13009 player->killed, player->active, player->reanimated);
13012 /* the following code was introduced to prevent an infinite loop when calling
13014 -> CheckTriggeredElementChangeExt()
13015 -> ExecuteCustomElementAction()
13017 -> (infinitely repeating the above sequence of function calls)
13018 which occurs when killing the player while having a CE with the setting
13019 "kill player X when explosion of <player X>"; the solution using a new
13020 field "player->killed" was chosen for backwards compatibility, although
13021 clever use of the fields "player->active" etc. would probably also work */
13023 if (player->killed)
13027 player->killed = TRUE;
13029 /* remove accessible field at the player's position */
13030 Feld[jx][jy] = EL_EMPTY;
13032 /* deactivate shield (else Bang()/Explode() would not work right) */
13033 player->shield_normal_time_left = 0;
13034 player->shield_deadly_time_left = 0;
13037 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13038 player->killed, player->active, player->reanimated);
13044 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13045 player->killed, player->active, player->reanimated);
13048 if (player->reanimated) /* killed player may have been reanimated */
13049 player->killed = player->reanimated = FALSE;
13051 BuryPlayer(player);
13054 static void KillPlayerUnlessEnemyProtected(int x, int y)
13056 if (!PLAYER_ENEMY_PROTECTED(x, y))
13057 KillPlayer(PLAYERINFO(x, y));
13060 static void KillPlayerUnlessExplosionProtected(int x, int y)
13062 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13063 KillPlayer(PLAYERINFO(x, y));
13066 void BuryPlayer(struct PlayerInfo *player)
13068 int jx = player->jx, jy = player->jy;
13070 if (!player->active)
13073 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13074 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13076 player->GameOver = TRUE;
13077 RemovePlayer(player);
13080 void RemovePlayer(struct PlayerInfo *player)
13082 int jx = player->jx, jy = player->jy;
13083 int i, found = FALSE;
13085 player->present = FALSE;
13086 player->active = FALSE;
13088 if (!ExplodeField[jx][jy])
13089 StorePlayer[jx][jy] = 0;
13091 if (player->is_moving)
13092 TEST_DrawLevelField(player->last_jx, player->last_jy);
13094 for (i = 0; i < MAX_PLAYERS; i++)
13095 if (stored_player[i].active)
13099 AllPlayersGone = TRUE;
13105 static void setFieldForSnapping(int x, int y, int element, int direction)
13107 struct ElementInfo *ei = &element_info[element];
13108 int direction_bit = MV_DIR_TO_BIT(direction);
13109 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13110 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13111 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13113 Feld[x][y] = EL_ELEMENT_SNAPPING;
13114 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13116 ResetGfxAnimation(x, y);
13118 GfxElement[x][y] = element;
13119 GfxAction[x][y] = action;
13120 GfxDir[x][y] = direction;
13121 GfxFrame[x][y] = -1;
13125 =============================================================================
13126 checkDiagonalPushing()
13127 -----------------------------------------------------------------------------
13128 check if diagonal input device direction results in pushing of object
13129 (by checking if the alternative direction is walkable, diggable, ...)
13130 =============================================================================
13133 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13134 int x, int y, int real_dx, int real_dy)
13136 int jx, jy, dx, dy, xx, yy;
13138 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13141 /* diagonal direction: check alternative direction */
13146 xx = jx + (dx == 0 ? real_dx : 0);
13147 yy = jy + (dy == 0 ? real_dy : 0);
13149 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13153 =============================================================================
13155 -----------------------------------------------------------------------------
13156 x, y: field next to player (non-diagonal) to try to dig to
13157 real_dx, real_dy: direction as read from input device (can be diagonal)
13158 =============================================================================
13161 static int DigField(struct PlayerInfo *player,
13162 int oldx, int oldy, int x, int y,
13163 int real_dx, int real_dy, int mode)
13165 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13166 boolean player_was_pushing = player->is_pushing;
13167 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13168 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13169 int jx = oldx, jy = oldy;
13170 int dx = x - jx, dy = y - jy;
13171 int nextx = x + dx, nexty = y + dy;
13172 int move_direction = (dx == -1 ? MV_LEFT :
13173 dx == +1 ? MV_RIGHT :
13175 dy == +1 ? MV_DOWN : MV_NONE);
13176 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13177 int dig_side = MV_DIR_OPPOSITE(move_direction);
13178 int old_element = Feld[jx][jy];
13179 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13182 if (is_player) /* function can also be called by EL_PENGUIN */
13184 if (player->MovPos == 0)
13186 player->is_digging = FALSE;
13187 player->is_collecting = FALSE;
13190 if (player->MovPos == 0) /* last pushing move finished */
13191 player->is_pushing = FALSE;
13193 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13195 player->is_switching = FALSE;
13196 player->push_delay = -1;
13198 return MP_NO_ACTION;
13202 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13203 old_element = Back[jx][jy];
13205 /* in case of element dropped at player position, check background */
13206 else if (Back[jx][jy] != EL_EMPTY &&
13207 game.engine_version >= VERSION_IDENT(2,2,0,0))
13208 old_element = Back[jx][jy];
13210 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13211 return MP_NO_ACTION; /* field has no opening in this direction */
13213 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13214 return MP_NO_ACTION; /* field has no opening in this direction */
13216 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13220 Feld[jx][jy] = player->artwork_element;
13221 InitMovingField(jx, jy, MV_DOWN);
13222 Store[jx][jy] = EL_ACID;
13223 ContinueMoving(jx, jy);
13224 BuryPlayer(player);
13226 return MP_DONT_RUN_INTO;
13229 if (player_can_move && DONT_RUN_INTO(element))
13231 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13233 return MP_DONT_RUN_INTO;
13236 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13237 return MP_NO_ACTION;
13239 collect_count = element_info[element].collect_count_initial;
13241 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13242 return MP_NO_ACTION;
13244 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13245 player_can_move = player_can_move_or_snap;
13247 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13248 game.engine_version >= VERSION_IDENT(2,2,0,0))
13250 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13251 player->index_bit, dig_side);
13252 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13253 player->index_bit, dig_side);
13255 if (element == EL_DC_LANDMINE)
13258 if (Feld[x][y] != element) /* field changed by snapping */
13261 return MP_NO_ACTION;
13264 if (player->gravity && is_player && !player->is_auto_moving &&
13265 canFallDown(player) && move_direction != MV_DOWN &&
13266 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13267 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13269 if (player_can_move &&
13270 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13272 int sound_element = SND_ELEMENT(element);
13273 int sound_action = ACTION_WALKING;
13275 if (IS_RND_GATE(element))
13277 if (!player->key[RND_GATE_NR(element)])
13278 return MP_NO_ACTION;
13280 else if (IS_RND_GATE_GRAY(element))
13282 if (!player->key[RND_GATE_GRAY_NR(element)])
13283 return MP_NO_ACTION;
13285 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13287 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13288 return MP_NO_ACTION;
13290 else if (element == EL_EXIT_OPEN ||
13291 element == EL_EM_EXIT_OPEN ||
13292 element == EL_EM_EXIT_OPENING ||
13293 element == EL_STEEL_EXIT_OPEN ||
13294 element == EL_EM_STEEL_EXIT_OPEN ||
13295 element == EL_EM_STEEL_EXIT_OPENING ||
13296 element == EL_SP_EXIT_OPEN ||
13297 element == EL_SP_EXIT_OPENING)
13299 sound_action = ACTION_PASSING; /* player is passing exit */
13301 else if (element == EL_EMPTY)
13303 sound_action = ACTION_MOVING; /* nothing to walk on */
13306 /* play sound from background or player, whatever is available */
13307 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13308 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13310 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13312 else if (player_can_move &&
13313 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13315 if (!ACCESS_FROM(element, opposite_direction))
13316 return MP_NO_ACTION; /* field not accessible from this direction */
13318 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13319 return MP_NO_ACTION;
13321 if (IS_EM_GATE(element))
13323 if (!player->key[EM_GATE_NR(element)])
13324 return MP_NO_ACTION;
13326 else if (IS_EM_GATE_GRAY(element))
13328 if (!player->key[EM_GATE_GRAY_NR(element)])
13329 return MP_NO_ACTION;
13331 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13333 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13334 return MP_NO_ACTION;
13336 else if (IS_EMC_GATE(element))
13338 if (!player->key[EMC_GATE_NR(element)])
13339 return MP_NO_ACTION;
13341 else if (IS_EMC_GATE_GRAY(element))
13343 if (!player->key[EMC_GATE_GRAY_NR(element)])
13344 return MP_NO_ACTION;
13346 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13348 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13349 return MP_NO_ACTION;
13351 else if (element == EL_DC_GATE_WHITE ||
13352 element == EL_DC_GATE_WHITE_GRAY ||
13353 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13355 if (player->num_white_keys == 0)
13356 return MP_NO_ACTION;
13358 player->num_white_keys--;
13360 else if (IS_SP_PORT(element))
13362 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13363 element == EL_SP_GRAVITY_PORT_RIGHT ||
13364 element == EL_SP_GRAVITY_PORT_UP ||
13365 element == EL_SP_GRAVITY_PORT_DOWN)
13366 player->gravity = !player->gravity;
13367 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13368 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13369 element == EL_SP_GRAVITY_ON_PORT_UP ||
13370 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13371 player->gravity = TRUE;
13372 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13373 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13374 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13375 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13376 player->gravity = FALSE;
13379 /* automatically move to the next field with double speed */
13380 player->programmed_action = move_direction;
13382 if (player->move_delay_reset_counter == 0)
13384 player->move_delay_reset_counter = 2; /* two double speed steps */
13386 DOUBLE_PLAYER_SPEED(player);
13389 PlayLevelSoundAction(x, y, ACTION_PASSING);
13391 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13395 if (mode != DF_SNAP)
13397 GfxElement[x][y] = GFX_ELEMENT(element);
13398 player->is_digging = TRUE;
13401 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13403 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13404 player->index_bit, dig_side);
13406 if (mode == DF_SNAP)
13408 if (level.block_snap_field)
13409 setFieldForSnapping(x, y, element, move_direction);
13411 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13413 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13414 player->index_bit, dig_side);
13417 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13421 if (is_player && mode != DF_SNAP)
13423 GfxElement[x][y] = element;
13424 player->is_collecting = TRUE;
13427 if (element == EL_SPEED_PILL)
13429 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13431 else if (element == EL_EXTRA_TIME && level.time > 0)
13433 TimeLeft += level.extra_time;
13435 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13437 DisplayGameControlValues();
13439 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13441 player->shield_normal_time_left += level.shield_normal_time;
13442 if (element == EL_SHIELD_DEADLY)
13443 player->shield_deadly_time_left += level.shield_deadly_time;
13445 else if (element == EL_DYNAMITE ||
13446 element == EL_EM_DYNAMITE ||
13447 element == EL_SP_DISK_RED)
13449 if (player->inventory_size < MAX_INVENTORY_SIZE)
13450 player->inventory_element[player->inventory_size++] = element;
13452 DrawGameDoorValues();
13454 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13456 player->dynabomb_count++;
13457 player->dynabombs_left++;
13459 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13461 player->dynabomb_size++;
13463 else if (element == EL_DYNABOMB_INCREASE_POWER)
13465 player->dynabomb_xl = TRUE;
13467 else if (IS_KEY(element))
13469 player->key[KEY_NR(element)] = TRUE;
13471 DrawGameDoorValues();
13473 else if (element == EL_DC_KEY_WHITE)
13475 player->num_white_keys++;
13477 /* display white keys? */
13478 /* DrawGameDoorValues(); */
13480 else if (IS_ENVELOPE(element))
13482 player->show_envelope = element;
13484 else if (element == EL_EMC_LENSES)
13486 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13488 RedrawAllInvisibleElementsForLenses();
13490 else if (element == EL_EMC_MAGNIFIER)
13492 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13494 RedrawAllInvisibleElementsForMagnifier();
13496 else if (IS_DROPPABLE(element) ||
13497 IS_THROWABLE(element)) /* can be collected and dropped */
13501 if (collect_count == 0)
13502 player->inventory_infinite_element = element;
13504 for (i = 0; i < collect_count; i++)
13505 if (player->inventory_size < MAX_INVENTORY_SIZE)
13506 player->inventory_element[player->inventory_size++] = element;
13508 DrawGameDoorValues();
13510 else if (collect_count > 0)
13512 local_player->gems_still_needed -= collect_count;
13513 if (local_player->gems_still_needed < 0)
13514 local_player->gems_still_needed = 0;
13516 game.snapshot.collected_item = TRUE;
13518 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13520 DisplayGameControlValues();
13523 RaiseScoreElement(element);
13524 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13527 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13528 player->index_bit, dig_side);
13530 if (mode == DF_SNAP)
13532 if (level.block_snap_field)
13533 setFieldForSnapping(x, y, element, move_direction);
13535 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13537 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13538 player->index_bit, dig_side);
13541 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13543 if (mode == DF_SNAP && element != EL_BD_ROCK)
13544 return MP_NO_ACTION;
13546 if (CAN_FALL(element) && dy)
13547 return MP_NO_ACTION;
13549 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13550 !(element == EL_SPRING && level.use_spring_bug))
13551 return MP_NO_ACTION;
13553 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13554 ((move_direction & MV_VERTICAL &&
13555 ((element_info[element].move_pattern & MV_LEFT &&
13556 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13557 (element_info[element].move_pattern & MV_RIGHT &&
13558 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13559 (move_direction & MV_HORIZONTAL &&
13560 ((element_info[element].move_pattern & MV_UP &&
13561 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13562 (element_info[element].move_pattern & MV_DOWN &&
13563 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13564 return MP_NO_ACTION;
13566 /* do not push elements already moving away faster than player */
13567 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13568 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13569 return MP_NO_ACTION;
13571 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13573 if (player->push_delay_value == -1 || !player_was_pushing)
13574 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13576 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13578 if (player->push_delay_value == -1)
13579 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13581 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13583 if (!player->is_pushing)
13584 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13587 player->is_pushing = TRUE;
13588 player->is_active = TRUE;
13590 if (!(IN_LEV_FIELD(nextx, nexty) &&
13591 (IS_FREE(nextx, nexty) ||
13592 (IS_SB_ELEMENT(element) &&
13593 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13594 (IS_CUSTOM_ELEMENT(element) &&
13595 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13596 return MP_NO_ACTION;
13598 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13599 return MP_NO_ACTION;
13601 if (player->push_delay == -1) /* new pushing; restart delay */
13602 player->push_delay = 0;
13604 if (player->push_delay < player->push_delay_value &&
13605 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13606 element != EL_SPRING && element != EL_BALLOON)
13608 /* make sure that there is no move delay before next try to push */
13609 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13610 player->move_delay = 0;
13612 return MP_NO_ACTION;
13615 if (IS_CUSTOM_ELEMENT(element) &&
13616 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13618 if (!DigFieldByCE(nextx, nexty, element))
13619 return MP_NO_ACTION;
13622 if (IS_SB_ELEMENT(element))
13624 if (element == EL_SOKOBAN_FIELD_FULL)
13626 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13627 local_player->sokobanfields_still_needed++;
13630 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13632 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13633 local_player->sokobanfields_still_needed--;
13636 Feld[x][y] = EL_SOKOBAN_OBJECT;
13638 if (Back[x][y] == Back[nextx][nexty])
13639 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13640 else if (Back[x][y] != 0)
13641 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13644 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13647 if (local_player->sokobanfields_still_needed == 0 &&
13648 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13650 PlayerWins(player);
13652 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13656 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13658 InitMovingField(x, y, move_direction);
13659 GfxAction[x][y] = ACTION_PUSHING;
13661 if (mode == DF_SNAP)
13662 ContinueMoving(x, y);
13664 MovPos[x][y] = (dx != 0 ? dx : dy);
13666 Pushed[x][y] = TRUE;
13667 Pushed[nextx][nexty] = TRUE;
13669 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13670 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13672 player->push_delay_value = -1; /* get new value later */
13674 /* check for element change _after_ element has been pushed */
13675 if (game.use_change_when_pushing_bug)
13677 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13678 player->index_bit, dig_side);
13679 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13680 player->index_bit, dig_side);
13683 else if (IS_SWITCHABLE(element))
13685 if (PLAYER_SWITCHING(player, x, y))
13687 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13688 player->index_bit, dig_side);
13693 player->is_switching = TRUE;
13694 player->switch_x = x;
13695 player->switch_y = y;
13697 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13699 if (element == EL_ROBOT_WHEEL)
13701 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13705 game.robot_wheel_active = TRUE;
13707 TEST_DrawLevelField(x, y);
13709 else if (element == EL_SP_TERMINAL)
13713 SCAN_PLAYFIELD(xx, yy)
13715 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13719 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13721 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13723 ResetGfxAnimation(xx, yy);
13724 TEST_DrawLevelField(xx, yy);
13728 else if (IS_BELT_SWITCH(element))
13730 ToggleBeltSwitch(x, y);
13732 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13733 element == EL_SWITCHGATE_SWITCH_DOWN ||
13734 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13735 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13737 ToggleSwitchgateSwitch(x, y);
13739 else if (element == EL_LIGHT_SWITCH ||
13740 element == EL_LIGHT_SWITCH_ACTIVE)
13742 ToggleLightSwitch(x, y);
13744 else if (element == EL_TIMEGATE_SWITCH ||
13745 element == EL_DC_TIMEGATE_SWITCH)
13747 ActivateTimegateSwitch(x, y);
13749 else if (element == EL_BALLOON_SWITCH_LEFT ||
13750 element == EL_BALLOON_SWITCH_RIGHT ||
13751 element == EL_BALLOON_SWITCH_UP ||
13752 element == EL_BALLOON_SWITCH_DOWN ||
13753 element == EL_BALLOON_SWITCH_NONE ||
13754 element == EL_BALLOON_SWITCH_ANY)
13756 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13757 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13758 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13759 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13760 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13763 else if (element == EL_LAMP)
13765 Feld[x][y] = EL_LAMP_ACTIVE;
13766 local_player->lights_still_needed--;
13768 ResetGfxAnimation(x, y);
13769 TEST_DrawLevelField(x, y);
13771 else if (element == EL_TIME_ORB_FULL)
13773 Feld[x][y] = EL_TIME_ORB_EMPTY;
13775 if (level.time > 0 || level.use_time_orb_bug)
13777 TimeLeft += level.time_orb_time;
13778 game.no_time_limit = FALSE;
13780 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13782 DisplayGameControlValues();
13785 ResetGfxAnimation(x, y);
13786 TEST_DrawLevelField(x, y);
13788 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13789 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13793 game.ball_state = !game.ball_state;
13795 SCAN_PLAYFIELD(xx, yy)
13797 int e = Feld[xx][yy];
13799 if (game.ball_state)
13801 if (e == EL_EMC_MAGIC_BALL)
13802 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13803 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13804 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13808 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13809 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13810 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13811 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13816 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13817 player->index_bit, dig_side);
13819 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13820 player->index_bit, dig_side);
13822 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13823 player->index_bit, dig_side);
13829 if (!PLAYER_SWITCHING(player, x, y))
13831 player->is_switching = TRUE;
13832 player->switch_x = x;
13833 player->switch_y = y;
13835 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13836 player->index_bit, dig_side);
13837 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13838 player->index_bit, dig_side);
13840 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13841 player->index_bit, dig_side);
13842 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13843 player->index_bit, dig_side);
13846 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13847 player->index_bit, dig_side);
13848 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13849 player->index_bit, dig_side);
13851 return MP_NO_ACTION;
13854 player->push_delay = -1;
13856 if (is_player) /* function can also be called by EL_PENGUIN */
13858 if (Feld[x][y] != element) /* really digged/collected something */
13860 player->is_collecting = !player->is_digging;
13861 player->is_active = TRUE;
13868 static boolean DigFieldByCE(int x, int y, int digging_element)
13870 int element = Feld[x][y];
13872 if (!IS_FREE(x, y))
13874 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13875 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13878 /* no element can dig solid indestructible elements */
13879 if (IS_INDESTRUCTIBLE(element) &&
13880 !IS_DIGGABLE(element) &&
13881 !IS_COLLECTIBLE(element))
13884 if (AmoebaNr[x][y] &&
13885 (element == EL_AMOEBA_FULL ||
13886 element == EL_BD_AMOEBA ||
13887 element == EL_AMOEBA_GROWING))
13889 AmoebaCnt[AmoebaNr[x][y]]--;
13890 AmoebaCnt2[AmoebaNr[x][y]]--;
13893 if (IS_MOVING(x, y))
13894 RemoveMovingField(x, y);
13898 TEST_DrawLevelField(x, y);
13901 /* if digged element was about to explode, prevent the explosion */
13902 ExplodeField[x][y] = EX_TYPE_NONE;
13904 PlayLevelSoundAction(x, y, action);
13907 Store[x][y] = EL_EMPTY;
13909 /* this makes it possible to leave the removed element again */
13910 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13911 Store[x][y] = element;
13916 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13918 int jx = player->jx, jy = player->jy;
13919 int x = jx + dx, y = jy + dy;
13920 int snap_direction = (dx == -1 ? MV_LEFT :
13921 dx == +1 ? MV_RIGHT :
13923 dy == +1 ? MV_DOWN : MV_NONE);
13924 boolean can_continue_snapping = (level.continuous_snapping &&
13925 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13927 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13930 if (!player->active || !IN_LEV_FIELD(x, y))
13938 if (player->MovPos == 0)
13939 player->is_pushing = FALSE;
13941 player->is_snapping = FALSE;
13943 if (player->MovPos == 0)
13945 player->is_moving = FALSE;
13946 player->is_digging = FALSE;
13947 player->is_collecting = FALSE;
13953 /* prevent snapping with already pressed snap key when not allowed */
13954 if (player->is_snapping && !can_continue_snapping)
13957 player->MovDir = snap_direction;
13959 if (player->MovPos == 0)
13961 player->is_moving = FALSE;
13962 player->is_digging = FALSE;
13963 player->is_collecting = FALSE;
13966 player->is_dropping = FALSE;
13967 player->is_dropping_pressed = FALSE;
13968 player->drop_pressed_delay = 0;
13970 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13973 player->is_snapping = TRUE;
13974 player->is_active = TRUE;
13976 if (player->MovPos == 0)
13978 player->is_moving = FALSE;
13979 player->is_digging = FALSE;
13980 player->is_collecting = FALSE;
13983 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13984 TEST_DrawLevelField(player->last_jx, player->last_jy);
13986 TEST_DrawLevelField(x, y);
13991 static boolean DropElement(struct PlayerInfo *player)
13993 int old_element, new_element;
13994 int dropx = player->jx, dropy = player->jy;
13995 int drop_direction = player->MovDir;
13996 int drop_side = drop_direction;
13997 int drop_element = get_next_dropped_element(player);
13999 /* do not drop an element on top of another element; when holding drop key
14000 pressed without moving, dropped element must move away before the next
14001 element can be dropped (this is especially important if the next element
14002 is dynamite, which can be placed on background for historical reasons) */
14003 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14006 if (IS_THROWABLE(drop_element))
14008 dropx += GET_DX_FROM_DIR(drop_direction);
14009 dropy += GET_DY_FROM_DIR(drop_direction);
14011 if (!IN_LEV_FIELD(dropx, dropy))
14015 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14016 new_element = drop_element; /* default: no change when dropping */
14018 /* check if player is active, not moving and ready to drop */
14019 if (!player->active || player->MovPos || player->drop_delay > 0)
14022 /* check if player has anything that can be dropped */
14023 if (new_element == EL_UNDEFINED)
14026 /* only set if player has anything that can be dropped */
14027 player->is_dropping_pressed = TRUE;
14029 /* check if drop key was pressed long enough for EM style dynamite */
14030 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14033 /* check if anything can be dropped at the current position */
14034 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14037 /* collected custom elements can only be dropped on empty fields */
14038 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14041 if (old_element != EL_EMPTY)
14042 Back[dropx][dropy] = old_element; /* store old element on this field */
14044 ResetGfxAnimation(dropx, dropy);
14045 ResetRandomAnimationValue(dropx, dropy);
14047 if (player->inventory_size > 0 ||
14048 player->inventory_infinite_element != EL_UNDEFINED)
14050 if (player->inventory_size > 0)
14052 player->inventory_size--;
14054 DrawGameDoorValues();
14056 if (new_element == EL_DYNAMITE)
14057 new_element = EL_DYNAMITE_ACTIVE;
14058 else if (new_element == EL_EM_DYNAMITE)
14059 new_element = EL_EM_DYNAMITE_ACTIVE;
14060 else if (new_element == EL_SP_DISK_RED)
14061 new_element = EL_SP_DISK_RED_ACTIVE;
14064 Feld[dropx][dropy] = new_element;
14066 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14067 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14068 el2img(Feld[dropx][dropy]), 0);
14070 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14072 /* needed if previous element just changed to "empty" in the last frame */
14073 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14075 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14076 player->index_bit, drop_side);
14077 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14079 player->index_bit, drop_side);
14081 TestIfElementTouchesCustomElement(dropx, dropy);
14083 else /* player is dropping a dyna bomb */
14085 player->dynabombs_left--;
14087 Feld[dropx][dropy] = new_element;
14089 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14090 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14091 el2img(Feld[dropx][dropy]), 0);
14093 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14096 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14097 InitField_WithBug1(dropx, dropy, FALSE);
14099 new_element = Feld[dropx][dropy]; /* element might have changed */
14101 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14102 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14104 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14105 MovDir[dropx][dropy] = drop_direction;
14107 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14109 /* do not cause impact style collision by dropping elements that can fall */
14110 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14113 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14114 player->is_dropping = TRUE;
14116 player->drop_pressed_delay = 0;
14117 player->is_dropping_pressed = FALSE;
14119 player->drop_x = dropx;
14120 player->drop_y = dropy;
14125 /* ------------------------------------------------------------------------- */
14126 /* game sound playing functions */
14127 /* ------------------------------------------------------------------------- */
14129 static int *loop_sound_frame = NULL;
14130 static int *loop_sound_volume = NULL;
14132 void InitPlayLevelSound()
14134 int num_sounds = getSoundListSize();
14136 checked_free(loop_sound_frame);
14137 checked_free(loop_sound_volume);
14139 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14140 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14143 static void PlayLevelSound(int x, int y, int nr)
14145 int sx = SCREENX(x), sy = SCREENY(y);
14146 int volume, stereo_position;
14147 int max_distance = 8;
14148 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14150 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14151 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14154 if (!IN_LEV_FIELD(x, y) ||
14155 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14156 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14159 volume = SOUND_MAX_VOLUME;
14161 if (!IN_SCR_FIELD(sx, sy))
14163 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14164 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14166 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14169 stereo_position = (SOUND_MAX_LEFT +
14170 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14171 (SCR_FIELDX + 2 * max_distance));
14173 if (IS_LOOP_SOUND(nr))
14175 /* This assures that quieter loop sounds do not overwrite louder ones,
14176 while restarting sound volume comparison with each new game frame. */
14178 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14181 loop_sound_volume[nr] = volume;
14182 loop_sound_frame[nr] = FrameCounter;
14185 PlaySoundExt(nr, volume, stereo_position, type);
14188 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14190 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14191 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14192 y < LEVELY(BY1) ? LEVELY(BY1) :
14193 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14197 static void PlayLevelSoundAction(int x, int y, int action)
14199 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14202 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14204 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14206 if (sound_effect != SND_UNDEFINED)
14207 PlayLevelSound(x, y, sound_effect);
14210 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14213 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14215 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14216 PlayLevelSound(x, y, sound_effect);
14219 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14221 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14223 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14224 PlayLevelSound(x, y, sound_effect);
14227 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14229 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14231 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14232 StopSound(sound_effect);
14235 static void PlayLevelMusic()
14237 if (levelset.music[level_nr] != MUS_UNDEFINED)
14238 PlayMusic(levelset.music[level_nr]); /* from config file */
14240 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14243 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14245 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14246 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14247 int x = xx - 1 - offset;
14248 int y = yy - 1 - offset;
14253 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14257 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14261 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14265 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14269 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14273 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14277 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14280 case SAMPLE_android_clone:
14281 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14284 case SAMPLE_android_move:
14285 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14288 case SAMPLE_spring:
14289 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14293 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14297 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14300 case SAMPLE_eater_eat:
14301 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14305 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14308 case SAMPLE_collect:
14309 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14312 case SAMPLE_diamond:
14313 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14316 case SAMPLE_squash:
14317 /* !!! CHECK THIS !!! */
14319 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14321 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14325 case SAMPLE_wonderfall:
14326 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14330 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14334 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14338 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14342 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14346 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14350 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14353 case SAMPLE_wonder:
14354 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14358 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14361 case SAMPLE_exit_open:
14362 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14365 case SAMPLE_exit_leave:
14366 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14369 case SAMPLE_dynamite:
14370 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14374 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14378 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14382 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14386 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14390 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14394 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14398 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14403 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14405 int element = map_element_SP_to_RND(element_sp);
14406 int action = map_action_SP_to_RND(action_sp);
14407 int offset = (setup.sp_show_border_elements ? 0 : 1);
14408 int x = xx - offset;
14409 int y = yy - offset;
14411 PlayLevelSoundElementAction(x, y, element, action);
14414 void RaiseScore(int value)
14416 local_player->score += value;
14418 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14420 DisplayGameControlValues();
14423 void RaiseScoreElement(int element)
14428 case EL_BD_DIAMOND:
14429 case EL_EMERALD_YELLOW:
14430 case EL_EMERALD_RED:
14431 case EL_EMERALD_PURPLE:
14432 case EL_SP_INFOTRON:
14433 RaiseScore(level.score[SC_EMERALD]);
14436 RaiseScore(level.score[SC_DIAMOND]);
14439 RaiseScore(level.score[SC_CRYSTAL]);
14442 RaiseScore(level.score[SC_PEARL]);
14445 case EL_BD_BUTTERFLY:
14446 case EL_SP_ELECTRON:
14447 RaiseScore(level.score[SC_BUG]);
14450 case EL_BD_FIREFLY:
14451 case EL_SP_SNIKSNAK:
14452 RaiseScore(level.score[SC_SPACESHIP]);
14455 case EL_DARK_YAMYAM:
14456 RaiseScore(level.score[SC_YAMYAM]);
14459 RaiseScore(level.score[SC_ROBOT]);
14462 RaiseScore(level.score[SC_PACMAN]);
14465 RaiseScore(level.score[SC_NUT]);
14468 case EL_EM_DYNAMITE:
14469 case EL_SP_DISK_RED:
14470 case EL_DYNABOMB_INCREASE_NUMBER:
14471 case EL_DYNABOMB_INCREASE_SIZE:
14472 case EL_DYNABOMB_INCREASE_POWER:
14473 RaiseScore(level.score[SC_DYNAMITE]);
14475 case EL_SHIELD_NORMAL:
14476 case EL_SHIELD_DEADLY:
14477 RaiseScore(level.score[SC_SHIELD]);
14479 case EL_EXTRA_TIME:
14480 RaiseScore(level.extra_time_score);
14494 case EL_DC_KEY_WHITE:
14495 RaiseScore(level.score[SC_KEY]);
14498 RaiseScore(element_info[element].collect_score);
14503 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14505 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14507 /* closing door required in case of envelope style request dialogs */
14509 CloseDoor(DOOR_CLOSE_1);
14511 #if defined(NETWORK_AVALIABLE)
14512 if (options.network)
14513 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14518 FadeSkipNextFadeIn();
14520 SetGameStatus(GAME_MODE_MAIN);
14525 else /* continue playing the game */
14527 if (tape.playing && tape.deactivate_display)
14528 TapeDeactivateDisplayOff(TRUE);
14530 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14532 if (tape.playing && tape.deactivate_display)
14533 TapeDeactivateDisplayOn();
14537 void RequestQuitGame(boolean ask_if_really_quit)
14539 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14540 boolean skip_request = AllPlayersGone || quick_quit;
14542 RequestQuitGameExt(skip_request, quick_quit,
14543 "Do you really want to quit the game?");
14547 /* ------------------------------------------------------------------------- */
14548 /* random generator functions */
14549 /* ------------------------------------------------------------------------- */
14551 unsigned int InitEngineRandom_RND(int seed)
14553 game.num_random_calls = 0;
14555 return InitEngineRandom(seed);
14558 unsigned int RND(int max)
14562 game.num_random_calls++;
14564 return GetEngineRandom(max);
14571 /* ------------------------------------------------------------------------- */
14572 /* game engine snapshot handling functions */
14573 /* ------------------------------------------------------------------------- */
14575 struct EngineSnapshotInfo
14577 /* runtime values for custom element collect score */
14578 int collect_score[NUM_CUSTOM_ELEMENTS];
14580 /* runtime values for group element choice position */
14581 int choice_pos[NUM_GROUP_ELEMENTS];
14583 /* runtime values for belt position animations */
14584 int belt_graphic[4][NUM_BELT_PARTS];
14585 int belt_anim_mode[4][NUM_BELT_PARTS];
14588 static struct EngineSnapshotInfo engine_snapshot_rnd;
14589 static char *snapshot_level_identifier = NULL;
14590 static int snapshot_level_nr = -1;
14592 static void SaveEngineSnapshotValues_RND()
14594 static int belt_base_active_element[4] =
14596 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14597 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14598 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14599 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14603 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14605 int element = EL_CUSTOM_START + i;
14607 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14610 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14612 int element = EL_GROUP_START + i;
14614 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14617 for (i = 0; i < 4; i++)
14619 for (j = 0; j < NUM_BELT_PARTS; j++)
14621 int element = belt_base_active_element[i] + j;
14622 int graphic = el2img(element);
14623 int anim_mode = graphic_info[graphic].anim_mode;
14625 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14626 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14631 static void LoadEngineSnapshotValues_RND()
14633 unsigned int num_random_calls = game.num_random_calls;
14636 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14638 int element = EL_CUSTOM_START + i;
14640 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14643 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14645 int element = EL_GROUP_START + i;
14647 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14650 for (i = 0; i < 4; i++)
14652 for (j = 0; j < NUM_BELT_PARTS; j++)
14654 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14655 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14657 graphic_info[graphic].anim_mode = anim_mode;
14661 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14663 InitRND(tape.random_seed);
14664 for (i = 0; i < num_random_calls; i++)
14668 if (game.num_random_calls != num_random_calls)
14670 Error(ERR_INFO, "number of random calls out of sync");
14671 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14672 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14673 Error(ERR_EXIT, "this should not happen -- please debug");
14677 void FreeEngineSnapshotSingle()
14679 FreeSnapshotSingle();
14681 setString(&snapshot_level_identifier, NULL);
14682 snapshot_level_nr = -1;
14685 void FreeEngineSnapshotList()
14687 FreeSnapshotList();
14690 ListNode *SaveEngineSnapshotBuffers()
14692 ListNode *buffers = NULL;
14694 /* copy some special values to a structure better suited for the snapshot */
14696 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14697 SaveEngineSnapshotValues_RND();
14698 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14699 SaveEngineSnapshotValues_EM();
14700 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14701 SaveEngineSnapshotValues_SP(&buffers);
14703 /* save values stored in special snapshot structure */
14705 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14706 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14707 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14708 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14709 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14710 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14712 /* save further RND engine values */
14714 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14715 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14716 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14718 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14719 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14720 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14721 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14723 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14724 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14725 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14726 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14727 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14729 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14730 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14731 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14733 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14735 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14737 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14738 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14740 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14741 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14742 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14743 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14744 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14745 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14746 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14747 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14748 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14749 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14750 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14751 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14753 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14754 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14755 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14756 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14757 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14759 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14760 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14762 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14763 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14764 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14766 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14767 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14769 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14770 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14771 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14772 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14773 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14775 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14776 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14779 ListNode *node = engine_snapshot_list_rnd;
14782 while (node != NULL)
14784 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14789 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14795 void SaveEngineSnapshotSingle()
14797 ListNode *buffers = SaveEngineSnapshotBuffers();
14799 /* finally save all snapshot buffers to single snapshot */
14800 SaveSnapshotSingle(buffers);
14802 /* save level identification information */
14803 setString(&snapshot_level_identifier, leveldir_current->identifier);
14804 snapshot_level_nr = level_nr;
14807 boolean CheckSaveEngineSnapshotToList()
14809 boolean save_snapshot =
14810 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14811 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14812 game.snapshot.changed_action) ||
14813 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14814 game.snapshot.collected_item));
14816 game.snapshot.changed_action = FALSE;
14817 game.snapshot.collected_item = FALSE;
14818 game.snapshot.save_snapshot = save_snapshot;
14820 return save_snapshot;
14823 void SaveEngineSnapshotToList()
14825 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14829 ListNode *buffers = SaveEngineSnapshotBuffers();
14831 /* finally save all snapshot buffers to snapshot list */
14832 SaveSnapshotToList(buffers);
14835 void SaveEngineSnapshotToListInitial()
14837 FreeEngineSnapshotList();
14839 SaveEngineSnapshotToList();
14842 void LoadEngineSnapshotValues()
14844 /* restore special values from snapshot structure */
14846 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14847 LoadEngineSnapshotValues_RND();
14848 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14849 LoadEngineSnapshotValues_EM();
14850 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14851 LoadEngineSnapshotValues_SP();
14854 void LoadEngineSnapshotSingle()
14856 LoadSnapshotSingle();
14858 LoadEngineSnapshotValues();
14861 void LoadEngineSnapshot_Undo(int steps)
14863 LoadSnapshotFromList_Older(steps);
14865 LoadEngineSnapshotValues();
14868 void LoadEngineSnapshot_Redo(int steps)
14870 LoadSnapshotFromList_Newer(steps);
14872 LoadEngineSnapshotValues();
14875 boolean CheckEngineSnapshotSingle()
14877 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14878 snapshot_level_nr == level_nr);
14881 boolean CheckEngineSnapshotList()
14883 return CheckSnapshotList();
14887 /* ---------- new game button stuff ---------------------------------------- */
14895 } gamebutton_info[NUM_GAME_BUTTONS] =
14898 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14899 GAME_CTRL_ID_STOP, "stop game"
14902 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14903 GAME_CTRL_ID_PAUSE, "pause game"
14906 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14907 GAME_CTRL_ID_PLAY, "play game"
14910 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14911 GAME_CTRL_ID_UNDO, "undo step"
14914 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14915 GAME_CTRL_ID_REDO, "redo step"
14918 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14919 GAME_CTRL_ID_SAVE, "save game"
14922 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14923 GAME_CTRL_ID_PAUSE2, "pause game"
14926 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14927 GAME_CTRL_ID_LOAD, "load game"
14930 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14931 SOUND_CTRL_ID_MUSIC, "background music on/off"
14934 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14935 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14938 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14939 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14943 void CreateGameButtons()
14947 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14949 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14950 struct XY *pos = gamebutton_info[i].pos;
14951 struct GadgetInfo *gi;
14954 unsigned int event_mask;
14955 int base_x = (tape.show_game_buttons ? VX : DX);
14956 int base_y = (tape.show_game_buttons ? VY : DY);
14957 int gd_x = gfx->src_x;
14958 int gd_y = gfx->src_y;
14959 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14960 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14961 int gd_xa = gfx->src_x + gfx->active_xoffset;
14962 int gd_ya = gfx->src_y + gfx->active_yoffset;
14963 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14964 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14967 if (gfx->bitmap == NULL)
14969 game_gadget[id] = NULL;
14974 if (id == GAME_CTRL_ID_STOP ||
14975 id == GAME_CTRL_ID_PLAY ||
14976 id == GAME_CTRL_ID_SAVE ||
14977 id == GAME_CTRL_ID_LOAD)
14979 button_type = GD_TYPE_NORMAL_BUTTON;
14981 event_mask = GD_EVENT_RELEASED;
14983 else if (id == GAME_CTRL_ID_UNDO ||
14984 id == GAME_CTRL_ID_REDO)
14986 button_type = GD_TYPE_NORMAL_BUTTON;
14988 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14992 button_type = GD_TYPE_CHECK_BUTTON;
14994 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14995 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14996 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14997 event_mask = GD_EVENT_PRESSED;
15000 gi = CreateGadget(GDI_CUSTOM_ID, id,
15001 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15002 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15003 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15004 GDI_WIDTH, gfx->width,
15005 GDI_HEIGHT, gfx->height,
15006 GDI_TYPE, button_type,
15007 GDI_STATE, GD_BUTTON_UNPRESSED,
15008 GDI_CHECKED, checked,
15009 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15010 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15011 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15012 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15013 GDI_DIRECT_DRAW, FALSE,
15014 GDI_EVENT_MASK, event_mask,
15015 GDI_CALLBACK_ACTION, HandleGameButtons,
15019 Error(ERR_EXIT, "cannot create gadget");
15021 game_gadget[id] = gi;
15025 void FreeGameButtons()
15029 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15030 FreeGadget(game_gadget[i]);
15033 static void UnmapGameButtonsAtSamePosition(int id)
15037 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15039 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15040 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15041 UnmapGadget(game_gadget[i]);
15044 static void UnmapGameButtonsAtSamePosition_All()
15046 if (setup.show_snapshot_buttons)
15048 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15049 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15050 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15054 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15055 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15056 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15060 static void MapGameButtonsAtSamePosition(int id)
15064 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15066 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15067 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15068 MapGadget(game_gadget[i]);
15070 UnmapGameButtonsAtSamePosition_All();
15073 void MapUndoRedoButtons()
15075 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15076 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15078 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15079 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15081 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15084 void UnmapUndoRedoButtons()
15086 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15087 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15089 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15090 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15092 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15095 void MapGameButtons()
15099 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15100 if (i != GAME_CTRL_ID_UNDO &&
15101 i != GAME_CTRL_ID_REDO)
15102 MapGadget(game_gadget[i]);
15104 UnmapGameButtonsAtSamePosition_All();
15106 RedrawGameButtons();
15109 void UnmapGameButtons()
15113 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15114 UnmapGadget(game_gadget[i]);
15117 void RedrawGameButtons()
15121 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15122 RedrawGadget(game_gadget[i]);
15124 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15125 redraw_mask &= ~REDRAW_ALL;
15128 void GameUndoRedoExt()
15130 ClearPlayerAction();
15132 tape.pausing = TRUE;
15135 UpdateAndDisplayGameControlValues();
15137 DrawCompleteVideoDisplay();
15138 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15139 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15140 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15145 void GameUndo(int steps)
15147 if (!CheckEngineSnapshotList())
15150 LoadEngineSnapshot_Undo(steps);
15155 void GameRedo(int steps)
15157 if (!CheckEngineSnapshotList())
15160 LoadEngineSnapshot_Redo(steps);
15165 static void HandleGameButtonsExt(int id, int button)
15167 static boolean game_undo_executed = FALSE;
15168 int steps = BUTTON_STEPSIZE(button);
15169 boolean handle_game_buttons =
15170 (game_status == GAME_MODE_PLAYING ||
15171 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15173 if (!handle_game_buttons)
15178 case GAME_CTRL_ID_STOP:
15179 if (game_status == GAME_MODE_MAIN)
15185 RequestQuitGame(TRUE);
15189 case GAME_CTRL_ID_PAUSE:
15190 case GAME_CTRL_ID_PAUSE2:
15191 if (options.network && game_status == GAME_MODE_PLAYING)
15193 #if defined(NETWORK_AVALIABLE)
15195 SendToServer_ContinuePlaying();
15197 SendToServer_PausePlaying();
15201 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15203 game_undo_executed = FALSE;
15207 case GAME_CTRL_ID_PLAY:
15208 if (game_status == GAME_MODE_MAIN)
15210 StartGameActions(options.network, setup.autorecord, level.random_seed);
15212 else if (tape.pausing)
15214 #if defined(NETWORK_AVALIABLE)
15215 if (options.network)
15216 SendToServer_ContinuePlaying();
15219 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15223 case GAME_CTRL_ID_UNDO:
15224 // Important: When using "save snapshot when collecting an item" mode,
15225 // load last (current) snapshot for first "undo" after pressing "pause"
15226 // (else the last-but-one snapshot would be loaded, because the snapshot
15227 // pointer already points to the last snapshot when pressing "pause",
15228 // which is fine for "every step/move" mode, but not for "every collect")
15229 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15230 !game_undo_executed)
15233 game_undo_executed = TRUE;
15238 case GAME_CTRL_ID_REDO:
15242 case GAME_CTRL_ID_SAVE:
15246 case GAME_CTRL_ID_LOAD:
15250 case SOUND_CTRL_ID_MUSIC:
15251 if (setup.sound_music)
15253 setup.sound_music = FALSE;
15257 else if (audio.music_available)
15259 setup.sound = setup.sound_music = TRUE;
15261 SetAudioMode(setup.sound);
15267 case SOUND_CTRL_ID_LOOPS:
15268 if (setup.sound_loops)
15269 setup.sound_loops = FALSE;
15270 else if (audio.loops_available)
15272 setup.sound = setup.sound_loops = TRUE;
15274 SetAudioMode(setup.sound);
15278 case SOUND_CTRL_ID_SIMPLE:
15279 if (setup.sound_simple)
15280 setup.sound_simple = FALSE;
15281 else if (audio.sound_available)
15283 setup.sound = setup.sound_simple = TRUE;
15285 SetAudioMode(setup.sound);
15294 static void HandleGameButtons(struct GadgetInfo *gi)
15296 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15299 void HandleSoundButtonKeys(Key key)
15302 if (key == setup.shortcut.sound_simple)
15303 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15304 else if (key == setup.shortcut.sound_loops)
15305 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15306 else if (key == setup.shortcut.sound_music)
15307 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);