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 other actions */
831 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
832 #define MOVE_STEPSIZE_MIN (1)
833 #define MOVE_STEPSIZE_MAX (TILEX)
835 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
836 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
838 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
840 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
841 RND(element_info[e].push_delay_random))
842 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
843 RND(element_info[e].drop_delay_random))
844 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
845 RND(element_info[e].move_delay_random))
846 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
847 (element_info[e].move_delay_random))
848 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
849 RND(element_info[e].ce_value_random_initial))
850 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
851 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
852 RND((c)->delay_random * (c)->delay_frames))
853 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
854 RND((c)->delay_random))
857 #define GET_VALID_RUNTIME_ELEMENT(e) \
858 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
860 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
861 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
862 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
863 (be) + (e) - EL_SELF)
865 #define GET_PLAYER_FROM_BITS(p) \
866 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
868 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
869 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
870 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
871 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
872 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
873 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
874 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
875 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
876 RESOLVED_REFERENCE_ELEMENT(be, e) : \
879 #define CAN_GROW_INTO(e) \
880 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
882 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
883 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
886 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
887 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
888 (CAN_MOVE_INTO_ACID(e) && \
889 Feld[x][y] == EL_ACID) || \
892 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
893 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
894 (CAN_MOVE_INTO_ACID(e) && \
895 Feld[x][y] == EL_ACID) || \
898 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
899 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
901 (CAN_MOVE_INTO_ACID(e) && \
902 Feld[x][y] == EL_ACID) || \
903 (DONT_COLLIDE_WITH(e) && \
905 !PLAYER_ENEMY_PROTECTED(x, y))))
907 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
908 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
910 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
911 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
913 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
914 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
916 #define ANDROID_CAN_CLONE_FIELD(x, y) \
917 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
918 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
920 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
921 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
923 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
924 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
926 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
927 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
929 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
930 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
932 #define PIG_CAN_ENTER_FIELD(e, x, y) \
933 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
935 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
936 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
937 Feld[x][y] == EL_EM_EXIT_OPEN || \
938 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
939 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
940 IS_FOOD_PENGUIN(Feld[x][y])))
941 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
944 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
947 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
950 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
952 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
954 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
956 #define CE_ENTER_FIELD_COND(e, x, y) \
957 (!IS_PLAYER(x, y) && \
958 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
960 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
963 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
964 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
966 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
967 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
968 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
969 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
971 /* game button identifiers */
972 #define GAME_CTRL_ID_STOP 0
973 #define GAME_CTRL_ID_PAUSE 1
974 #define GAME_CTRL_ID_PLAY 2
975 #define GAME_CTRL_ID_UNDO 3
976 #define GAME_CTRL_ID_REDO 4
977 #define GAME_CTRL_ID_SAVE 5
978 #define GAME_CTRL_ID_PAUSE2 6
979 #define GAME_CTRL_ID_LOAD 7
980 #define SOUND_CTRL_ID_MUSIC 8
981 #define SOUND_CTRL_ID_LOOPS 9
982 #define SOUND_CTRL_ID_SIMPLE 10
984 #define NUM_GAME_BUTTONS 11
987 /* forward declaration for internal use */
989 static void CreateField(int, int, int);
991 static void ResetGfxAnimation(int, int);
993 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
994 static void AdvanceFrameAndPlayerCounters(int);
996 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
997 static boolean MovePlayer(struct PlayerInfo *, int, int);
998 static void ScrollPlayer(struct PlayerInfo *, int);
999 static void ScrollScreen(struct PlayerInfo *, int);
1001 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1002 static boolean DigFieldByCE(int, int, int);
1003 static boolean SnapField(struct PlayerInfo *, int, int);
1004 static boolean DropElement(struct PlayerInfo *);
1006 static void InitBeltMovement(void);
1007 static void CloseAllOpenTimegates(void);
1008 static void CheckGravityMovement(struct PlayerInfo *);
1009 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1010 static void KillPlayerUnlessEnemyProtected(int, int);
1011 static void KillPlayerUnlessExplosionProtected(int, int);
1013 static void TestIfPlayerTouchesCustomElement(int, int);
1014 static void TestIfElementTouchesCustomElement(int, int);
1015 static void TestIfElementHitsCustomElement(int, int, int);
1017 static void HandleElementChange(int, int, int);
1018 static void ExecuteCustomElementAction(int, int, int, int);
1019 static boolean ChangeElement(int, int, int, int);
1021 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1022 #define CheckTriggeredElementChange(x, y, e, ev) \
1023 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1024 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1025 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1026 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1027 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1028 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1029 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1031 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1032 #define CheckElementChange(x, y, e, te, ev) \
1033 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1034 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1035 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1036 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1037 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1039 static void PlayLevelSound(int, int, int);
1040 static void PlayLevelSoundNearest(int, int, int);
1041 static void PlayLevelSoundAction(int, int, int);
1042 static void PlayLevelSoundElementAction(int, int, int, int);
1043 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1044 static void PlayLevelSoundActionIfLoop(int, int, int);
1045 static void StopLevelSoundActionIfLoop(int, int, int);
1046 static void PlayLevelMusic();
1048 static void HandleGameButtons(struct GadgetInfo *);
1050 int AmoebeNachbarNr(int, int);
1051 void AmoebeUmwandeln(int, int);
1052 void ContinueMoving(int, int);
1053 void Bang(int, int);
1054 void InitMovDir(int, int);
1055 void InitAmoebaNr(int, int);
1056 int NewHiScore(void);
1058 void TestIfGoodThingHitsBadThing(int, int, int);
1059 void TestIfBadThingHitsGoodThing(int, int, int);
1060 void TestIfPlayerTouchesBadThing(int, int);
1061 void TestIfPlayerRunsIntoBadThing(int, int, int);
1062 void TestIfBadThingTouchesPlayer(int, int);
1063 void TestIfBadThingRunsIntoPlayer(int, int, int);
1064 void TestIfFriendTouchesBadThing(int, int);
1065 void TestIfBadThingTouchesFriend(int, int);
1066 void TestIfBadThingTouchesOtherBadThing(int, int);
1067 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1069 void KillPlayer(struct PlayerInfo *);
1070 void BuryPlayer(struct PlayerInfo *);
1071 void RemovePlayer(struct PlayerInfo *);
1073 static int getInvisibleActiveFromInvisibleElement(int);
1074 static int getInvisibleFromInvisibleActiveElement(int);
1076 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1078 /* for detection of endless loops, caused by custom element programming */
1079 /* (using maximal playfield width x 10 is just a rough approximation) */
1080 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1082 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1084 if (recursion_loop_detected) \
1087 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1089 recursion_loop_detected = TRUE; \
1090 recursion_loop_element = (e); \
1093 recursion_loop_depth++; \
1096 #define RECURSION_LOOP_DETECTION_END() \
1098 recursion_loop_depth--; \
1101 static int recursion_loop_depth;
1102 static boolean recursion_loop_detected;
1103 static boolean recursion_loop_element;
1105 static int map_player_action[MAX_PLAYERS];
1108 /* ------------------------------------------------------------------------- */
1109 /* definition of elements that automatically change to other elements after */
1110 /* a specified time, eventually calling a function when changing */
1111 /* ------------------------------------------------------------------------- */
1113 /* forward declaration for changer functions */
1114 static void InitBuggyBase(int, int);
1115 static void WarnBuggyBase(int, int);
1117 static void InitTrap(int, int);
1118 static void ActivateTrap(int, int);
1119 static void ChangeActiveTrap(int, int);
1121 static void InitRobotWheel(int, int);
1122 static void RunRobotWheel(int, int);
1123 static void StopRobotWheel(int, int);
1125 static void InitTimegateWheel(int, int);
1126 static void RunTimegateWheel(int, int);
1128 static void InitMagicBallDelay(int, int);
1129 static void ActivateMagicBall(int, int);
1131 struct ChangingElementInfo
1136 void (*pre_change_function)(int x, int y);
1137 void (*change_function)(int x, int y);
1138 void (*post_change_function)(int x, int y);
1141 static struct ChangingElementInfo change_delay_list[] =
1176 EL_STEEL_EXIT_OPENING,
1184 EL_STEEL_EXIT_CLOSING,
1185 EL_STEEL_EXIT_CLOSED,
1208 EL_EM_STEEL_EXIT_OPENING,
1209 EL_EM_STEEL_EXIT_OPEN,
1216 EL_EM_STEEL_EXIT_CLOSING,
1240 EL_SWITCHGATE_OPENING,
1248 EL_SWITCHGATE_CLOSING,
1249 EL_SWITCHGATE_CLOSED,
1256 EL_TIMEGATE_OPENING,
1264 EL_TIMEGATE_CLOSING,
1273 EL_ACID_SPLASH_LEFT,
1281 EL_ACID_SPLASH_RIGHT,
1290 EL_SP_BUGGY_BASE_ACTIVATING,
1297 EL_SP_BUGGY_BASE_ACTIVATING,
1298 EL_SP_BUGGY_BASE_ACTIVE,
1305 EL_SP_BUGGY_BASE_ACTIVE,
1329 EL_ROBOT_WHEEL_ACTIVE,
1337 EL_TIMEGATE_SWITCH_ACTIVE,
1345 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1346 EL_DC_TIMEGATE_SWITCH,
1353 EL_EMC_MAGIC_BALL_ACTIVE,
1354 EL_EMC_MAGIC_BALL_ACTIVE,
1361 EL_EMC_SPRING_BUMPER_ACTIVE,
1362 EL_EMC_SPRING_BUMPER,
1369 EL_DIAGONAL_SHRINKING,
1377 EL_DIAGONAL_GROWING,
1398 int push_delay_fixed, push_delay_random;
1402 { EL_SPRING, 0, 0 },
1403 { EL_BALLOON, 0, 0 },
1405 { EL_SOKOBAN_OBJECT, 2, 0 },
1406 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1407 { EL_SATELLITE, 2, 0 },
1408 { EL_SP_DISK_YELLOW, 2, 0 },
1410 { EL_UNDEFINED, 0, 0 },
1418 move_stepsize_list[] =
1420 { EL_AMOEBA_DROP, 2 },
1421 { EL_AMOEBA_DROPPING, 2 },
1422 { EL_QUICKSAND_FILLING, 1 },
1423 { EL_QUICKSAND_EMPTYING, 1 },
1424 { EL_QUICKSAND_FAST_FILLING, 2 },
1425 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1426 { EL_MAGIC_WALL_FILLING, 2 },
1427 { EL_MAGIC_WALL_EMPTYING, 2 },
1428 { EL_BD_MAGIC_WALL_FILLING, 2 },
1429 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1430 { EL_DC_MAGIC_WALL_FILLING, 2 },
1431 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1433 { EL_UNDEFINED, 0 },
1441 collect_count_list[] =
1444 { EL_BD_DIAMOND, 1 },
1445 { EL_EMERALD_YELLOW, 1 },
1446 { EL_EMERALD_RED, 1 },
1447 { EL_EMERALD_PURPLE, 1 },
1449 { EL_SP_INFOTRON, 1 },
1453 { EL_UNDEFINED, 0 },
1461 access_direction_list[] =
1463 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1464 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1465 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1466 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1467 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1468 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1469 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1470 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1471 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1472 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1473 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1475 { EL_SP_PORT_LEFT, MV_RIGHT },
1476 { EL_SP_PORT_RIGHT, MV_LEFT },
1477 { EL_SP_PORT_UP, MV_DOWN },
1478 { EL_SP_PORT_DOWN, MV_UP },
1479 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1480 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1481 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1482 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1483 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1484 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1485 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1486 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1487 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1488 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1489 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1490 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1491 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1492 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1493 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1495 { EL_UNDEFINED, MV_NONE }
1498 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1500 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1501 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1502 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1503 IS_JUST_CHANGING(x, y))
1505 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1507 /* static variables for playfield scan mode (scanning forward or backward) */
1508 static int playfield_scan_start_x = 0;
1509 static int playfield_scan_start_y = 0;
1510 static int playfield_scan_delta_x = 1;
1511 static int playfield_scan_delta_y = 1;
1513 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1514 (y) >= 0 && (y) <= lev_fieldy - 1; \
1515 (y) += playfield_scan_delta_y) \
1516 for ((x) = playfield_scan_start_x; \
1517 (x) >= 0 && (x) <= lev_fieldx - 1; \
1518 (x) += playfield_scan_delta_x)
1521 void DEBUG_SetMaximumDynamite()
1525 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1526 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1527 local_player->inventory_element[local_player->inventory_size++] =
1532 static void InitPlayfieldScanModeVars()
1534 if (game.use_reverse_scan_direction)
1536 playfield_scan_start_x = lev_fieldx - 1;
1537 playfield_scan_start_y = lev_fieldy - 1;
1539 playfield_scan_delta_x = -1;
1540 playfield_scan_delta_y = -1;
1544 playfield_scan_start_x = 0;
1545 playfield_scan_start_y = 0;
1547 playfield_scan_delta_x = 1;
1548 playfield_scan_delta_y = 1;
1552 static void InitPlayfieldScanMode(int mode)
1554 game.use_reverse_scan_direction =
1555 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1557 InitPlayfieldScanModeVars();
1560 static int get_move_delay_from_stepsize(int move_stepsize)
1563 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1565 /* make sure that stepsize value is always a power of 2 */
1566 move_stepsize = (1 << log_2(move_stepsize));
1568 return TILEX / move_stepsize;
1571 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1574 int player_nr = player->index_nr;
1575 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1576 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1578 /* do no immediately change move delay -- the player might just be moving */
1579 player->move_delay_value_next = move_delay;
1581 /* information if player can move must be set separately */
1582 player->cannot_move = cannot_move;
1586 player->move_delay = game.initial_move_delay[player_nr];
1587 player->move_delay_value = game.initial_move_delay_value[player_nr];
1589 player->move_delay_value_next = -1;
1591 player->move_delay_reset_counter = 0;
1595 void GetPlayerConfig()
1597 GameFrameDelay = setup.game_frame_delay;
1599 if (!audio.sound_available)
1600 setup.sound_simple = FALSE;
1602 if (!audio.loops_available)
1603 setup.sound_loops = FALSE;
1605 if (!audio.music_available)
1606 setup.sound_music = FALSE;
1608 if (!video.fullscreen_available)
1609 setup.fullscreen = FALSE;
1611 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1613 SetAudioMode(setup.sound);
1617 int GetElementFromGroupElement(int element)
1619 if (IS_GROUP_ELEMENT(element))
1621 struct ElementGroupInfo *group = element_info[element].group;
1622 int last_anim_random_frame = gfx.anim_random_frame;
1625 if (group->choice_mode == ANIM_RANDOM)
1626 gfx.anim_random_frame = RND(group->num_elements_resolved);
1628 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1629 group->choice_mode, 0,
1632 if (group->choice_mode == ANIM_RANDOM)
1633 gfx.anim_random_frame = last_anim_random_frame;
1635 group->choice_pos++;
1637 element = group->element_resolved[element_pos];
1643 static void InitPlayerField(int x, int y, int element, boolean init_game)
1645 if (element == EL_SP_MURPHY)
1649 if (stored_player[0].present)
1651 Feld[x][y] = EL_SP_MURPHY_CLONE;
1657 stored_player[0].initial_element = element;
1658 stored_player[0].use_murphy = TRUE;
1660 if (!level.use_artwork_element[0])
1661 stored_player[0].artwork_element = EL_SP_MURPHY;
1664 Feld[x][y] = EL_PLAYER_1;
1670 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1671 int jx = player->jx, jy = player->jy;
1673 player->present = TRUE;
1675 player->block_last_field = (element == EL_SP_MURPHY ?
1676 level.sp_block_last_field :
1677 level.block_last_field);
1679 /* ---------- initialize player's last field block delay --------------- */
1681 /* always start with reliable default value (no adjustment needed) */
1682 player->block_delay_adjustment = 0;
1684 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1685 if (player->block_last_field && element == EL_SP_MURPHY)
1686 player->block_delay_adjustment = 1;
1688 /* special case 2: in game engines before 3.1.1, blocking was different */
1689 if (game.use_block_last_field_bug)
1690 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1692 if (!options.network || player->connected)
1694 player->active = TRUE;
1696 /* remove potentially duplicate players */
1697 if (StorePlayer[jx][jy] == Feld[x][y])
1698 StorePlayer[jx][jy] = 0;
1700 StorePlayer[x][y] = Feld[x][y];
1702 #if DEBUG_INIT_PLAYER
1705 printf("- player element %d activated", player->element_nr);
1706 printf(" (local player is %d and currently %s)\n",
1707 local_player->element_nr,
1708 local_player->active ? "active" : "not active");
1713 Feld[x][y] = EL_EMPTY;
1715 player->jx = player->last_jx = x;
1716 player->jy = player->last_jy = y;
1721 int player_nr = GET_PLAYER_NR(element);
1722 struct PlayerInfo *player = &stored_player[player_nr];
1724 if (player->active && player->killed)
1725 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1729 static void InitField(int x, int y, boolean init_game)
1731 int element = Feld[x][y];
1740 InitPlayerField(x, y, element, init_game);
1743 case EL_SOKOBAN_FIELD_PLAYER:
1744 element = Feld[x][y] = EL_PLAYER_1;
1745 InitField(x, y, init_game);
1747 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1748 InitField(x, y, init_game);
1751 case EL_SOKOBAN_FIELD_EMPTY:
1752 local_player->sokobanfields_still_needed++;
1756 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1757 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1758 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1759 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1760 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1761 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1762 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1763 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1764 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1765 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1774 case EL_SPACESHIP_RIGHT:
1775 case EL_SPACESHIP_UP:
1776 case EL_SPACESHIP_LEFT:
1777 case EL_SPACESHIP_DOWN:
1778 case EL_BD_BUTTERFLY:
1779 case EL_BD_BUTTERFLY_RIGHT:
1780 case EL_BD_BUTTERFLY_UP:
1781 case EL_BD_BUTTERFLY_LEFT:
1782 case EL_BD_BUTTERFLY_DOWN:
1784 case EL_BD_FIREFLY_RIGHT:
1785 case EL_BD_FIREFLY_UP:
1786 case EL_BD_FIREFLY_LEFT:
1787 case EL_BD_FIREFLY_DOWN:
1788 case EL_PACMAN_RIGHT:
1790 case EL_PACMAN_LEFT:
1791 case EL_PACMAN_DOWN:
1793 case EL_YAMYAM_LEFT:
1794 case EL_YAMYAM_RIGHT:
1796 case EL_YAMYAM_DOWN:
1797 case EL_DARK_YAMYAM:
1800 case EL_SP_SNIKSNAK:
1801 case EL_SP_ELECTRON:
1810 case EL_AMOEBA_FULL:
1815 case EL_AMOEBA_DROP:
1816 if (y == lev_fieldy - 1)
1818 Feld[x][y] = EL_AMOEBA_GROWING;
1819 Store[x][y] = EL_AMOEBA_WET;
1823 case EL_DYNAMITE_ACTIVE:
1824 case EL_SP_DISK_RED_ACTIVE:
1825 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1826 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1827 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1828 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1829 MovDelay[x][y] = 96;
1832 case EL_EM_DYNAMITE_ACTIVE:
1833 MovDelay[x][y] = 32;
1837 local_player->lights_still_needed++;
1841 local_player->friends_still_needed++;
1846 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1849 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1850 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1851 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1852 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1853 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1854 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1855 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1856 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1857 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1858 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1859 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1860 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1863 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1864 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1865 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1867 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1869 game.belt_dir[belt_nr] = belt_dir;
1870 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1872 else /* more than one switch -- set it like the first switch */
1874 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1879 case EL_LIGHT_SWITCH_ACTIVE:
1881 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1884 case EL_INVISIBLE_STEELWALL:
1885 case EL_INVISIBLE_WALL:
1886 case EL_INVISIBLE_SAND:
1887 if (game.light_time_left > 0 ||
1888 game.lenses_time_left > 0)
1889 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1892 case EL_EMC_MAGIC_BALL:
1893 if (game.ball_state)
1894 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1897 case EL_EMC_MAGIC_BALL_SWITCH:
1898 if (game.ball_state)
1899 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1902 case EL_TRIGGER_PLAYER:
1903 case EL_TRIGGER_ELEMENT:
1904 case EL_TRIGGER_CE_VALUE:
1905 case EL_TRIGGER_CE_SCORE:
1907 case EL_ANY_ELEMENT:
1908 case EL_CURRENT_CE_VALUE:
1909 case EL_CURRENT_CE_SCORE:
1926 /* reference elements should not be used on the playfield */
1927 Feld[x][y] = EL_EMPTY;
1931 if (IS_CUSTOM_ELEMENT(element))
1933 if (CAN_MOVE(element))
1936 if (!element_info[element].use_last_ce_value || init_game)
1937 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1939 else if (IS_GROUP_ELEMENT(element))
1941 Feld[x][y] = GetElementFromGroupElement(element);
1943 InitField(x, y, init_game);
1950 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1953 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1955 InitField(x, y, init_game);
1957 /* not needed to call InitMovDir() -- already done by InitField()! */
1958 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1959 CAN_MOVE(Feld[x][y]))
1963 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1965 int old_element = Feld[x][y];
1967 InitField(x, y, init_game);
1969 /* not needed to call InitMovDir() -- already done by InitField()! */
1970 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971 CAN_MOVE(old_element) &&
1972 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1975 /* this case is in fact a combination of not less than three bugs:
1976 first, it calls InitMovDir() for elements that can move, although this is
1977 already done by InitField(); then, it checks the element that was at this
1978 field _before_ the call to InitField() (which can change it); lastly, it
1979 was not called for "mole with direction" elements, which were treated as
1980 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1984 static int get_key_element_from_nr(int key_nr)
1986 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1987 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1988 EL_EM_KEY_1 : EL_KEY_1);
1990 return key_base_element + key_nr;
1993 static int get_next_dropped_element(struct PlayerInfo *player)
1995 return (player->inventory_size > 0 ?
1996 player->inventory_element[player->inventory_size - 1] :
1997 player->inventory_infinite_element != EL_UNDEFINED ?
1998 player->inventory_infinite_element :
1999 player->dynabombs_left > 0 ?
2000 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2004 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2006 /* pos >= 0: get element from bottom of the stack;
2007 pos < 0: get element from top of the stack */
2011 int min_inventory_size = -pos;
2012 int inventory_pos = player->inventory_size - min_inventory_size;
2013 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2015 return (player->inventory_size >= min_inventory_size ?
2016 player->inventory_element[inventory_pos] :
2017 player->inventory_infinite_element != EL_UNDEFINED ?
2018 player->inventory_infinite_element :
2019 player->dynabombs_left >= min_dynabombs_left ?
2020 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2025 int min_dynabombs_left = pos + 1;
2026 int min_inventory_size = pos + 1 - player->dynabombs_left;
2027 int inventory_pos = pos - player->dynabombs_left;
2029 return (player->inventory_infinite_element != EL_UNDEFINED ?
2030 player->inventory_infinite_element :
2031 player->dynabombs_left >= min_dynabombs_left ?
2032 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2033 player->inventory_size >= min_inventory_size ?
2034 player->inventory_element[inventory_pos] :
2039 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2041 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2042 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2045 if (gpo1->sort_priority != gpo2->sort_priority)
2046 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2048 compare_result = gpo1->nr - gpo2->nr;
2050 return compare_result;
2053 int getPlayerInventorySize(int player_nr)
2055 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2056 return level.native_em_level->ply[player_nr]->dynamite;
2057 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2058 return level.native_sp_level->game_sp->red_disk_count;
2060 return stored_player[player_nr].inventory_size;
2063 void InitGameControlValues()
2067 for (i = 0; game_panel_controls[i].nr != -1; i++)
2069 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2070 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2071 struct TextPosInfo *pos = gpc->pos;
2073 int type = gpc->type;
2077 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2078 Error(ERR_EXIT, "this should not happen -- please debug");
2081 /* force update of game controls after initialization */
2082 gpc->value = gpc->last_value = -1;
2083 gpc->frame = gpc->last_frame = -1;
2084 gpc->gfx_frame = -1;
2086 /* determine panel value width for later calculation of alignment */
2087 if (type == TYPE_INTEGER || type == TYPE_STRING)
2089 pos->width = pos->size * getFontWidth(pos->font);
2090 pos->height = getFontHeight(pos->font);
2092 else if (type == TYPE_ELEMENT)
2094 pos->width = pos->size;
2095 pos->height = pos->size;
2098 /* fill structure for game panel draw order */
2100 gpo->sort_priority = pos->sort_priority;
2103 /* sort game panel controls according to sort_priority and control number */
2104 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2105 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2108 void UpdatePlayfieldElementCount()
2110 boolean use_element_count = FALSE;
2113 /* first check if it is needed at all to calculate playfield element count */
2114 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2115 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2116 use_element_count = TRUE;
2118 if (!use_element_count)
2121 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2122 element_info[i].element_count = 0;
2124 SCAN_PLAYFIELD(x, y)
2126 element_info[Feld[x][y]].element_count++;
2129 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2130 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2131 if (IS_IN_GROUP(j, i))
2132 element_info[EL_GROUP_START + i].element_count +=
2133 element_info[j].element_count;
2136 void UpdateGameControlValues()
2139 int time = (local_player->LevelSolved ?
2140 local_player->LevelSolved_CountingTime :
2141 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142 level.native_em_level->lev->time :
2143 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144 level.native_sp_level->game_sp->time_played :
2145 game.no_time_limit ? TimePlayed : TimeLeft);
2146 int score = (local_player->LevelSolved ?
2147 local_player->LevelSolved_CountingScore :
2148 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149 level.native_em_level->lev->score :
2150 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151 level.native_sp_level->game_sp->score :
2152 local_player->score);
2153 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154 level.native_em_level->lev->required :
2155 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2156 level.native_sp_level->game_sp->infotrons_still_needed :
2157 local_player->gems_still_needed);
2158 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2159 level.native_em_level->lev->required > 0 :
2160 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2161 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2162 local_player->gems_still_needed > 0 ||
2163 local_player->sokobanfields_still_needed > 0 ||
2164 local_player->lights_still_needed > 0);
2166 UpdatePlayfieldElementCount();
2168 /* update game panel control values */
2170 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2171 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2173 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2174 for (i = 0; i < MAX_NUM_KEYS; i++)
2175 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2176 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2177 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2179 if (game.centered_player_nr == -1)
2181 for (i = 0; i < MAX_PLAYERS; i++)
2183 /* only one player in Supaplex game engine */
2184 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2187 for (k = 0; k < MAX_NUM_KEYS; k++)
2189 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2191 if (level.native_em_level->ply[i]->keys & (1 << k))
2192 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2193 get_key_element_from_nr(k);
2195 else if (stored_player[i].key[k])
2196 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2197 get_key_element_from_nr(k);
2200 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2201 getPlayerInventorySize(i);
2203 if (stored_player[i].num_white_keys > 0)
2204 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2207 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2208 stored_player[i].num_white_keys;
2213 int player_nr = game.centered_player_nr;
2215 for (k = 0; k < MAX_NUM_KEYS; k++)
2217 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2219 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2220 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221 get_key_element_from_nr(k);
2223 else if (stored_player[player_nr].key[k])
2224 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2225 get_key_element_from_nr(k);
2228 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229 getPlayerInventorySize(player_nr);
2231 if (stored_player[player_nr].num_white_keys > 0)
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2234 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235 stored_player[player_nr].num_white_keys;
2238 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2240 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241 get_inventory_element_from_pos(local_player, i);
2242 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243 get_inventory_element_from_pos(local_player, -i - 1);
2246 game_panel_controls[GAME_PANEL_SCORE].value = score;
2247 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2249 game_panel_controls[GAME_PANEL_TIME].value = time;
2251 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2252 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2253 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2255 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2257 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2258 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2260 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2261 local_player->shield_normal_time_left;
2262 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2263 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2265 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2266 local_player->shield_deadly_time_left;
2268 game_panel_controls[GAME_PANEL_EXIT].value =
2269 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2271 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2272 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2273 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2274 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2275 EL_EMC_MAGIC_BALL_SWITCH);
2277 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2278 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2279 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2280 game.light_time_left;
2282 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2283 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2284 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2285 game.timegate_time_left;
2287 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2288 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2290 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2291 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2292 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2293 game.lenses_time_left;
2295 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2296 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2297 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2298 game.magnify_time_left;
2300 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2301 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2302 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2303 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2304 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2305 EL_BALLOON_SWITCH_NONE);
2307 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2308 local_player->dynabomb_count;
2309 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2310 local_player->dynabomb_size;
2311 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2312 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2314 game_panel_controls[GAME_PANEL_PENGUINS].value =
2315 local_player->friends_still_needed;
2317 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2318 local_player->sokobanfields_still_needed;
2319 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2320 local_player->sokobanfields_still_needed;
2322 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2323 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2325 for (i = 0; i < NUM_BELTS; i++)
2327 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2328 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2329 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2330 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2331 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2335 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2336 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2337 game.magic_wall_time_left;
2339 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2340 local_player->gravity;
2342 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2343 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2345 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2346 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2347 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2348 game.panel.element[i].id : EL_UNDEFINED);
2350 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2351 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2352 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2353 element_info[game.panel.element_count[i].id].element_count : 0);
2355 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2356 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2357 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2358 element_info[game.panel.ce_score[i].id].collect_score : 0);
2360 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2361 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2362 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2363 element_info[game.panel.ce_score_element[i].id].collect_score :
2366 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2367 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2368 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2370 /* update game panel control frames */
2372 for (i = 0; game_panel_controls[i].nr != -1; i++)
2374 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2376 if (gpc->type == TYPE_ELEMENT)
2378 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2380 int last_anim_random_frame = gfx.anim_random_frame;
2381 int element = gpc->value;
2382 int graphic = el2panelimg(element);
2384 if (gpc->value != gpc->last_value)
2387 gpc->gfx_random = INIT_GFX_RANDOM();
2393 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2394 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2395 gpc->gfx_random = INIT_GFX_RANDOM();
2398 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399 gfx.anim_random_frame = gpc->gfx_random;
2401 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2402 gpc->gfx_frame = element_info[element].collect_score;
2404 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2408 gfx.anim_random_frame = last_anim_random_frame;
2414 void DisplayGameControlValues()
2416 boolean redraw_panel = FALSE;
2419 for (i = 0; game_panel_controls[i].nr != -1; i++)
2421 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2423 if (PANEL_DEACTIVATED(gpc->pos))
2426 if (gpc->value == gpc->last_value &&
2427 gpc->frame == gpc->last_frame)
2430 redraw_panel = TRUE;
2436 /* copy default game door content to main double buffer */
2438 /* !!! CHECK AGAIN !!! */
2439 SetPanelBackground();
2440 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2441 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2443 /* redraw game control buttons */
2444 RedrawGameButtons();
2446 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2448 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2450 int nr = game_panel_order[i].nr;
2451 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2452 struct TextPosInfo *pos = gpc->pos;
2453 int type = gpc->type;
2454 int value = gpc->value;
2455 int frame = gpc->frame;
2456 int size = pos->size;
2457 int font = pos->font;
2458 boolean draw_masked = pos->draw_masked;
2459 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2461 if (PANEL_DEACTIVATED(pos))
2464 gpc->last_value = value;
2465 gpc->last_frame = frame;
2467 if (type == TYPE_INTEGER)
2469 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2470 nr == GAME_PANEL_TIME)
2472 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2474 if (use_dynamic_size) /* use dynamic number of digits */
2476 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2477 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2478 int size2 = size1 + 1;
2479 int font1 = pos->font;
2480 int font2 = pos->font_alt;
2482 size = (value < value_change ? size1 : size2);
2483 font = (value < value_change ? font1 : font2);
2487 /* correct text size if "digits" is zero or less */
2489 size = strlen(int2str(value, size));
2491 /* dynamically correct text alignment */
2492 pos->width = size * getFontWidth(font);
2494 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2495 int2str(value, size), font, mask_mode);
2497 else if (type == TYPE_ELEMENT)
2499 int element, graphic;
2503 int dst_x = PANEL_XPOS(pos);
2504 int dst_y = PANEL_YPOS(pos);
2506 if (value != EL_UNDEFINED && value != EL_EMPTY)
2509 graphic = el2panelimg(value);
2511 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2513 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519 width = graphic_info[graphic].width * size / TILESIZE;
2520 height = graphic_info[graphic].height * size / TILESIZE;
2523 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530 else if (type == TYPE_STRING)
2532 boolean active = (value != 0);
2533 char *state_normal = "off";
2534 char *state_active = "on";
2535 char *state = (active ? state_active : state_normal);
2536 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2537 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2538 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2539 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2541 if (nr == GAME_PANEL_GRAVITY_STATE)
2543 int font1 = pos->font; /* (used for normal state) */
2544 int font2 = pos->font_alt; /* (used for active state) */
2546 font = (active ? font2 : font1);
2555 /* don't truncate output if "chars" is zero or less */
2558 /* dynamically correct text alignment */
2559 pos->width = size * getFontWidth(font);
2562 s_cut = getStringCopyN(s, size);
2564 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2565 s_cut, font, mask_mode);
2571 redraw_mask |= REDRAW_DOOR_1;
2574 SetGameStatus(GAME_MODE_PLAYING);
2577 void UpdateAndDisplayGameControlValues()
2579 if (tape.deactivate_display)
2582 UpdateGameControlValues();
2583 DisplayGameControlValues();
2586 void UpdateGameDoorValues()
2588 UpdateGameControlValues();
2591 void DrawGameDoorValues()
2593 DisplayGameControlValues();
2598 =============================================================================
2600 -----------------------------------------------------------------------------
2601 initialize game engine due to level / tape version number
2602 =============================================================================
2605 static void InitGameEngine()
2607 int i, j, k, l, x, y;
2609 /* set game engine from tape file when re-playing, else from level file */
2610 game.engine_version = (tape.playing ? tape.engine_version :
2611 level.game_version);
2613 /* set single or multi-player game mode (needed for re-playing tapes) */
2614 game.team_mode = setup.team_mode;
2618 int num_players = 0;
2620 for (i = 0; i < MAX_PLAYERS; i++)
2621 if (tape.player_participates[i])
2624 /* multi-player tapes contain input data for more than one player */
2625 game.team_mode = (num_players > 1);
2628 /* ---------------------------------------------------------------------- */
2629 /* set flags for bugs and changes according to active game engine version */
2630 /* ---------------------------------------------------------------------- */
2633 Summary of bugfix/change:
2634 Fixed handling for custom elements that change when pushed by the player.
2636 Fixed/changed in version:
2640 Before 3.1.0, custom elements that "change when pushing" changed directly
2641 after the player started pushing them (until then handled in "DigField()").
2642 Since 3.1.0, these custom elements are not changed until the "pushing"
2643 move of the element is finished (now handled in "ContinueMoving()").
2645 Affected levels/tapes:
2646 The first condition is generally needed for all levels/tapes before version
2647 3.1.0, which might use the old behaviour before it was changed; known tapes
2648 that are affected are some tapes from the level set "Walpurgis Gardens" by
2650 The second condition is an exception from the above case and is needed for
2651 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2652 above (including some development versions of 3.1.0), but before it was
2653 known that this change would break tapes like the above and was fixed in
2654 3.1.1, so that the changed behaviour was active although the engine version
2655 while recording maybe was before 3.1.0. There is at least one tape that is
2656 affected by this exception, which is the tape for the one-level set "Bug
2657 Machine" by Juergen Bonhagen.
2660 game.use_change_when_pushing_bug =
2661 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2663 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2664 tape.game_version < VERSION_IDENT(3,1,1,0)));
2667 Summary of bugfix/change:
2668 Fixed handling for blocking the field the player leaves when moving.
2670 Fixed/changed in version:
2674 Before 3.1.1, when "block last field when moving" was enabled, the field
2675 the player is leaving when moving was blocked for the time of the move,
2676 and was directly unblocked afterwards. This resulted in the last field
2677 being blocked for exactly one less than the number of frames of one player
2678 move. Additionally, even when blocking was disabled, the last field was
2679 blocked for exactly one frame.
2680 Since 3.1.1, due to changes in player movement handling, the last field
2681 is not blocked at all when blocking is disabled. When blocking is enabled,
2682 the last field is blocked for exactly the number of frames of one player
2683 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2684 last field is blocked for exactly one more than the number of frames of
2687 Affected levels/tapes:
2688 (!!! yet to be determined -- probably many !!!)
2691 game.use_block_last_field_bug =
2692 (game.engine_version < VERSION_IDENT(3,1,1,0));
2694 /* ---------------------------------------------------------------------- */
2696 /* set maximal allowed number of custom element changes per game frame */
2697 game.max_num_changes_per_frame = 1;
2699 /* default scan direction: scan playfield from top/left to bottom/right */
2700 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2702 /* dynamically adjust element properties according to game engine version */
2703 InitElementPropertiesEngine(game.engine_version);
2706 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2707 printf(" tape version == %06d [%s] [file: %06d]\n",
2708 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2710 printf(" => game.engine_version == %06d\n", game.engine_version);
2713 /* ---------- initialize player's initial move delay --------------------- */
2715 /* dynamically adjust player properties according to level information */
2716 for (i = 0; i < MAX_PLAYERS; i++)
2717 game.initial_move_delay_value[i] =
2718 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2720 /* dynamically adjust player properties according to game engine version */
2721 for (i = 0; i < MAX_PLAYERS; i++)
2722 game.initial_move_delay[i] =
2723 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2724 game.initial_move_delay_value[i] : 0);
2726 /* ---------- initialize player's initial push delay --------------------- */
2728 /* dynamically adjust player properties according to game engine version */
2729 game.initial_push_delay_value =
2730 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2732 /* ---------- initialize changing elements ------------------------------- */
2734 /* initialize changing elements information */
2735 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2737 struct ElementInfo *ei = &element_info[i];
2739 /* this pointer might have been changed in the level editor */
2740 ei->change = &ei->change_page[0];
2742 if (!IS_CUSTOM_ELEMENT(i))
2744 ei->change->target_element = EL_EMPTY_SPACE;
2745 ei->change->delay_fixed = 0;
2746 ei->change->delay_random = 0;
2747 ei->change->delay_frames = 1;
2750 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2752 ei->has_change_event[j] = FALSE;
2754 ei->event_page_nr[j] = 0;
2755 ei->event_page[j] = &ei->change_page[0];
2759 /* add changing elements from pre-defined list */
2760 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2762 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2763 struct ElementInfo *ei = &element_info[ch_delay->element];
2765 ei->change->target_element = ch_delay->target_element;
2766 ei->change->delay_fixed = ch_delay->change_delay;
2768 ei->change->pre_change_function = ch_delay->pre_change_function;
2769 ei->change->change_function = ch_delay->change_function;
2770 ei->change->post_change_function = ch_delay->post_change_function;
2772 ei->change->can_change = TRUE;
2773 ei->change->can_change_or_has_action = TRUE;
2775 ei->has_change_event[CE_DELAY] = TRUE;
2777 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2778 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781 /* ---------- initialize internal run-time variables --------------------- */
2783 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2785 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2787 for (j = 0; j < ei->num_change_pages; j++)
2789 ei->change_page[j].can_change_or_has_action =
2790 (ei->change_page[j].can_change |
2791 ei->change_page[j].has_action);
2795 /* add change events from custom element configuration */
2796 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2798 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2800 for (j = 0; j < ei->num_change_pages; j++)
2802 if (!ei->change_page[j].can_change_or_has_action)
2805 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2807 /* only add event page for the first page found with this event */
2808 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2810 ei->has_change_event[k] = TRUE;
2812 ei->event_page_nr[k] = j;
2813 ei->event_page[k] = &ei->change_page[j];
2819 /* ---------- initialize reference elements in change conditions --------- */
2821 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2823 int element = EL_CUSTOM_START + i;
2824 struct ElementInfo *ei = &element_info[element];
2826 for (j = 0; j < ei->num_change_pages; j++)
2828 int trigger_element = ei->change_page[j].initial_trigger_element;
2830 if (trigger_element >= EL_PREV_CE_8 &&
2831 trigger_element <= EL_NEXT_CE_8)
2832 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2834 ei->change_page[j].trigger_element = trigger_element;
2838 /* ---------- initialize run-time trigger player and element ------------- */
2840 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2842 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2844 for (j = 0; j < ei->num_change_pages; j++)
2846 ei->change_page[j].actual_trigger_element = EL_EMPTY;
2847 ei->change_page[j].actual_trigger_player = EL_EMPTY;
2848 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2849 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2850 ei->change_page[j].actual_trigger_ce_value = 0;
2851 ei->change_page[j].actual_trigger_ce_score = 0;
2855 /* ---------- initialize trigger events ---------------------------------- */
2857 /* initialize trigger events information */
2858 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2859 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2860 trigger_events[i][j] = FALSE;
2862 /* add trigger events from element change event properties */
2863 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2865 struct ElementInfo *ei = &element_info[i];
2867 for (j = 0; j < ei->num_change_pages; j++)
2869 if (!ei->change_page[j].can_change_or_has_action)
2872 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2874 int trigger_element = ei->change_page[j].trigger_element;
2876 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2878 if (ei->change_page[j].has_event[k])
2880 if (IS_GROUP_ELEMENT(trigger_element))
2882 struct ElementGroupInfo *group =
2883 element_info[trigger_element].group;
2885 for (l = 0; l < group->num_elements_resolved; l++)
2886 trigger_events[group->element_resolved[l]][k] = TRUE;
2888 else if (trigger_element == EL_ANY_ELEMENT)
2889 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2890 trigger_events[l][k] = TRUE;
2892 trigger_events[trigger_element][k] = TRUE;
2899 /* ---------- initialize push delay -------------------------------------- */
2901 /* initialize push delay values to default */
2902 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2904 if (!IS_CUSTOM_ELEMENT(i))
2906 /* set default push delay values (corrected since version 3.0.7-1) */
2907 if (game.engine_version < VERSION_IDENT(3,0,7,1))
2909 element_info[i].push_delay_fixed = 2;
2910 element_info[i].push_delay_random = 8;
2914 element_info[i].push_delay_fixed = 8;
2915 element_info[i].push_delay_random = 8;
2920 /* set push delay value for certain elements from pre-defined list */
2921 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2923 int e = push_delay_list[i].element;
2925 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
2926 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929 /* set push delay value for Supaplex elements for newer engine versions */
2930 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2932 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2934 if (IS_SP_ELEMENT(i))
2936 /* set SP push delay to just enough to push under a falling zonk */
2937 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2939 element_info[i].push_delay_fixed = delay;
2940 element_info[i].push_delay_random = 0;
2945 /* ---------- initialize move stepsize ----------------------------------- */
2947 /* initialize move stepsize values to default */
2948 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2949 if (!IS_CUSTOM_ELEMENT(i))
2950 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2952 /* set move stepsize value for certain elements from pre-defined list */
2953 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2955 int e = move_stepsize_list[i].element;
2957 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960 /* ---------- initialize collect score ----------------------------------- */
2962 /* initialize collect score values for custom elements from initial value */
2963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2964 if (IS_CUSTOM_ELEMENT(i))
2965 element_info[i].collect_score = element_info[i].collect_score_initial;
2967 /* ---------- initialize collect count ----------------------------------- */
2969 /* initialize collect count values for non-custom elements */
2970 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2971 if (!IS_CUSTOM_ELEMENT(i))
2972 element_info[i].collect_count_initial = 0;
2974 /* add collect count values for all elements from pre-defined list */
2975 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2976 element_info[collect_count_list[i].element].collect_count_initial =
2977 collect_count_list[i].count;
2979 /* ---------- initialize access direction -------------------------------- */
2981 /* initialize access direction values to default (access from every side) */
2982 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2983 if (!IS_CUSTOM_ELEMENT(i))
2984 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2986 /* set access direction value for certain elements from pre-defined list */
2987 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2988 element_info[access_direction_list[i].element].access_direction =
2989 access_direction_list[i].direction;
2991 /* ---------- initialize explosion content ------------------------------- */
2992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2994 if (IS_CUSTOM_ELEMENT(i))
2997 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2999 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3001 element_info[i].content.e[x][y] =
3002 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3003 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3004 i == EL_PLAYER_3 ? EL_EMERALD :
3005 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3006 i == EL_MOLE ? EL_EMERALD_RED :
3007 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3008 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3009 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3010 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3011 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3012 i == EL_WALL_EMERALD ? EL_EMERALD :
3013 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3014 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3015 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3016 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3017 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3018 i == EL_WALL_PEARL ? EL_PEARL :
3019 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3024 /* ---------- initialize recursion detection ------------------------------ */
3025 recursion_loop_depth = 0;
3026 recursion_loop_detected = FALSE;
3027 recursion_loop_element = EL_UNDEFINED;
3029 /* ---------- initialize graphics engine ---------------------------------- */
3030 game.scroll_delay_value =
3031 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3032 setup.scroll_delay ? setup.scroll_delay_value : 0);
3033 game.scroll_delay_value =
3034 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3036 /* ---------- initialize game engine snapshots ---------------------------- */
3037 for (i = 0; i < MAX_PLAYERS; i++)
3038 game.snapshot.last_action[i] = 0;
3039 game.snapshot.changed_action = FALSE;
3040 game.snapshot.collected_item = FALSE;
3041 game.snapshot.mode =
3042 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3043 SNAPSHOT_MODE_EVERY_STEP :
3044 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3045 SNAPSHOT_MODE_EVERY_MOVE :
3046 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3047 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3048 game.snapshot.save_snapshot = FALSE;
3051 int get_num_special_action(int element, int action_first, int action_last)
3053 int num_special_action = 0;
3056 for (i = action_first; i <= action_last; i++)
3058 boolean found = FALSE;
3060 for (j = 0; j < NUM_DIRECTIONS; j++)
3061 if (el_act_dir2img(element, i, j) !=
3062 el_act_dir2img(element, ACTION_DEFAULT, j))
3066 num_special_action++;
3071 return num_special_action;
3076 =============================================================================
3078 -----------------------------------------------------------------------------
3079 initialize and start new game
3080 =============================================================================
3085 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3086 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3087 int fade_mask = REDRAW_FIELD;
3089 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3090 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3091 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3092 int initial_move_dir = MV_DOWN;
3095 // required here to update video display before fading (FIX THIS)
3096 DrawMaskedBorder(REDRAW_DOOR_2);
3098 if (!game.restart_level)
3099 CloseDoor(DOOR_CLOSE_1);
3101 SetGameStatus(GAME_MODE_PLAYING);
3103 if (level_editor_test_game)
3104 FadeSkipNextFadeIn();
3106 FadeSetEnterScreen();
3108 if (CheckIfGlobalBorderHasChanged())
3109 fade_mask = REDRAW_ALL;
3111 FadeSoundsAndMusic();
3113 ExpireSoundLoops(TRUE);
3117 /* needed if different viewport properties defined for playing */
3118 ChangeViewportPropertiesIfNeeded();
3122 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3124 DrawCompleteVideoDisplay();
3127 InitGameControlValues();
3129 /* don't play tapes over network */
3130 network_playing = (options.network && !tape.playing);
3132 for (i = 0; i < MAX_PLAYERS; i++)
3134 struct PlayerInfo *player = &stored_player[i];
3136 player->index_nr = i;
3137 player->index_bit = (1 << i);
3138 player->element_nr = EL_PLAYER_1 + i;
3140 player->present = FALSE;
3141 player->active = FALSE;
3142 player->mapped = FALSE;
3144 player->killed = FALSE;
3145 player->reanimated = FALSE;
3148 player->effective_action = 0;
3149 player->programmed_action = 0;
3152 player->score_final = 0;
3154 player->gems_still_needed = level.gems_needed;
3155 player->sokobanfields_still_needed = 0;
3156 player->lights_still_needed = 0;
3157 player->friends_still_needed = 0;
3159 for (j = 0; j < MAX_NUM_KEYS; j++)
3160 player->key[j] = FALSE;
3162 player->num_white_keys = 0;
3164 player->dynabomb_count = 0;
3165 player->dynabomb_size = 1;
3166 player->dynabombs_left = 0;
3167 player->dynabomb_xl = FALSE;
3169 player->MovDir = initial_move_dir;
3172 player->GfxDir = initial_move_dir;
3173 player->GfxAction = ACTION_DEFAULT;
3175 player->StepFrame = 0;
3177 player->initial_element = player->element_nr;
3178 player->artwork_element =
3179 (level.use_artwork_element[i] ? level.artwork_element[i] :
3180 player->element_nr);
3181 player->use_murphy = FALSE;
3183 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3184 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3186 player->gravity = level.initial_player_gravity[i];
3188 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3190 player->actual_frame_counter = 0;
3192 player->step_counter = 0;
3194 player->last_move_dir = initial_move_dir;
3196 player->is_active = FALSE;
3198 player->is_waiting = FALSE;
3199 player->is_moving = FALSE;
3200 player->is_auto_moving = FALSE;
3201 player->is_digging = FALSE;
3202 player->is_snapping = FALSE;
3203 player->is_collecting = FALSE;
3204 player->is_pushing = FALSE;
3205 player->is_switching = FALSE;
3206 player->is_dropping = FALSE;
3207 player->is_dropping_pressed = FALSE;
3209 player->is_bored = FALSE;
3210 player->is_sleeping = FALSE;
3212 player->was_waiting = TRUE;
3213 player->was_moving = FALSE;
3214 player->was_snapping = FALSE;
3215 player->was_dropping = FALSE;
3217 player->frame_counter_bored = -1;
3218 player->frame_counter_sleeping = -1;
3220 player->anim_delay_counter = 0;
3221 player->post_delay_counter = 0;
3223 player->dir_waiting = initial_move_dir;
3224 player->action_waiting = ACTION_DEFAULT;
3225 player->last_action_waiting = ACTION_DEFAULT;
3226 player->special_action_bored = ACTION_DEFAULT;
3227 player->special_action_sleeping = ACTION_DEFAULT;
3229 player->switch_x = -1;
3230 player->switch_y = -1;
3232 player->drop_x = -1;
3233 player->drop_y = -1;
3235 player->show_envelope = 0;
3237 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3239 player->push_delay = -1; /* initialized when pushing starts */
3240 player->push_delay_value = game.initial_push_delay_value;
3242 player->drop_delay = 0;
3243 player->drop_pressed_delay = 0;
3245 player->last_jx = -1;
3246 player->last_jy = -1;
3250 player->shield_normal_time_left = 0;
3251 player->shield_deadly_time_left = 0;
3253 player->inventory_infinite_element = EL_UNDEFINED;
3254 player->inventory_size = 0;
3256 if (level.use_initial_inventory[i])
3258 for (j = 0; j < level.initial_inventory_size[i]; j++)
3260 int element = level.initial_inventory_content[i][j];
3261 int collect_count = element_info[element].collect_count_initial;
3264 if (!IS_CUSTOM_ELEMENT(element))
3267 if (collect_count == 0)
3268 player->inventory_infinite_element = element;
3270 for (k = 0; k < collect_count; k++)
3271 if (player->inventory_size < MAX_INVENTORY_SIZE)
3272 player->inventory_element[player->inventory_size++] = element;
3276 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3277 SnapField(player, 0, 0);
3279 player->LevelSolved = FALSE;
3280 player->GameOver = FALSE;
3282 player->LevelSolved_GameWon = FALSE;
3283 player->LevelSolved_GameEnd = FALSE;
3284 player->LevelSolved_PanelOff = FALSE;
3285 player->LevelSolved_SaveTape = FALSE;
3286 player->LevelSolved_SaveScore = FALSE;
3287 player->LevelSolved_CountingTime = 0;
3288 player->LevelSolved_CountingScore = 0;
3290 map_player_action[i] = i;
3293 network_player_action_received = FALSE;
3295 #if defined(NETWORK_AVALIABLE)
3296 /* initial null action */
3297 if (network_playing)
3298 SendToServer_MovePlayer(MV_NONE);
3307 TimeLeft = level.time;
3310 ScreenMovDir = MV_NONE;
3314 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3316 AllPlayersGone = FALSE;
3318 game.no_time_limit = (level.time == 0);
3320 game.yamyam_content_nr = 0;
3321 game.robot_wheel_active = FALSE;
3322 game.magic_wall_active = FALSE;
3323 game.magic_wall_time_left = 0;
3324 game.light_time_left = 0;
3325 game.timegate_time_left = 0;
3326 game.switchgate_pos = 0;
3327 game.wind_direction = level.wind_direction_initial;
3329 game.lenses_time_left = 0;
3330 game.magnify_time_left = 0;
3332 game.ball_state = level.ball_state_initial;
3333 game.ball_content_nr = 0;
3335 game.envelope_active = FALSE;
3337 /* set focus to local player for network games, else to all players */
3338 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3339 game.centered_player_nr_next = game.centered_player_nr;
3340 game.set_centered_player = FALSE;
3342 if (network_playing && tape.recording)
3344 /* store client dependent player focus when recording network games */
3345 tape.centered_player_nr_next = game.centered_player_nr_next;
3346 tape.set_centered_player = TRUE;
3349 for (i = 0; i < NUM_BELTS; i++)
3351 game.belt_dir[i] = MV_NONE;
3352 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3355 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3356 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3358 #if DEBUG_INIT_PLAYER
3361 printf("Player status at level initialization:\n");
3365 SCAN_PLAYFIELD(x, y)
3367 Feld[x][y] = level.field[x][y];
3368 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3369 ChangeDelay[x][y] = 0;
3370 ChangePage[x][y] = -1;
3371 CustomValue[x][y] = 0; /* initialized in InitField() */
3372 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3374 WasJustMoving[x][y] = 0;
3375 WasJustFalling[x][y] = 0;
3376 CheckCollision[x][y] = 0;
3377 CheckImpact[x][y] = 0;
3379 Pushed[x][y] = FALSE;
3381 ChangeCount[x][y] = 0;
3382 ChangeEvent[x][y] = -1;
3384 ExplodePhase[x][y] = 0;
3385 ExplodeDelay[x][y] = 0;
3386 ExplodeField[x][y] = EX_TYPE_NONE;
3388 RunnerVisit[x][y] = 0;
3389 PlayerVisit[x][y] = 0;
3392 GfxRandom[x][y] = INIT_GFX_RANDOM();
3393 GfxElement[x][y] = EL_UNDEFINED;
3394 GfxAction[x][y] = ACTION_DEFAULT;
3395 GfxDir[x][y] = MV_NONE;
3396 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3399 SCAN_PLAYFIELD(x, y)
3401 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3403 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3405 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3408 InitField(x, y, TRUE);
3410 ResetGfxAnimation(x, y);
3415 for (i = 0; i < MAX_PLAYERS; i++)
3417 struct PlayerInfo *player = &stored_player[i];
3419 /* set number of special actions for bored and sleeping animation */
3420 player->num_special_action_bored =
3421 get_num_special_action(player->artwork_element,
3422 ACTION_BORING_1, ACTION_BORING_LAST);
3423 player->num_special_action_sleeping =
3424 get_num_special_action(player->artwork_element,
3425 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3428 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3429 emulate_sb ? EMU_SOKOBAN :
3430 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3432 /* initialize type of slippery elements */
3433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435 if (!IS_CUSTOM_ELEMENT(i))
3437 /* default: elements slip down either to the left or right randomly */
3438 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3440 /* SP style elements prefer to slip down on the left side */
3441 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3442 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3444 /* BD style elements prefer to slip down on the left side */
3445 if (game.emulation == EMU_BOULDERDASH)
3446 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3450 /* initialize explosion and ignition delay */
3451 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3453 if (!IS_CUSTOM_ELEMENT(i))
3456 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3457 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3458 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3459 int last_phase = (num_phase + 1) * delay;
3460 int half_phase = (num_phase / 2) * delay;
3462 element_info[i].explosion_delay = last_phase - 1;
3463 element_info[i].ignition_delay = half_phase;
3465 if (i == EL_BLACK_ORB)
3466 element_info[i].ignition_delay = 1;
3470 /* correct non-moving belts to start moving left */
3471 for (i = 0; i < NUM_BELTS; i++)
3472 if (game.belt_dir[i] == MV_NONE)
3473 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3475 #if USE_NEW_PLAYER_ASSIGNMENTS
3476 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3477 /* choose default local player */
3478 local_player = &stored_player[0];
3480 for (i = 0; i < MAX_PLAYERS; i++)
3481 stored_player[i].connected = FALSE;
3483 local_player->connected = TRUE;
3484 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3488 for (i = 0; i < MAX_PLAYERS; i++)
3489 stored_player[i].connected = tape.player_participates[i];
3491 else if (game.team_mode && !options.network)
3493 /* try to guess locally connected team mode players (needed for correct
3494 assignment of player figures from level to locally playing players) */
3496 for (i = 0; i < MAX_PLAYERS; i++)
3497 if (setup.input[i].use_joystick ||
3498 setup.input[i].key.left != KSYM_UNDEFINED)
3499 stored_player[i].connected = TRUE;
3502 #if DEBUG_INIT_PLAYER
3505 printf("Player status after level initialization:\n");
3507 for (i = 0; i < MAX_PLAYERS; i++)
3509 struct PlayerInfo *player = &stored_player[i];
3511 printf("- player %d: present == %d, connected == %d, active == %d",
3517 if (local_player == player)
3518 printf(" (local player)");
3525 #if DEBUG_INIT_PLAYER
3527 printf("Reassigning players ...\n");
3530 /* check if any connected player was not found in playfield */
3531 for (i = 0; i < MAX_PLAYERS; i++)
3533 struct PlayerInfo *player = &stored_player[i];
3535 if (player->connected && !player->present)
3537 struct PlayerInfo *field_player = NULL;
3539 #if DEBUG_INIT_PLAYER
3541 printf("- looking for field player for player %d ...\n", i + 1);
3544 /* assign first free player found that is present in the playfield */
3546 /* first try: look for unmapped playfield player that is not connected */
3547 for (j = 0; j < MAX_PLAYERS; j++)
3548 if (field_player == NULL &&
3549 stored_player[j].present &&
3550 !stored_player[j].mapped &&
3551 !stored_player[j].connected)
3552 field_player = &stored_player[j];
3554 /* second try: look for *any* unmapped playfield player */
3555 for (j = 0; j < MAX_PLAYERS; j++)
3556 if (field_player == NULL &&
3557 stored_player[j].present &&
3558 !stored_player[j].mapped)
3559 field_player = &stored_player[j];
3561 if (field_player != NULL)
3563 int jx = field_player->jx, jy = field_player->jy;
3565 #if DEBUG_INIT_PLAYER
3567 printf("- found player %d\n", field_player->index_nr + 1);
3570 player->present = FALSE;
3571 player->active = FALSE;
3573 field_player->present = TRUE;
3574 field_player->active = TRUE;
3577 player->initial_element = field_player->initial_element;
3578 player->artwork_element = field_player->artwork_element;
3580 player->block_last_field = field_player->block_last_field;
3581 player->block_delay_adjustment = field_player->block_delay_adjustment;
3584 StorePlayer[jx][jy] = field_player->element_nr;
3586 field_player->jx = field_player->last_jx = jx;
3587 field_player->jy = field_player->last_jy = jy;
3589 if (local_player == player)
3590 local_player = field_player;
3592 map_player_action[field_player->index_nr] = i;
3594 field_player->mapped = TRUE;
3596 #if DEBUG_INIT_PLAYER
3598 printf("- map_player_action[%d] == %d\n",
3599 field_player->index_nr + 1, i + 1);
3604 if (player->connected && player->present)
3605 player->mapped = TRUE;
3608 #if DEBUG_INIT_PLAYER
3611 printf("Player status after player assignment (first stage):\n");
3613 for (i = 0; i < MAX_PLAYERS; i++)
3615 struct PlayerInfo *player = &stored_player[i];
3617 printf("- player %d: present == %d, connected == %d, active == %d",
3623 if (local_player == player)
3624 printf(" (local player)");
3633 /* check if any connected player was not found in playfield */
3634 for (i = 0; i < MAX_PLAYERS; i++)
3636 struct PlayerInfo *player = &stored_player[i];
3638 if (player->connected && !player->present)
3640 for (j = 0; j < MAX_PLAYERS; j++)
3642 struct PlayerInfo *field_player = &stored_player[j];
3643 int jx = field_player->jx, jy = field_player->jy;
3645 /* assign first free player found that is present in the playfield */
3646 if (field_player->present && !field_player->connected)
3648 player->present = TRUE;
3649 player->active = TRUE;
3651 field_player->present = FALSE;
3652 field_player->active = FALSE;
3654 player->initial_element = field_player->initial_element;
3655 player->artwork_element = field_player->artwork_element;
3657 player->block_last_field = field_player->block_last_field;
3658 player->block_delay_adjustment = field_player->block_delay_adjustment;
3660 StorePlayer[jx][jy] = player->element_nr;
3662 player->jx = player->last_jx = jx;
3663 player->jy = player->last_jy = jy;
3673 printf("::: local_player->present == %d\n", local_player->present);
3678 /* when playing a tape, eliminate all players who do not participate */
3680 #if USE_NEW_PLAYER_ASSIGNMENTS
3682 if (!game.team_mode)
3684 for (i = 0; i < MAX_PLAYERS; i++)
3686 if (stored_player[i].active &&
3687 !tape.player_participates[map_player_action[i]])
3689 struct PlayerInfo *player = &stored_player[i];
3690 int jx = player->jx, jy = player->jy;
3692 #if DEBUG_INIT_PLAYER
3694 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3697 player->active = FALSE;
3698 StorePlayer[jx][jy] = 0;
3699 Feld[jx][jy] = EL_EMPTY;
3706 for (i = 0; i < MAX_PLAYERS; i++)
3708 if (stored_player[i].active &&
3709 !tape.player_participates[i])
3711 struct PlayerInfo *player = &stored_player[i];
3712 int jx = player->jx, jy = player->jy;
3714 player->active = FALSE;
3715 StorePlayer[jx][jy] = 0;
3716 Feld[jx][jy] = EL_EMPTY;
3721 else if (!options.network && !game.team_mode) /* && !tape.playing */
3723 /* when in single player mode, eliminate all but the first active player */
3725 for (i = 0; i < MAX_PLAYERS; i++)
3727 if (stored_player[i].active)
3729 for (j = i + 1; j < MAX_PLAYERS; j++)
3731 if (stored_player[j].active)
3733 struct PlayerInfo *player = &stored_player[j];
3734 int jx = player->jx, jy = player->jy;
3736 player->active = FALSE;
3737 player->present = FALSE;
3739 StorePlayer[jx][jy] = 0;
3740 Feld[jx][jy] = EL_EMPTY;
3747 /* when recording the game, store which players take part in the game */
3750 #if USE_NEW_PLAYER_ASSIGNMENTS
3751 for (i = 0; i < MAX_PLAYERS; i++)
3752 if (stored_player[i].connected)
3753 tape.player_participates[i] = TRUE;
3755 for (i = 0; i < MAX_PLAYERS; i++)
3756 if (stored_player[i].active)
3757 tape.player_participates[i] = TRUE;
3761 #if DEBUG_INIT_PLAYER
3764 printf("Player status after player assignment (final stage):\n");
3766 for (i = 0; i < MAX_PLAYERS; i++)
3768 struct PlayerInfo *player = &stored_player[i];
3770 printf("- player %d: present == %d, connected == %d, active == %d",
3776 if (local_player == player)
3777 printf(" (local player)");
3784 if (BorderElement == EL_EMPTY)
3787 SBX_Right = lev_fieldx - SCR_FIELDX;
3789 SBY_Lower = lev_fieldy - SCR_FIELDY;
3794 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3796 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3799 if (full_lev_fieldx <= SCR_FIELDX)
3800 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3801 if (full_lev_fieldy <= SCR_FIELDY)
3802 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3804 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3806 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3809 /* if local player not found, look for custom element that might create
3810 the player (make some assumptions about the right custom element) */
3811 if (!local_player->present)
3813 int start_x = 0, start_y = 0;
3814 int found_rating = 0;
3815 int found_element = EL_UNDEFINED;
3816 int player_nr = local_player->index_nr;
3818 SCAN_PLAYFIELD(x, y)
3820 int element = Feld[x][y];
3825 if (level.use_start_element[player_nr] &&
3826 level.start_element[player_nr] == element &&
3833 found_element = element;
3836 if (!IS_CUSTOM_ELEMENT(element))
3839 if (CAN_CHANGE(element))
3841 for (i = 0; i < element_info[element].num_change_pages; i++)
3843 /* check for player created from custom element as single target */
3844 content = element_info[element].change_page[i].target_element;
3845 is_player = ELEM_IS_PLAYER(content);
3847 if (is_player && (found_rating < 3 ||
3848 (found_rating == 3 && element < found_element)))
3854 found_element = element;
3859 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3861 /* check for player created from custom element as explosion content */
3862 content = element_info[element].content.e[xx][yy];
3863 is_player = ELEM_IS_PLAYER(content);
3865 if (is_player && (found_rating < 2 ||
3866 (found_rating == 2 && element < found_element)))
3868 start_x = x + xx - 1;
3869 start_y = y + yy - 1;
3872 found_element = element;
3875 if (!CAN_CHANGE(element))
3878 for (i = 0; i < element_info[element].num_change_pages; i++)
3880 /* check for player created from custom element as extended target */
3882 element_info[element].change_page[i].target_content.e[xx][yy];
3884 is_player = ELEM_IS_PLAYER(content);
3886 if (is_player && (found_rating < 1 ||
3887 (found_rating == 1 && element < found_element)))
3889 start_x = x + xx - 1;
3890 start_y = y + yy - 1;
3893 found_element = element;
3899 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
3900 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3903 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3904 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3909 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3910 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3911 local_player->jx - MIDPOSX);
3913 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3914 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3915 local_player->jy - MIDPOSY);
3918 /* !!! FIX THIS (START) !!! */
3919 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3921 InitGameEngine_EM();
3923 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3925 InitGameEngine_SP();
3929 DrawLevel(REDRAW_FIELD);
3932 /* after drawing the level, correct some elements */
3933 if (game.timegate_time_left == 0)
3934 CloseAllOpenTimegates();
3937 /* blit playfield from scroll buffer to normal back buffer for fading in */
3938 BlitScreenToBitmap(backbuffer);
3939 /* !!! FIX THIS (END) !!! */
3941 DrawMaskedBorder(fade_mask);
3946 // full screen redraw is required at this point in the following cases:
3947 // - special editor door undrawn when game was started from level editor
3948 // - drawing area (playfield) was changed and has to be removed completely
3949 redraw_mask = REDRAW_ALL;
3953 if (!game.restart_level)
3955 /* copy default game door content to main double buffer */
3957 /* !!! CHECK AGAIN !!! */
3958 SetPanelBackground();
3959 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3960 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3963 SetPanelBackground();
3964 SetDrawBackgroundMask(REDRAW_DOOR_1);
3966 UpdateAndDisplayGameControlValues();
3968 if (!game.restart_level)
3974 CreateGameButtons();
3976 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3977 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3978 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3983 /* copy actual game door content to door double buffer for OpenDoor() */
3984 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3986 OpenDoor(DOOR_OPEN_ALL);
3988 PlaySound(SND_GAME_STARTING);
3990 if (setup.sound_music)
3993 KeyboardAutoRepeatOffUnlessAutoplay();
3995 #if DEBUG_INIT_PLAYER
3998 printf("Player status (final):\n");
4000 for (i = 0; i < MAX_PLAYERS; i++)
4002 struct PlayerInfo *player = &stored_player[i];
4004 printf("- player %d: present == %d, connected == %d, active == %d",
4010 if (local_player == player)
4011 printf(" (local player)");
4024 if (!game.restart_level && !tape.playing)
4026 LevelStats_incPlayed(level_nr);
4028 SaveLevelSetup_SeriesInfo();
4031 game.restart_level = FALSE;
4033 SaveEngineSnapshotToListInitial();
4036 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4037 int actual_player_x, int actual_player_y)
4039 /* this is used for non-R'n'D game engines to update certain engine values */
4041 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4043 actual_player_x = correctLevelPosX_EM(actual_player_x);
4044 actual_player_y = correctLevelPosY_EM(actual_player_y);
4047 /* needed to determine if sounds are played within the visible screen area */
4048 scroll_x = actual_scroll_x;
4049 scroll_y = actual_scroll_y;
4051 /* needed to get player position for "follow finger" playing input method */
4052 local_player->jx = actual_player_x;
4053 local_player->jy = actual_player_y;
4056 void InitMovDir(int x, int y)
4058 int i, element = Feld[x][y];
4059 static int xy[4][2] =
4066 static int direction[3][4] =
4068 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4069 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4070 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4079 Feld[x][y] = EL_BUG;
4080 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4083 case EL_SPACESHIP_RIGHT:
4084 case EL_SPACESHIP_UP:
4085 case EL_SPACESHIP_LEFT:
4086 case EL_SPACESHIP_DOWN:
4087 Feld[x][y] = EL_SPACESHIP;
4088 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4091 case EL_BD_BUTTERFLY_RIGHT:
4092 case EL_BD_BUTTERFLY_UP:
4093 case EL_BD_BUTTERFLY_LEFT:
4094 case EL_BD_BUTTERFLY_DOWN:
4095 Feld[x][y] = EL_BD_BUTTERFLY;
4096 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4099 case EL_BD_FIREFLY_RIGHT:
4100 case EL_BD_FIREFLY_UP:
4101 case EL_BD_FIREFLY_LEFT:
4102 case EL_BD_FIREFLY_DOWN:
4103 Feld[x][y] = EL_BD_FIREFLY;
4104 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4107 case EL_PACMAN_RIGHT:
4109 case EL_PACMAN_LEFT:
4110 case EL_PACMAN_DOWN:
4111 Feld[x][y] = EL_PACMAN;
4112 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4115 case EL_YAMYAM_LEFT:
4116 case EL_YAMYAM_RIGHT:
4118 case EL_YAMYAM_DOWN:
4119 Feld[x][y] = EL_YAMYAM;
4120 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4123 case EL_SP_SNIKSNAK:
4124 MovDir[x][y] = MV_UP;
4127 case EL_SP_ELECTRON:
4128 MovDir[x][y] = MV_LEFT;
4135 Feld[x][y] = EL_MOLE;
4136 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4140 if (IS_CUSTOM_ELEMENT(element))
4142 struct ElementInfo *ei = &element_info[element];
4143 int move_direction_initial = ei->move_direction_initial;
4144 int move_pattern = ei->move_pattern;
4146 if (move_direction_initial == MV_START_PREVIOUS)
4148 if (MovDir[x][y] != MV_NONE)
4151 move_direction_initial = MV_START_AUTOMATIC;
4154 if (move_direction_initial == MV_START_RANDOM)
4155 MovDir[x][y] = 1 << RND(4);
4156 else if (move_direction_initial & MV_ANY_DIRECTION)
4157 MovDir[x][y] = move_direction_initial;
4158 else if (move_pattern == MV_ALL_DIRECTIONS ||
4159 move_pattern == MV_TURNING_LEFT ||
4160 move_pattern == MV_TURNING_RIGHT ||
4161 move_pattern == MV_TURNING_LEFT_RIGHT ||
4162 move_pattern == MV_TURNING_RIGHT_LEFT ||
4163 move_pattern == MV_TURNING_RANDOM)
4164 MovDir[x][y] = 1 << RND(4);
4165 else if (move_pattern == MV_HORIZONTAL)
4166 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4167 else if (move_pattern == MV_VERTICAL)
4168 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4169 else if (move_pattern & MV_ANY_DIRECTION)
4170 MovDir[x][y] = element_info[element].move_pattern;
4171 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4172 move_pattern == MV_ALONG_RIGHT_SIDE)
4174 /* use random direction as default start direction */
4175 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4176 MovDir[x][y] = 1 << RND(4);
4178 for (i = 0; i < NUM_DIRECTIONS; i++)
4180 int x1 = x + xy[i][0];
4181 int y1 = y + xy[i][1];
4183 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4185 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4186 MovDir[x][y] = direction[0][i];
4188 MovDir[x][y] = direction[1][i];
4197 MovDir[x][y] = 1 << RND(4);
4199 if (element != EL_BUG &&
4200 element != EL_SPACESHIP &&
4201 element != EL_BD_BUTTERFLY &&
4202 element != EL_BD_FIREFLY)
4205 for (i = 0; i < NUM_DIRECTIONS; i++)
4207 int x1 = x + xy[i][0];
4208 int y1 = y + xy[i][1];
4210 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4212 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4214 MovDir[x][y] = direction[0][i];
4217 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4218 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4220 MovDir[x][y] = direction[1][i];
4229 GfxDir[x][y] = MovDir[x][y];
4232 void InitAmoebaNr(int x, int y)
4235 int group_nr = AmoebeNachbarNr(x, y);
4239 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4241 if (AmoebaCnt[i] == 0)
4249 AmoebaNr[x][y] = group_nr;
4250 AmoebaCnt[group_nr]++;
4251 AmoebaCnt2[group_nr]++;
4254 static void PlayerWins(struct PlayerInfo *player)
4256 player->LevelSolved = TRUE;
4257 player->GameOver = TRUE;
4259 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4260 level.native_em_level->lev->score : player->score);
4262 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4264 player->LevelSolved_CountingScore = player->score_final;
4269 static int time, time_final;
4270 static int score, score_final;
4271 static int game_over_delay_1 = 0;
4272 static int game_over_delay_2 = 0;
4273 int game_over_delay_value_1 = 50;
4274 int game_over_delay_value_2 = 50;
4276 if (!local_player->LevelSolved_GameWon)
4280 /* do not start end game actions before the player stops moving (to exit) */
4281 if (local_player->MovPos)
4284 local_player->LevelSolved_GameWon = TRUE;
4285 local_player->LevelSolved_SaveTape = tape.recording;
4286 local_player->LevelSolved_SaveScore = !tape.playing;
4290 LevelStats_incSolved(level_nr);
4292 SaveLevelSetup_SeriesInfo();
4295 if (tape.auto_play) /* tape might already be stopped here */
4296 tape.auto_play_level_solved = TRUE;
4300 game_over_delay_1 = game_over_delay_value_1;
4301 game_over_delay_2 = game_over_delay_value_2;
4303 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4304 score = score_final = local_player->score_final;
4309 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4311 else if (game.no_time_limit && TimePlayed < 999)
4314 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4317 local_player->score_final = score_final;
4319 if (level_editor_test_game)
4322 score = score_final;
4324 local_player->LevelSolved_CountingTime = time;
4325 local_player->LevelSolved_CountingScore = score;
4327 game_panel_controls[GAME_PANEL_TIME].value = time;
4328 game_panel_controls[GAME_PANEL_SCORE].value = score;
4330 DisplayGameControlValues();
4333 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4335 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4337 /* close exit door after last player */
4338 if ((AllPlayersGone &&
4339 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4340 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4341 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4342 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4343 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4345 int element = Feld[ExitX][ExitY];
4347 Feld[ExitX][ExitY] =
4348 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4349 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4350 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4351 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4352 EL_EM_STEEL_EXIT_CLOSING);
4354 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4357 /* player disappears */
4358 DrawLevelField(ExitX, ExitY);
4361 for (i = 0; i < MAX_PLAYERS; i++)
4363 struct PlayerInfo *player = &stored_player[i];
4365 if (player->present)
4367 RemovePlayer(player);
4369 /* player disappears */
4370 DrawLevelField(player->jx, player->jy);
4375 PlaySound(SND_GAME_WINNING);
4378 if (game_over_delay_1 > 0)
4380 game_over_delay_1--;
4385 if (time != time_final)
4387 int time_to_go = ABS(time_final - time);
4388 int time_count_dir = (time < time_final ? +1 : -1);
4389 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4391 time += time_count_steps * time_count_dir;
4392 score += time_count_steps * level.score[SC_TIME_BONUS];
4394 local_player->LevelSolved_CountingTime = time;
4395 local_player->LevelSolved_CountingScore = score;
4397 game_panel_controls[GAME_PANEL_TIME].value = time;
4398 game_panel_controls[GAME_PANEL_SCORE].value = score;
4400 DisplayGameControlValues();
4402 if (time == time_final)
4403 StopSound(SND_GAME_LEVELTIME_BONUS);
4404 else if (setup.sound_loops)
4405 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4407 PlaySound(SND_GAME_LEVELTIME_BONUS);
4412 local_player->LevelSolved_PanelOff = TRUE;
4414 if (game_over_delay_2 > 0)
4416 game_over_delay_2--;
4427 boolean raise_level = FALSE;
4429 local_player->LevelSolved_GameEnd = TRUE;
4431 if (!global.use_envelope_request)
4432 CloseDoor(DOOR_CLOSE_1);
4434 if (local_player->LevelSolved_SaveTape)
4436 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4439 CloseDoor(DOOR_CLOSE_ALL);
4441 if (level_editor_test_game)
4443 SetGameStatus(GAME_MODE_MAIN);
4450 if (!local_player->LevelSolved_SaveScore)
4452 SetGameStatus(GAME_MODE_MAIN);
4459 if (level_nr == leveldir_current->handicap_level)
4461 leveldir_current->handicap_level++;
4463 SaveLevelSetup_SeriesInfo();
4466 if (level_nr < leveldir_current->last_level)
4467 raise_level = TRUE; /* advance to next level */
4469 if ((hi_pos = NewHiScore()) >= 0)
4471 SetGameStatus(GAME_MODE_SCORES);
4473 DrawHallOfFame(hi_pos);
4483 SetGameStatus(GAME_MODE_MAIN);
4500 LoadScore(level_nr);
4502 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4503 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4506 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4508 if (local_player->score_final > highscore[k].Score)
4510 /* player has made it to the hall of fame */
4512 if (k < MAX_SCORE_ENTRIES - 1)
4514 int m = MAX_SCORE_ENTRIES - 1;
4517 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4518 if (strEqual(setup.player_name, highscore[l].Name))
4520 if (m == k) /* player's new highscore overwrites his old one */
4524 for (l = m; l > k; l--)
4526 strcpy(highscore[l].Name, highscore[l - 1].Name);
4527 highscore[l].Score = highscore[l - 1].Score;
4534 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4535 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4536 highscore[k].Score = local_player->score_final;
4542 else if (!strncmp(setup.player_name, highscore[k].Name,
4543 MAX_PLAYER_NAME_LEN))
4544 break; /* player already there with a higher score */
4550 SaveScore(level_nr);
4555 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4557 int element = Feld[x][y];
4558 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4559 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4560 int horiz_move = (dx != 0);
4561 int sign = (horiz_move ? dx : dy);
4562 int step = sign * element_info[element].move_stepsize;
4564 /* special values for move stepsize for spring and things on conveyor belt */
4567 if (CAN_FALL(element) &&
4568 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4569 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4570 else if (element == EL_SPRING)
4571 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4577 inline static int getElementMoveStepsize(int x, int y)
4579 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4582 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4584 if (player->GfxAction != action || player->GfxDir != dir)
4586 player->GfxAction = action;
4587 player->GfxDir = dir;
4589 player->StepFrame = 0;
4593 static void ResetGfxFrame(int x, int y)
4595 int element = Feld[x][y];
4596 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4598 if (graphic_info[graphic].anim_global_sync)
4599 GfxFrame[x][y] = FrameCounter;
4600 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4601 GfxFrame[x][y] = CustomValue[x][y];
4602 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4603 GfxFrame[x][y] = element_info[element].collect_score;
4604 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4605 GfxFrame[x][y] = ChangeDelay[x][y];
4608 static void ResetGfxAnimation(int x, int y)
4610 GfxAction[x][y] = ACTION_DEFAULT;
4611 GfxDir[x][y] = MovDir[x][y];
4614 ResetGfxFrame(x, y);
4617 static void ResetRandomAnimationValue(int x, int y)
4619 GfxRandom[x][y] = INIT_GFX_RANDOM();
4622 void InitMovingField(int x, int y, int direction)
4624 int element = Feld[x][y];
4625 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4626 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4629 boolean is_moving_before, is_moving_after;
4631 /* check if element was/is moving or being moved before/after mode change */
4632 is_moving_before = (WasJustMoving[x][y] != 0);
4633 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4635 /* reset animation only for moving elements which change direction of moving
4636 or which just started or stopped moving
4637 (else CEs with property "can move" / "not moving" are reset each frame) */
4638 if (is_moving_before != is_moving_after ||
4639 direction != MovDir[x][y])
4640 ResetGfxAnimation(x, y);
4642 MovDir[x][y] = direction;
4643 GfxDir[x][y] = direction;
4645 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4646 direction == MV_DOWN && CAN_FALL(element) ?
4647 ACTION_FALLING : ACTION_MOVING);
4649 /* this is needed for CEs with property "can move" / "not moving" */
4651 if (is_moving_after)
4653 if (Feld[newx][newy] == EL_EMPTY)
4654 Feld[newx][newy] = EL_BLOCKED;
4656 MovDir[newx][newy] = MovDir[x][y];
4658 CustomValue[newx][newy] = CustomValue[x][y];
4660 GfxFrame[newx][newy] = GfxFrame[x][y];
4661 GfxRandom[newx][newy] = GfxRandom[x][y];
4662 GfxAction[newx][newy] = GfxAction[x][y];
4663 GfxDir[newx][newy] = GfxDir[x][y];
4667 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4669 int direction = MovDir[x][y];
4670 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4671 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4677 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4679 int oldx = x, oldy = y;
4680 int direction = MovDir[x][y];
4682 if (direction == MV_LEFT)
4684 else if (direction == MV_RIGHT)
4686 else if (direction == MV_UP)
4688 else if (direction == MV_DOWN)
4691 *comes_from_x = oldx;
4692 *comes_from_y = oldy;
4695 int MovingOrBlocked2Element(int x, int y)
4697 int element = Feld[x][y];
4699 if (element == EL_BLOCKED)
4703 Blocked2Moving(x, y, &oldx, &oldy);
4704 return Feld[oldx][oldy];
4710 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4712 /* like MovingOrBlocked2Element(), but if element is moving
4713 and (x,y) is the field the moving element is just leaving,
4714 return EL_BLOCKED instead of the element value */
4715 int element = Feld[x][y];
4717 if (IS_MOVING(x, y))
4719 if (element == EL_BLOCKED)
4723 Blocked2Moving(x, y, &oldx, &oldy);
4724 return Feld[oldx][oldy];
4733 static void RemoveField(int x, int y)
4735 Feld[x][y] = EL_EMPTY;
4741 CustomValue[x][y] = 0;
4744 ChangeDelay[x][y] = 0;
4745 ChangePage[x][y] = -1;
4746 Pushed[x][y] = FALSE;
4748 GfxElement[x][y] = EL_UNDEFINED;
4749 GfxAction[x][y] = ACTION_DEFAULT;
4750 GfxDir[x][y] = MV_NONE;
4753 void RemoveMovingField(int x, int y)
4755 int oldx = x, oldy = y, newx = x, newy = y;
4756 int element = Feld[x][y];
4757 int next_element = EL_UNDEFINED;
4759 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4762 if (IS_MOVING(x, y))
4764 Moving2Blocked(x, y, &newx, &newy);
4766 if (Feld[newx][newy] != EL_BLOCKED)
4768 /* element is moving, but target field is not free (blocked), but
4769 already occupied by something different (example: acid pool);
4770 in this case, only remove the moving field, but not the target */
4772 RemoveField(oldx, oldy);
4774 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4776 TEST_DrawLevelField(oldx, oldy);
4781 else if (element == EL_BLOCKED)
4783 Blocked2Moving(x, y, &oldx, &oldy);
4784 if (!IS_MOVING(oldx, oldy))
4788 if (element == EL_BLOCKED &&
4789 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4790 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4791 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4792 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4793 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4794 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4795 next_element = get_next_element(Feld[oldx][oldy]);
4797 RemoveField(oldx, oldy);
4798 RemoveField(newx, newy);
4800 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4802 if (next_element != EL_UNDEFINED)
4803 Feld[oldx][oldy] = next_element;
4805 TEST_DrawLevelField(oldx, oldy);
4806 TEST_DrawLevelField(newx, newy);
4809 void DrawDynamite(int x, int y)
4811 int sx = SCREENX(x), sy = SCREENY(y);
4812 int graphic = el2img(Feld[x][y]);
4815 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4818 if (IS_WALKABLE_INSIDE(Back[x][y]))
4822 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4823 else if (Store[x][y])
4824 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4826 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4828 if (Back[x][y] || Store[x][y])
4829 DrawGraphicThruMask(sx, sy, graphic, frame);
4831 DrawGraphic(sx, sy, graphic, frame);
4834 void CheckDynamite(int x, int y)
4836 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
4840 if (MovDelay[x][y] != 0)
4843 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4849 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4854 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4856 boolean num_checked_players = 0;
4859 for (i = 0; i < MAX_PLAYERS; i++)
4861 if (stored_player[i].active)
4863 int sx = stored_player[i].jx;
4864 int sy = stored_player[i].jy;
4866 if (num_checked_players == 0)
4873 *sx1 = MIN(*sx1, sx);
4874 *sy1 = MIN(*sy1, sy);
4875 *sx2 = MAX(*sx2, sx);
4876 *sy2 = MAX(*sy2, sy);
4879 num_checked_players++;
4884 static boolean checkIfAllPlayersFitToScreen_RND()
4886 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4888 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4890 return (sx2 - sx1 < SCR_FIELDX &&
4891 sy2 - sy1 < SCR_FIELDY);
4894 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4896 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4898 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4900 *sx = (sx1 + sx2) / 2;
4901 *sy = (sy1 + sy2) / 2;
4904 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4905 boolean center_screen, boolean quick_relocation)
4907 unsigned int frame_delay_value_old = GetVideoFrameDelay();
4908 boolean ffwd_delay = (tape.playing && tape.fast_forward);
4909 boolean no_delay = (tape.warp_forward);
4910 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4911 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4912 int new_scroll_x, new_scroll_y;
4914 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4916 /* case 1: quick relocation inside visible screen (without scrolling) */
4923 if (!level.shifted_relocation || center_screen)
4925 /* relocation _with_ centering of screen */
4927 new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
4928 x > SBX_Right + MIDPOSX ? SBX_Right :
4931 new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4932 y > SBY_Lower + MIDPOSY ? SBY_Lower :
4937 /* relocation _without_ centering of screen */
4939 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
4940 old_x > SBX_Right + MIDPOSX ? SBX_Right :
4943 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4944 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4947 int offset_x = x + (scroll_x - center_scroll_x);
4948 int offset_y = y + (scroll_y - center_scroll_y);
4950 new_scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
4951 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4952 offset_x - MIDPOSX);
4954 new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4955 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4956 offset_y - MIDPOSY);
4959 if (quick_relocation)
4961 /* case 2: quick relocation (redraw without visible scrolling) */
4963 scroll_x = new_scroll_x;
4964 scroll_y = new_scroll_y;
4971 /* case 3: visible relocation (with scrolling to new position) */
4973 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
4975 SetVideoFrameDelay(wait_delay_value);
4977 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4980 int fx = FX, fy = FY;
4982 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4983 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4985 if (dx == 0 && dy == 0) /* no scrolling needed at all */
4991 fx += dx * TILEX / 2;
4992 fy += dy * TILEY / 2;
4994 ScrollLevel(dx, dy);
4997 /* scroll in two steps of half tile size to make things smoother */
4998 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5000 /* scroll second step to align at full tile size */
5001 BlitScreenToBitmap(window);
5007 SetVideoFrameDelay(frame_delay_value_old);
5010 void RelocatePlayer(int jx, int jy, int el_player_raw)
5012 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5013 int player_nr = GET_PLAYER_NR(el_player);
5014 struct PlayerInfo *player = &stored_player[player_nr];
5015 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5016 boolean no_delay = (tape.warp_forward);
5017 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5018 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5019 int old_jx = player->jx;
5020 int old_jy = player->jy;
5021 int old_element = Feld[old_jx][old_jy];
5022 int element = Feld[jx][jy];
5023 boolean player_relocated = (old_jx != jx || old_jy != jy);
5025 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5026 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5027 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5028 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5029 int leave_side_horiz = move_dir_horiz;
5030 int leave_side_vert = move_dir_vert;
5031 int enter_side = enter_side_horiz | enter_side_vert;
5032 int leave_side = leave_side_horiz | leave_side_vert;
5034 if (player->GameOver) /* do not reanimate dead player */
5037 if (!player_relocated) /* no need to relocate the player */
5040 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5042 RemoveField(jx, jy); /* temporarily remove newly placed player */
5043 DrawLevelField(jx, jy);
5046 if (player->present)
5048 while (player->MovPos)
5050 ScrollPlayer(player, SCROLL_GO_ON);
5051 ScrollScreen(NULL, SCROLL_GO_ON);
5053 AdvanceFrameAndPlayerCounters(player->index_nr);
5057 BackToFront_WithFrameDelay(wait_delay_value);
5060 DrawPlayer(player); /* needed here only to cleanup last field */
5061 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5063 player->is_moving = FALSE;
5066 if (IS_CUSTOM_ELEMENT(old_element))
5067 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5069 player->index_bit, leave_side);
5071 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5073 player->index_bit, leave_side);
5075 Feld[jx][jy] = el_player;
5076 InitPlayerField(jx, jy, el_player, TRUE);
5078 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5079 possible that the relocation target field did not contain a player element,
5080 but a walkable element, to which the new player was relocated -- in this
5081 case, restore that (already initialized!) element on the player field */
5082 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5084 Feld[jx][jy] = element; /* restore previously existing element */
5087 /* only visually relocate centered player */
5088 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5089 FALSE, level.instant_relocation);
5091 TestIfPlayerTouchesBadThing(jx, jy);
5092 TestIfPlayerTouchesCustomElement(jx, jy);
5094 if (IS_CUSTOM_ELEMENT(element))
5095 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5096 player->index_bit, enter_side);
5098 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5099 player->index_bit, enter_side);
5101 if (player->is_switching)
5103 /* ensure that relocation while still switching an element does not cause
5104 a new element to be treated as also switched directly after relocation
5105 (this is important for teleporter switches that teleport the player to
5106 a place where another teleporter switch is in the same direction, which
5107 would then incorrectly be treated as immediately switched before the
5108 direction key that caused the switch was released) */
5110 player->switch_x += jx - old_jx;
5111 player->switch_y += jy - old_jy;
5115 void Explode(int ex, int ey, int phase, int mode)
5121 /* !!! eliminate this variable !!! */
5122 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5124 if (game.explosions_delayed)
5126 ExplodeField[ex][ey] = mode;
5130 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5132 int center_element = Feld[ex][ey];
5133 int artwork_element, explosion_element; /* set these values later */
5135 /* remove things displayed in background while burning dynamite */
5136 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5139 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5141 /* put moving element to center field (and let it explode there) */
5142 center_element = MovingOrBlocked2Element(ex, ey);
5143 RemoveMovingField(ex, ey);
5144 Feld[ex][ey] = center_element;
5147 /* now "center_element" is finally determined -- set related values now */
5148 artwork_element = center_element; /* for custom player artwork */
5149 explosion_element = center_element; /* for custom player artwork */
5151 if (IS_PLAYER(ex, ey))
5153 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5155 artwork_element = stored_player[player_nr].artwork_element;
5157 if (level.use_explosion_element[player_nr])
5159 explosion_element = level.explosion_element[player_nr];
5160 artwork_element = explosion_element;
5164 if (mode == EX_TYPE_NORMAL ||
5165 mode == EX_TYPE_CENTER ||
5166 mode == EX_TYPE_CROSS)
5167 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5169 last_phase = element_info[explosion_element].explosion_delay + 1;
5171 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5173 int xx = x - ex + 1;
5174 int yy = y - ey + 1;
5177 if (!IN_LEV_FIELD(x, y) ||
5178 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5179 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5182 element = Feld[x][y];
5184 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5186 element = MovingOrBlocked2Element(x, y);
5188 if (!IS_EXPLOSION_PROOF(element))
5189 RemoveMovingField(x, y);
5192 /* indestructible elements can only explode in center (but not flames) */
5193 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5194 mode == EX_TYPE_BORDER)) ||
5195 element == EL_FLAMES)
5198 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5199 behaviour, for example when touching a yamyam that explodes to rocks
5200 with active deadly shield, a rock is created under the player !!! */
5201 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5203 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5204 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5205 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5207 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5210 if (IS_ACTIVE_BOMB(element))
5212 /* re-activate things under the bomb like gate or penguin */
5213 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5220 /* save walkable background elements while explosion on same tile */
5221 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5222 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5223 Back[x][y] = element;
5225 /* ignite explodable elements reached by other explosion */
5226 if (element == EL_EXPLOSION)
5227 element = Store2[x][y];
5229 if (AmoebaNr[x][y] &&
5230 (element == EL_AMOEBA_FULL ||
5231 element == EL_BD_AMOEBA ||
5232 element == EL_AMOEBA_GROWING))
5234 AmoebaCnt[AmoebaNr[x][y]]--;
5235 AmoebaCnt2[AmoebaNr[x][y]]--;
5240 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5242 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5244 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5246 if (PLAYERINFO(ex, ey)->use_murphy)
5247 Store[x][y] = EL_EMPTY;
5250 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5251 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5252 else if (ELEM_IS_PLAYER(center_element))
5253 Store[x][y] = EL_EMPTY;
5254 else if (center_element == EL_YAMYAM)
5255 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5256 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5257 Store[x][y] = element_info[center_element].content.e[xx][yy];
5259 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5260 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5261 otherwise) -- FIX THIS !!! */
5262 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5263 Store[x][y] = element_info[element].content.e[1][1];
5265 else if (!CAN_EXPLODE(element))
5266 Store[x][y] = element_info[element].content.e[1][1];
5269 Store[x][y] = EL_EMPTY;
5271 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5272 center_element == EL_AMOEBA_TO_DIAMOND)
5273 Store2[x][y] = element;
5275 Feld[x][y] = EL_EXPLOSION;
5276 GfxElement[x][y] = artwork_element;
5278 ExplodePhase[x][y] = 1;
5279 ExplodeDelay[x][y] = last_phase;
5284 if (center_element == EL_YAMYAM)
5285 game.yamyam_content_nr =
5286 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5298 GfxFrame[x][y] = 0; /* restart explosion animation */
5300 last_phase = ExplodeDelay[x][y];
5302 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5304 /* this can happen if the player leaves an explosion just in time */
5305 if (GfxElement[x][y] == EL_UNDEFINED)
5306 GfxElement[x][y] = EL_EMPTY;
5308 border_element = Store2[x][y];
5309 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5310 border_element = StorePlayer[x][y];
5312 if (phase == element_info[border_element].ignition_delay ||
5313 phase == last_phase)
5315 boolean border_explosion = FALSE;
5317 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5318 !PLAYER_EXPLOSION_PROTECTED(x, y))
5320 KillPlayerUnlessExplosionProtected(x, y);
5321 border_explosion = TRUE;
5323 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5325 Feld[x][y] = Store2[x][y];
5328 border_explosion = TRUE;
5330 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5332 AmoebeUmwandeln(x, y);
5334 border_explosion = TRUE;
5337 /* if an element just explodes due to another explosion (chain-reaction),
5338 do not immediately end the new explosion when it was the last frame of
5339 the explosion (as it would be done in the following "if"-statement!) */
5340 if (border_explosion && phase == last_phase)
5344 if (phase == last_phase)
5348 element = Feld[x][y] = Store[x][y];
5349 Store[x][y] = Store2[x][y] = 0;
5350 GfxElement[x][y] = EL_UNDEFINED;
5352 /* player can escape from explosions and might therefore be still alive */
5353 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5354 element <= EL_PLAYER_IS_EXPLODING_4)
5356 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5357 int explosion_element = EL_PLAYER_1 + player_nr;
5358 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5359 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5361 if (level.use_explosion_element[player_nr])
5362 explosion_element = level.explosion_element[player_nr];
5364 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5365 element_info[explosion_element].content.e[xx][yy]);
5368 /* restore probably existing indestructible background element */
5369 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5370 element = Feld[x][y] = Back[x][y];
5373 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5374 GfxDir[x][y] = MV_NONE;
5375 ChangeDelay[x][y] = 0;
5376 ChangePage[x][y] = -1;
5378 CustomValue[x][y] = 0;
5380 InitField_WithBug2(x, y, FALSE);
5382 TEST_DrawLevelField(x, y);
5384 TestIfElementTouchesCustomElement(x, y);
5386 if (GFX_CRUMBLED(element))
5387 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5389 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5390 StorePlayer[x][y] = 0;
5392 if (ELEM_IS_PLAYER(element))
5393 RelocatePlayer(x, y, element);
5395 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5397 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5398 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5401 TEST_DrawLevelFieldCrumbled(x, y);
5403 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5405 DrawLevelElement(x, y, Back[x][y]);
5406 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5408 else if (IS_WALKABLE_UNDER(Back[x][y]))
5410 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5411 DrawLevelElementThruMask(x, y, Back[x][y]);
5413 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5414 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5418 void DynaExplode(int ex, int ey)
5421 int dynabomb_element = Feld[ex][ey];
5422 int dynabomb_size = 1;
5423 boolean dynabomb_xl = FALSE;
5424 struct PlayerInfo *player;
5425 static int xy[4][2] =
5433 if (IS_ACTIVE_BOMB(dynabomb_element))
5435 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5436 dynabomb_size = player->dynabomb_size;
5437 dynabomb_xl = player->dynabomb_xl;
5438 player->dynabombs_left++;
5441 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5443 for (i = 0; i < NUM_DIRECTIONS; i++)
5445 for (j = 1; j <= dynabomb_size; j++)
5447 int x = ex + j * xy[i][0];
5448 int y = ey + j * xy[i][1];
5451 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5454 element = Feld[x][y];
5456 /* do not restart explosions of fields with active bombs */
5457 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5460 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5462 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5463 !IS_DIGGABLE(element) && !dynabomb_xl)
5469 void Bang(int x, int y)
5471 int element = MovingOrBlocked2Element(x, y);
5472 int explosion_type = EX_TYPE_NORMAL;
5474 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5476 struct PlayerInfo *player = PLAYERINFO(x, y);
5478 element = Feld[x][y] = player->initial_element;
5480 if (level.use_explosion_element[player->index_nr])
5482 int explosion_element = level.explosion_element[player->index_nr];
5484 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5485 explosion_type = EX_TYPE_CROSS;
5486 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5487 explosion_type = EX_TYPE_CENTER;
5495 case EL_BD_BUTTERFLY:
5498 case EL_DARK_YAMYAM:
5502 RaiseScoreElement(element);
5505 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5506 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5507 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5508 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5509 case EL_DYNABOMB_INCREASE_NUMBER:
5510 case EL_DYNABOMB_INCREASE_SIZE:
5511 case EL_DYNABOMB_INCREASE_POWER:
5512 explosion_type = EX_TYPE_DYNA;
5515 case EL_DC_LANDMINE:
5516 explosion_type = EX_TYPE_CENTER;
5521 case EL_LAMP_ACTIVE:
5522 case EL_AMOEBA_TO_DIAMOND:
5523 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5524 explosion_type = EX_TYPE_CENTER;
5528 if (element_info[element].explosion_type == EXPLODES_CROSS)
5529 explosion_type = EX_TYPE_CROSS;
5530 else if (element_info[element].explosion_type == EXPLODES_1X1)
5531 explosion_type = EX_TYPE_CENTER;
5535 if (explosion_type == EX_TYPE_DYNA)
5538 Explode(x, y, EX_PHASE_START, explosion_type);
5540 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5543 void SplashAcid(int x, int y)
5545 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5546 (!IN_LEV_FIELD(x - 1, y - 2) ||
5547 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5548 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5550 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5551 (!IN_LEV_FIELD(x + 1, y - 2) ||
5552 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5553 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5555 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5558 static void InitBeltMovement()
5560 static int belt_base_element[4] =
5562 EL_CONVEYOR_BELT_1_LEFT,
5563 EL_CONVEYOR_BELT_2_LEFT,
5564 EL_CONVEYOR_BELT_3_LEFT,
5565 EL_CONVEYOR_BELT_4_LEFT
5567 static int belt_base_active_element[4] =
5569 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5570 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5571 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5572 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5577 /* set frame order for belt animation graphic according to belt direction */
5578 for (i = 0; i < NUM_BELTS; i++)
5582 for (j = 0; j < NUM_BELT_PARTS; j++)
5584 int element = belt_base_active_element[belt_nr] + j;
5585 int graphic_1 = el2img(element);
5586 int graphic_2 = el2panelimg(element);
5588 if (game.belt_dir[i] == MV_LEFT)
5590 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5591 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5595 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5596 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5601 SCAN_PLAYFIELD(x, y)
5603 int element = Feld[x][y];
5605 for (i = 0; i < NUM_BELTS; i++)
5607 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5609 int e_belt_nr = getBeltNrFromBeltElement(element);
5612 if (e_belt_nr == belt_nr)
5614 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5616 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5623 static void ToggleBeltSwitch(int x, int y)
5625 static int belt_base_element[4] =
5627 EL_CONVEYOR_BELT_1_LEFT,
5628 EL_CONVEYOR_BELT_2_LEFT,
5629 EL_CONVEYOR_BELT_3_LEFT,
5630 EL_CONVEYOR_BELT_4_LEFT
5632 static int belt_base_active_element[4] =
5634 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5635 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5636 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5637 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5639 static int belt_base_switch_element[4] =
5641 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5642 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5643 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5644 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5646 static int belt_move_dir[4] =
5654 int element = Feld[x][y];
5655 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5656 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5657 int belt_dir = belt_move_dir[belt_dir_nr];
5660 if (!IS_BELT_SWITCH(element))
5663 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5664 game.belt_dir[belt_nr] = belt_dir;
5666 if (belt_dir_nr == 3)
5669 /* set frame order for belt animation graphic according to belt direction */
5670 for (i = 0; i < NUM_BELT_PARTS; i++)
5672 int element = belt_base_active_element[belt_nr] + i;
5673 int graphic_1 = el2img(element);
5674 int graphic_2 = el2panelimg(element);
5676 if (belt_dir == MV_LEFT)
5678 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5679 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5683 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5684 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5688 SCAN_PLAYFIELD(xx, yy)
5690 int element = Feld[xx][yy];
5692 if (IS_BELT_SWITCH(element))
5694 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5696 if (e_belt_nr == belt_nr)
5698 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5699 TEST_DrawLevelField(xx, yy);
5702 else if (IS_BELT(element) && belt_dir != MV_NONE)
5704 int e_belt_nr = getBeltNrFromBeltElement(element);
5706 if (e_belt_nr == belt_nr)
5708 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5710 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5711 TEST_DrawLevelField(xx, yy);
5714 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5716 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5718 if (e_belt_nr == belt_nr)
5720 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5722 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5723 TEST_DrawLevelField(xx, yy);
5729 static void ToggleSwitchgateSwitch(int x, int y)
5733 game.switchgate_pos = !game.switchgate_pos;
5735 SCAN_PLAYFIELD(xx, yy)
5737 int element = Feld[xx][yy];
5739 if (element == EL_SWITCHGATE_SWITCH_UP)
5741 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5742 TEST_DrawLevelField(xx, yy);
5744 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5746 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5747 TEST_DrawLevelField(xx, yy);
5749 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5751 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5752 TEST_DrawLevelField(xx, yy);
5754 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5756 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5757 TEST_DrawLevelField(xx, yy);
5759 else if (element == EL_SWITCHGATE_OPEN ||
5760 element == EL_SWITCHGATE_OPENING)
5762 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5764 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5766 else if (element == EL_SWITCHGATE_CLOSED ||
5767 element == EL_SWITCHGATE_CLOSING)
5769 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5771 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5776 static int getInvisibleActiveFromInvisibleElement(int element)
5778 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5779 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5780 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5784 static int getInvisibleFromInvisibleActiveElement(int element)
5786 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5787 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5788 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5792 static void RedrawAllLightSwitchesAndInvisibleElements()
5796 SCAN_PLAYFIELD(x, y)
5798 int element = Feld[x][y];
5800 if (element == EL_LIGHT_SWITCH &&
5801 game.light_time_left > 0)
5803 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5804 TEST_DrawLevelField(x, y);
5806 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5807 game.light_time_left == 0)
5809 Feld[x][y] = EL_LIGHT_SWITCH;
5810 TEST_DrawLevelField(x, y);
5812 else if (element == EL_EMC_DRIPPER &&
5813 game.light_time_left > 0)
5815 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5816 TEST_DrawLevelField(x, y);
5818 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5819 game.light_time_left == 0)
5821 Feld[x][y] = EL_EMC_DRIPPER;
5822 TEST_DrawLevelField(x, y);
5824 else if (element == EL_INVISIBLE_STEELWALL ||
5825 element == EL_INVISIBLE_WALL ||
5826 element == EL_INVISIBLE_SAND)
5828 if (game.light_time_left > 0)
5829 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5831 TEST_DrawLevelField(x, y);
5833 /* uncrumble neighbour fields, if needed */
5834 if (element == EL_INVISIBLE_SAND)
5835 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5837 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5838 element == EL_INVISIBLE_WALL_ACTIVE ||
5839 element == EL_INVISIBLE_SAND_ACTIVE)
5841 if (game.light_time_left == 0)
5842 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5844 TEST_DrawLevelField(x, y);
5846 /* re-crumble neighbour fields, if needed */
5847 if (element == EL_INVISIBLE_SAND)
5848 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5853 static void RedrawAllInvisibleElementsForLenses()
5857 SCAN_PLAYFIELD(x, y)
5859 int element = Feld[x][y];
5861 if (element == EL_EMC_DRIPPER &&
5862 game.lenses_time_left > 0)
5864 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5865 TEST_DrawLevelField(x, y);
5867 else if (element == EL_EMC_DRIPPER_ACTIVE &&
5868 game.lenses_time_left == 0)
5870 Feld[x][y] = EL_EMC_DRIPPER;
5871 TEST_DrawLevelField(x, y);
5873 else if (element == EL_INVISIBLE_STEELWALL ||
5874 element == EL_INVISIBLE_WALL ||
5875 element == EL_INVISIBLE_SAND)
5877 if (game.lenses_time_left > 0)
5878 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5880 TEST_DrawLevelField(x, y);
5882 /* uncrumble neighbour fields, if needed */
5883 if (element == EL_INVISIBLE_SAND)
5884 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5886 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5887 element == EL_INVISIBLE_WALL_ACTIVE ||
5888 element == EL_INVISIBLE_SAND_ACTIVE)
5890 if (game.lenses_time_left == 0)
5891 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5893 TEST_DrawLevelField(x, y);
5895 /* re-crumble neighbour fields, if needed */
5896 if (element == EL_INVISIBLE_SAND)
5897 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5902 static void RedrawAllInvisibleElementsForMagnifier()
5906 SCAN_PLAYFIELD(x, y)
5908 int element = Feld[x][y];
5910 if (element == EL_EMC_FAKE_GRASS &&
5911 game.magnify_time_left > 0)
5913 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5914 TEST_DrawLevelField(x, y);
5916 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5917 game.magnify_time_left == 0)
5919 Feld[x][y] = EL_EMC_FAKE_GRASS;
5920 TEST_DrawLevelField(x, y);
5922 else if (IS_GATE_GRAY(element) &&
5923 game.magnify_time_left > 0)
5925 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5926 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5927 IS_EM_GATE_GRAY(element) ?
5928 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5929 IS_EMC_GATE_GRAY(element) ?
5930 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5931 IS_DC_GATE_GRAY(element) ?
5932 EL_DC_GATE_WHITE_GRAY_ACTIVE :
5934 TEST_DrawLevelField(x, y);
5936 else if (IS_GATE_GRAY_ACTIVE(element) &&
5937 game.magnify_time_left == 0)
5939 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5940 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5941 IS_EM_GATE_GRAY_ACTIVE(element) ?
5942 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5943 IS_EMC_GATE_GRAY_ACTIVE(element) ?
5944 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5945 IS_DC_GATE_GRAY_ACTIVE(element) ?
5946 EL_DC_GATE_WHITE_GRAY :
5948 TEST_DrawLevelField(x, y);
5953 static void ToggleLightSwitch(int x, int y)
5955 int element = Feld[x][y];
5957 game.light_time_left =
5958 (element == EL_LIGHT_SWITCH ?
5959 level.time_light * FRAMES_PER_SECOND : 0);
5961 RedrawAllLightSwitchesAndInvisibleElements();
5964 static void ActivateTimegateSwitch(int x, int y)
5968 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5970 SCAN_PLAYFIELD(xx, yy)
5972 int element = Feld[xx][yy];
5974 if (element == EL_TIMEGATE_CLOSED ||
5975 element == EL_TIMEGATE_CLOSING)
5977 Feld[xx][yy] = EL_TIMEGATE_OPENING;
5978 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5982 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5984 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5985 TEST_DrawLevelField(xx, yy);
5991 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5992 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5995 void Impact(int x, int y)
5997 boolean last_line = (y == lev_fieldy - 1);
5998 boolean object_hit = FALSE;
5999 boolean impact = (last_line || object_hit);
6000 int element = Feld[x][y];
6001 int smashed = EL_STEELWALL;
6003 if (!last_line) /* check if element below was hit */
6005 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6008 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6009 MovDir[x][y + 1] != MV_DOWN ||
6010 MovPos[x][y + 1] <= TILEY / 2));
6012 /* do not smash moving elements that left the smashed field in time */
6013 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6014 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6017 #if USE_QUICKSAND_IMPACT_BUGFIX
6018 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6020 RemoveMovingField(x, y + 1);
6021 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6022 Feld[x][y + 2] = EL_ROCK;
6023 TEST_DrawLevelField(x, y + 2);
6028 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6030 RemoveMovingField(x, y + 1);
6031 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6032 Feld[x][y + 2] = EL_ROCK;
6033 TEST_DrawLevelField(x, y + 2);
6040 smashed = MovingOrBlocked2Element(x, y + 1);
6042 impact = (last_line || object_hit);
6045 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6047 SplashAcid(x, y + 1);
6051 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6052 /* only reset graphic animation if graphic really changes after impact */
6054 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6056 ResetGfxAnimation(x, y);
6057 TEST_DrawLevelField(x, y);
6060 if (impact && CAN_EXPLODE_IMPACT(element))
6065 else if (impact && element == EL_PEARL &&
6066 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6068 ResetGfxAnimation(x, y);
6070 Feld[x][y] = EL_PEARL_BREAKING;
6071 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6074 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6076 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6081 if (impact && element == EL_AMOEBA_DROP)
6083 if (object_hit && IS_PLAYER(x, y + 1))
6084 KillPlayerUnlessEnemyProtected(x, y + 1);
6085 else if (object_hit && smashed == EL_PENGUIN)
6089 Feld[x][y] = EL_AMOEBA_GROWING;
6090 Store[x][y] = EL_AMOEBA_WET;
6092 ResetRandomAnimationValue(x, y);
6097 if (object_hit) /* check which object was hit */
6099 if ((CAN_PASS_MAGIC_WALL(element) &&
6100 (smashed == EL_MAGIC_WALL ||
6101 smashed == EL_BD_MAGIC_WALL)) ||
6102 (CAN_PASS_DC_MAGIC_WALL(element) &&
6103 smashed == EL_DC_MAGIC_WALL))
6106 int activated_magic_wall =
6107 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6108 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6109 EL_DC_MAGIC_WALL_ACTIVE);
6111 /* activate magic wall / mill */
6112 SCAN_PLAYFIELD(xx, yy)
6114 if (Feld[xx][yy] == smashed)
6115 Feld[xx][yy] = activated_magic_wall;
6118 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6119 game.magic_wall_active = TRUE;
6121 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6122 SND_MAGIC_WALL_ACTIVATING :
6123 smashed == EL_BD_MAGIC_WALL ?
6124 SND_BD_MAGIC_WALL_ACTIVATING :
6125 SND_DC_MAGIC_WALL_ACTIVATING));
6128 if (IS_PLAYER(x, y + 1))
6130 if (CAN_SMASH_PLAYER(element))
6132 KillPlayerUnlessEnemyProtected(x, y + 1);
6136 else if (smashed == EL_PENGUIN)
6138 if (CAN_SMASH_PLAYER(element))
6144 else if (element == EL_BD_DIAMOND)
6146 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6152 else if (((element == EL_SP_INFOTRON ||
6153 element == EL_SP_ZONK) &&
6154 (smashed == EL_SP_SNIKSNAK ||
6155 smashed == EL_SP_ELECTRON ||
6156 smashed == EL_SP_DISK_ORANGE)) ||
6157 (element == EL_SP_INFOTRON &&
6158 smashed == EL_SP_DISK_YELLOW))
6163 else if (CAN_SMASH_EVERYTHING(element))
6165 if (IS_CLASSIC_ENEMY(smashed) ||
6166 CAN_EXPLODE_SMASHED(smashed))
6171 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6173 if (smashed == EL_LAMP ||
6174 smashed == EL_LAMP_ACTIVE)
6179 else if (smashed == EL_NUT)
6181 Feld[x][y + 1] = EL_NUT_BREAKING;
6182 PlayLevelSound(x, y, SND_NUT_BREAKING);
6183 RaiseScoreElement(EL_NUT);
6186 else if (smashed == EL_PEARL)
6188 ResetGfxAnimation(x, y);
6190 Feld[x][y + 1] = EL_PEARL_BREAKING;
6191 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6194 else if (smashed == EL_DIAMOND)
6196 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6197 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6200 else if (IS_BELT_SWITCH(smashed))
6202 ToggleBeltSwitch(x, y + 1);
6204 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6205 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6206 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6207 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6209 ToggleSwitchgateSwitch(x, y + 1);
6211 else if (smashed == EL_LIGHT_SWITCH ||
6212 smashed == EL_LIGHT_SWITCH_ACTIVE)
6214 ToggleLightSwitch(x, y + 1);
6218 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6220 CheckElementChangeBySide(x, y + 1, smashed, element,
6221 CE_SWITCHED, CH_SIDE_TOP);
6222 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6228 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6233 /* play sound of magic wall / mill */
6235 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6236 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6237 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6239 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6240 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6241 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6242 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6243 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6244 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6249 /* play sound of object that hits the ground */
6250 if (last_line || object_hit)
6251 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6254 inline static void TurnRoundExt(int x, int y)
6266 { 0, 0 }, { 0, 0 }, { 0, 0 },
6271 int left, right, back;
6275 { MV_DOWN, MV_UP, MV_RIGHT },
6276 { MV_UP, MV_DOWN, MV_LEFT },
6278 { MV_LEFT, MV_RIGHT, MV_DOWN },
6282 { MV_RIGHT, MV_LEFT, MV_UP }
6285 int element = Feld[x][y];
6286 int move_pattern = element_info[element].move_pattern;
6288 int old_move_dir = MovDir[x][y];
6289 int left_dir = turn[old_move_dir].left;
6290 int right_dir = turn[old_move_dir].right;
6291 int back_dir = turn[old_move_dir].back;
6293 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6294 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6295 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6296 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6298 int left_x = x + left_dx, left_y = y + left_dy;
6299 int right_x = x + right_dx, right_y = y + right_dy;
6300 int move_x = x + move_dx, move_y = y + move_dy;
6304 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6306 TestIfBadThingTouchesOtherBadThing(x, y);
6308 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6309 MovDir[x][y] = right_dir;
6310 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6311 MovDir[x][y] = left_dir;
6313 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6315 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6318 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6320 TestIfBadThingTouchesOtherBadThing(x, y);
6322 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6323 MovDir[x][y] = left_dir;
6324 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6325 MovDir[x][y] = right_dir;
6327 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6329 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6332 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6334 TestIfBadThingTouchesOtherBadThing(x, y);
6336 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6337 MovDir[x][y] = left_dir;
6338 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6339 MovDir[x][y] = right_dir;
6341 if (MovDir[x][y] != old_move_dir)
6344 else if (element == EL_YAMYAM)
6346 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6347 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6349 if (can_turn_left && can_turn_right)
6350 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6351 else if (can_turn_left)
6352 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6353 else if (can_turn_right)
6354 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6356 MovDir[x][y] = back_dir;
6358 MovDelay[x][y] = 16 + 16 * RND(3);
6360 else if (element == EL_DARK_YAMYAM)
6362 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6364 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6367 if (can_turn_left && can_turn_right)
6368 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6369 else if (can_turn_left)
6370 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6371 else if (can_turn_right)
6372 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6374 MovDir[x][y] = back_dir;
6376 MovDelay[x][y] = 16 + 16 * RND(3);
6378 else if (element == EL_PACMAN)
6380 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6381 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6383 if (can_turn_left && can_turn_right)
6384 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6385 else if (can_turn_left)
6386 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6387 else if (can_turn_right)
6388 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6390 MovDir[x][y] = back_dir;
6392 MovDelay[x][y] = 6 + RND(40);
6394 else if (element == EL_PIG)
6396 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6397 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6398 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6399 boolean should_turn_left, should_turn_right, should_move_on;
6401 int rnd = RND(rnd_value);
6403 should_turn_left = (can_turn_left &&
6405 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6406 y + back_dy + left_dy)));
6407 should_turn_right = (can_turn_right &&
6409 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6410 y + back_dy + right_dy)));
6411 should_move_on = (can_move_on &&
6414 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6415 y + move_dy + left_dy) ||
6416 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6417 y + move_dy + right_dy)));
6419 if (should_turn_left || should_turn_right || should_move_on)
6421 if (should_turn_left && should_turn_right && should_move_on)
6422 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6423 rnd < 2 * rnd_value / 3 ? right_dir :
6425 else if (should_turn_left && should_turn_right)
6426 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6427 else if (should_turn_left && should_move_on)
6428 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6429 else if (should_turn_right && should_move_on)
6430 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6431 else if (should_turn_left)
6432 MovDir[x][y] = left_dir;
6433 else if (should_turn_right)
6434 MovDir[x][y] = right_dir;
6435 else if (should_move_on)
6436 MovDir[x][y] = old_move_dir;
6438 else if (can_move_on && rnd > rnd_value / 8)
6439 MovDir[x][y] = old_move_dir;
6440 else if (can_turn_left && can_turn_right)
6441 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6442 else if (can_turn_left && rnd > rnd_value / 8)
6443 MovDir[x][y] = left_dir;
6444 else if (can_turn_right && rnd > rnd_value/8)
6445 MovDir[x][y] = right_dir;
6447 MovDir[x][y] = back_dir;
6449 xx = x + move_xy[MovDir[x][y]].dx;
6450 yy = y + move_xy[MovDir[x][y]].dy;
6452 if (!IN_LEV_FIELD(xx, yy) ||
6453 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6454 MovDir[x][y] = old_move_dir;
6458 else if (element == EL_DRAGON)
6460 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6461 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6462 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6464 int rnd = RND(rnd_value);
6466 if (can_move_on && rnd > rnd_value / 8)
6467 MovDir[x][y] = old_move_dir;
6468 else if (can_turn_left && can_turn_right)
6469 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6470 else if (can_turn_left && rnd > rnd_value / 8)
6471 MovDir[x][y] = left_dir;
6472 else if (can_turn_right && rnd > rnd_value / 8)
6473 MovDir[x][y] = right_dir;
6475 MovDir[x][y] = back_dir;
6477 xx = x + move_xy[MovDir[x][y]].dx;
6478 yy = y + move_xy[MovDir[x][y]].dy;
6480 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6481 MovDir[x][y] = old_move_dir;
6485 else if (element == EL_MOLE)
6487 boolean can_move_on =
6488 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6489 IS_AMOEBOID(Feld[move_x][move_y]) ||
6490 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6493 boolean can_turn_left =
6494 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6495 IS_AMOEBOID(Feld[left_x][left_y])));
6497 boolean can_turn_right =
6498 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6499 IS_AMOEBOID(Feld[right_x][right_y])));
6501 if (can_turn_left && can_turn_right)
6502 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6503 else if (can_turn_left)
6504 MovDir[x][y] = left_dir;
6506 MovDir[x][y] = right_dir;
6509 if (MovDir[x][y] != old_move_dir)
6512 else if (element == EL_BALLOON)
6514 MovDir[x][y] = game.wind_direction;
6517 else if (element == EL_SPRING)
6519 if (MovDir[x][y] & MV_HORIZONTAL)
6521 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6522 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6524 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6525 ResetGfxAnimation(move_x, move_y);
6526 TEST_DrawLevelField(move_x, move_y);
6528 MovDir[x][y] = back_dir;
6530 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6531 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6532 MovDir[x][y] = MV_NONE;
6537 else if (element == EL_ROBOT ||
6538 element == EL_SATELLITE ||
6539 element == EL_PENGUIN ||
6540 element == EL_EMC_ANDROID)
6542 int attr_x = -1, attr_y = -1;
6553 for (i = 0; i < MAX_PLAYERS; i++)
6555 struct PlayerInfo *player = &stored_player[i];
6556 int jx = player->jx, jy = player->jy;
6558 if (!player->active)
6562 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6570 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6571 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6572 game.engine_version < VERSION_IDENT(3,1,0,0)))
6578 if (element == EL_PENGUIN)
6581 static int xy[4][2] =
6589 for (i = 0; i < NUM_DIRECTIONS; i++)
6591 int ex = x + xy[i][0];
6592 int ey = y + xy[i][1];
6594 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6595 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6596 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6597 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6606 MovDir[x][y] = MV_NONE;
6608 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6609 else if (attr_x > x)
6610 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6612 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6613 else if (attr_y > y)
6614 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6616 if (element == EL_ROBOT)
6620 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6621 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6622 Moving2Blocked(x, y, &newx, &newy);
6624 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6625 MovDelay[x][y] = 8 + 8 * !RND(3);
6627 MovDelay[x][y] = 16;
6629 else if (element == EL_PENGUIN)
6635 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6637 boolean first_horiz = RND(2);
6638 int new_move_dir = MovDir[x][y];
6641 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6642 Moving2Blocked(x, y, &newx, &newy);
6644 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6648 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6649 Moving2Blocked(x, y, &newx, &newy);
6651 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6654 MovDir[x][y] = old_move_dir;
6658 else if (element == EL_SATELLITE)
6664 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6666 boolean first_horiz = RND(2);
6667 int new_move_dir = MovDir[x][y];
6670 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6671 Moving2Blocked(x, y, &newx, &newy);
6673 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6677 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6678 Moving2Blocked(x, y, &newx, &newy);
6680 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6683 MovDir[x][y] = old_move_dir;
6687 else if (element == EL_EMC_ANDROID)
6689 static int check_pos[16] =
6691 -1, /* 0 => (invalid) */
6692 7, /* 1 => MV_LEFT */
6693 3, /* 2 => MV_RIGHT */
6694 -1, /* 3 => (invalid) */
6696 0, /* 5 => MV_LEFT | MV_UP */
6697 2, /* 6 => MV_RIGHT | MV_UP */
6698 -1, /* 7 => (invalid) */
6699 5, /* 8 => MV_DOWN */
6700 6, /* 9 => MV_LEFT | MV_DOWN */
6701 4, /* 10 => MV_RIGHT | MV_DOWN */
6702 -1, /* 11 => (invalid) */
6703 -1, /* 12 => (invalid) */
6704 -1, /* 13 => (invalid) */
6705 -1, /* 14 => (invalid) */
6706 -1, /* 15 => (invalid) */
6714 { -1, -1, MV_LEFT | MV_UP },
6716 { +1, -1, MV_RIGHT | MV_UP },
6717 { +1, 0, MV_RIGHT },
6718 { +1, +1, MV_RIGHT | MV_DOWN },
6720 { -1, +1, MV_LEFT | MV_DOWN },
6723 int start_pos, check_order;
6724 boolean can_clone = FALSE;
6727 /* check if there is any free field around current position */
6728 for (i = 0; i < 8; i++)
6730 int newx = x + check_xy[i].dx;
6731 int newy = y + check_xy[i].dy;
6733 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6741 if (can_clone) /* randomly find an element to clone */
6745 start_pos = check_pos[RND(8)];
6746 check_order = (RND(2) ? -1 : +1);
6748 for (i = 0; i < 8; i++)
6750 int pos_raw = start_pos + i * check_order;
6751 int pos = (pos_raw + 8) % 8;
6752 int newx = x + check_xy[pos].dx;
6753 int newy = y + check_xy[pos].dy;
6755 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6757 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6758 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6760 Store[x][y] = Feld[newx][newy];
6769 if (can_clone) /* randomly find a direction to move */
6773 start_pos = check_pos[RND(8)];
6774 check_order = (RND(2) ? -1 : +1);
6776 for (i = 0; i < 8; i++)
6778 int pos_raw = start_pos + i * check_order;
6779 int pos = (pos_raw + 8) % 8;
6780 int newx = x + check_xy[pos].dx;
6781 int newy = y + check_xy[pos].dy;
6782 int new_move_dir = check_xy[pos].dir;
6784 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6786 MovDir[x][y] = new_move_dir;
6787 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6796 if (can_clone) /* cloning and moving successful */
6799 /* cannot clone -- try to move towards player */
6801 start_pos = check_pos[MovDir[x][y] & 0x0f];
6802 check_order = (RND(2) ? -1 : +1);
6804 for (i = 0; i < 3; i++)
6806 /* first check start_pos, then previous/next or (next/previous) pos */
6807 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6808 int pos = (pos_raw + 8) % 8;
6809 int newx = x + check_xy[pos].dx;
6810 int newy = y + check_xy[pos].dy;
6811 int new_move_dir = check_xy[pos].dir;
6813 if (IS_PLAYER(newx, newy))
6816 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6818 MovDir[x][y] = new_move_dir;
6819 MovDelay[x][y] = level.android_move_time * 8 + 1;
6826 else if (move_pattern == MV_TURNING_LEFT ||
6827 move_pattern == MV_TURNING_RIGHT ||
6828 move_pattern == MV_TURNING_LEFT_RIGHT ||
6829 move_pattern == MV_TURNING_RIGHT_LEFT ||
6830 move_pattern == MV_TURNING_RANDOM ||
6831 move_pattern == MV_ALL_DIRECTIONS)
6833 boolean can_turn_left =
6834 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6835 boolean can_turn_right =
6836 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6838 if (element_info[element].move_stepsize == 0) /* "not moving" */
6841 if (move_pattern == MV_TURNING_LEFT)
6842 MovDir[x][y] = left_dir;
6843 else if (move_pattern == MV_TURNING_RIGHT)
6844 MovDir[x][y] = right_dir;
6845 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6846 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6847 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6848 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6849 else if (move_pattern == MV_TURNING_RANDOM)
6850 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6851 can_turn_right && !can_turn_left ? right_dir :
6852 RND(2) ? left_dir : right_dir);
6853 else if (can_turn_left && can_turn_right)
6854 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6855 else if (can_turn_left)
6856 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6857 else if (can_turn_right)
6858 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6860 MovDir[x][y] = back_dir;
6862 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6864 else if (move_pattern == MV_HORIZONTAL ||
6865 move_pattern == MV_VERTICAL)
6867 if (move_pattern & old_move_dir)
6868 MovDir[x][y] = back_dir;
6869 else if (move_pattern == MV_HORIZONTAL)
6870 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6871 else if (move_pattern == MV_VERTICAL)
6872 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6874 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6876 else if (move_pattern & MV_ANY_DIRECTION)
6878 MovDir[x][y] = move_pattern;
6879 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6881 else if (move_pattern & MV_WIND_DIRECTION)
6883 MovDir[x][y] = game.wind_direction;
6884 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6886 else if (move_pattern == MV_ALONG_LEFT_SIDE)
6888 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6889 MovDir[x][y] = left_dir;
6890 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6891 MovDir[x][y] = right_dir;
6893 if (MovDir[x][y] != old_move_dir)
6894 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6896 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6898 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6899 MovDir[x][y] = right_dir;
6900 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6901 MovDir[x][y] = left_dir;
6903 if (MovDir[x][y] != old_move_dir)
6904 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6906 else if (move_pattern == MV_TOWARDS_PLAYER ||
6907 move_pattern == MV_AWAY_FROM_PLAYER)
6909 int attr_x = -1, attr_y = -1;
6911 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6922 for (i = 0; i < MAX_PLAYERS; i++)
6924 struct PlayerInfo *player = &stored_player[i];
6925 int jx = player->jx, jy = player->jy;
6927 if (!player->active)
6931 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6939 MovDir[x][y] = MV_NONE;
6941 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6942 else if (attr_x > x)
6943 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6945 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6946 else if (attr_y > y)
6947 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6949 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6951 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6953 boolean first_horiz = RND(2);
6954 int new_move_dir = MovDir[x][y];
6956 if (element_info[element].move_stepsize == 0) /* "not moving" */
6958 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6959 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6965 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6966 Moving2Blocked(x, y, &newx, &newy);
6968 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6972 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6973 Moving2Blocked(x, y, &newx, &newy);
6975 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6978 MovDir[x][y] = old_move_dir;
6981 else if (move_pattern == MV_WHEN_PUSHED ||
6982 move_pattern == MV_WHEN_DROPPED)
6984 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6985 MovDir[x][y] = MV_NONE;
6989 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6991 static int test_xy[7][2] =
7001 static int test_dir[7] =
7011 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7012 int move_preference = -1000000; /* start with very low preference */
7013 int new_move_dir = MV_NONE;
7014 int start_test = RND(4);
7017 for (i = 0; i < NUM_DIRECTIONS; i++)
7019 int move_dir = test_dir[start_test + i];
7020 int move_dir_preference;
7022 xx = x + test_xy[start_test + i][0];
7023 yy = y + test_xy[start_test + i][1];
7025 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7026 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7028 new_move_dir = move_dir;
7033 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7036 move_dir_preference = -1 * RunnerVisit[xx][yy];
7037 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7038 move_dir_preference = PlayerVisit[xx][yy];
7040 if (move_dir_preference > move_preference)
7042 /* prefer field that has not been visited for the longest time */
7043 move_preference = move_dir_preference;
7044 new_move_dir = move_dir;
7046 else if (move_dir_preference == move_preference &&
7047 move_dir == old_move_dir)
7049 /* prefer last direction when all directions are preferred equally */
7050 move_preference = move_dir_preference;
7051 new_move_dir = move_dir;
7055 MovDir[x][y] = new_move_dir;
7056 if (old_move_dir != new_move_dir)
7057 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7061 static void TurnRound(int x, int y)
7063 int direction = MovDir[x][y];
7067 GfxDir[x][y] = MovDir[x][y];
7069 if (direction != MovDir[x][y])
7073 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7075 ResetGfxFrame(x, y);
7078 static boolean JustBeingPushed(int x, int y)
7082 for (i = 0; i < MAX_PLAYERS; i++)
7084 struct PlayerInfo *player = &stored_player[i];
7086 if (player->active && player->is_pushing && player->MovPos)
7088 int next_jx = player->jx + (player->jx - player->last_jx);
7089 int next_jy = player->jy + (player->jy - player->last_jy);
7091 if (x == next_jx && y == next_jy)
7099 void StartMoving(int x, int y)
7101 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7102 int element = Feld[x][y];
7107 if (MovDelay[x][y] == 0)
7108 GfxAction[x][y] = ACTION_DEFAULT;
7110 if (CAN_FALL(element) && y < lev_fieldy - 1)
7112 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7113 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7114 if (JustBeingPushed(x, y))
7117 if (element == EL_QUICKSAND_FULL)
7119 if (IS_FREE(x, y + 1))
7121 InitMovingField(x, y, MV_DOWN);
7122 started_moving = TRUE;
7124 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7125 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7126 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7127 Store[x][y] = EL_ROCK;
7129 Store[x][y] = EL_ROCK;
7132 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7134 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7136 if (!MovDelay[x][y])
7138 MovDelay[x][y] = TILEY + 1;
7140 ResetGfxAnimation(x, y);
7141 ResetGfxAnimation(x, y + 1);
7146 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7147 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7154 Feld[x][y] = EL_QUICKSAND_EMPTY;
7155 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7156 Store[x][y + 1] = Store[x][y];
7159 PlayLevelSoundAction(x, y, ACTION_FILLING);
7161 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7163 if (!MovDelay[x][y])
7165 MovDelay[x][y] = TILEY + 1;
7167 ResetGfxAnimation(x, y);
7168 ResetGfxAnimation(x, y + 1);
7173 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7174 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7181 Feld[x][y] = EL_QUICKSAND_EMPTY;
7182 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7183 Store[x][y + 1] = Store[x][y];
7186 PlayLevelSoundAction(x, y, ACTION_FILLING);
7189 else if (element == EL_QUICKSAND_FAST_FULL)
7191 if (IS_FREE(x, y + 1))
7193 InitMovingField(x, y, MV_DOWN);
7194 started_moving = TRUE;
7196 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7197 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7198 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7199 Store[x][y] = EL_ROCK;
7201 Store[x][y] = EL_ROCK;
7204 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7206 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7208 if (!MovDelay[x][y])
7210 MovDelay[x][y] = TILEY + 1;
7212 ResetGfxAnimation(x, y);
7213 ResetGfxAnimation(x, y + 1);
7218 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7219 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7226 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7227 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7228 Store[x][y + 1] = Store[x][y];
7231 PlayLevelSoundAction(x, y, ACTION_FILLING);
7233 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7235 if (!MovDelay[x][y])
7237 MovDelay[x][y] = TILEY + 1;
7239 ResetGfxAnimation(x, y);
7240 ResetGfxAnimation(x, y + 1);
7245 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7246 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7253 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7254 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7255 Store[x][y + 1] = Store[x][y];
7258 PlayLevelSoundAction(x, y, ACTION_FILLING);
7261 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7262 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7264 InitMovingField(x, y, MV_DOWN);
7265 started_moving = TRUE;
7267 Feld[x][y] = EL_QUICKSAND_FILLING;
7268 Store[x][y] = element;
7270 PlayLevelSoundAction(x, y, ACTION_FILLING);
7272 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7273 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7275 InitMovingField(x, y, MV_DOWN);
7276 started_moving = TRUE;
7278 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7279 Store[x][y] = element;
7281 PlayLevelSoundAction(x, y, ACTION_FILLING);
7283 else if (element == EL_MAGIC_WALL_FULL)
7285 if (IS_FREE(x, y + 1))
7287 InitMovingField(x, y, MV_DOWN);
7288 started_moving = TRUE;
7290 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7291 Store[x][y] = EL_CHANGED(Store[x][y]);
7293 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7295 if (!MovDelay[x][y])
7296 MovDelay[x][y] = TILEY / 4 + 1;
7305 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7306 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7307 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7311 else if (element == EL_BD_MAGIC_WALL_FULL)
7313 if (IS_FREE(x, y + 1))
7315 InitMovingField(x, y, MV_DOWN);
7316 started_moving = TRUE;
7318 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7319 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7321 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7323 if (!MovDelay[x][y])
7324 MovDelay[x][y] = TILEY / 4 + 1;
7333 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7334 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7335 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7339 else if (element == EL_DC_MAGIC_WALL_FULL)
7341 if (IS_FREE(x, y + 1))
7343 InitMovingField(x, y, MV_DOWN);
7344 started_moving = TRUE;
7346 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7347 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7349 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7351 if (!MovDelay[x][y])
7352 MovDelay[x][y] = TILEY / 4 + 1;
7361 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7362 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7363 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7367 else if ((CAN_PASS_MAGIC_WALL(element) &&
7368 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7369 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7370 (CAN_PASS_DC_MAGIC_WALL(element) &&
7371 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7374 InitMovingField(x, y, MV_DOWN);
7375 started_moving = TRUE;
7378 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7379 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7380 EL_DC_MAGIC_WALL_FILLING);
7381 Store[x][y] = element;
7383 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7385 SplashAcid(x, y + 1);
7387 InitMovingField(x, y, MV_DOWN);
7388 started_moving = TRUE;
7390 Store[x][y] = EL_ACID;
7393 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7394 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7395 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7396 CAN_FALL(element) && WasJustFalling[x][y] &&
7397 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7399 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7400 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7401 (Feld[x][y + 1] == EL_BLOCKED)))
7403 /* this is needed for a special case not covered by calling "Impact()"
7404 from "ContinueMoving()": if an element moves to a tile directly below
7405 another element which was just falling on that tile (which was empty
7406 in the previous frame), the falling element above would just stop
7407 instead of smashing the element below (in previous version, the above
7408 element was just checked for "moving" instead of "falling", resulting
7409 in incorrect smashes caused by horizontal movement of the above
7410 element; also, the case of the player being the element to smash was
7411 simply not covered here... :-/ ) */
7413 CheckCollision[x][y] = 0;
7414 CheckImpact[x][y] = 0;
7418 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7420 if (MovDir[x][y] == MV_NONE)
7422 InitMovingField(x, y, MV_DOWN);
7423 started_moving = TRUE;
7426 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7428 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7429 MovDir[x][y] = MV_DOWN;
7431 InitMovingField(x, y, MV_DOWN);
7432 started_moving = TRUE;
7434 else if (element == EL_AMOEBA_DROP)
7436 Feld[x][y] = EL_AMOEBA_GROWING;
7437 Store[x][y] = EL_AMOEBA_WET;
7439 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7440 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7441 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7442 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7444 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7445 (IS_FREE(x - 1, y + 1) ||
7446 Feld[x - 1][y + 1] == EL_ACID));
7447 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7448 (IS_FREE(x + 1, y + 1) ||
7449 Feld[x + 1][y + 1] == EL_ACID));
7450 boolean can_fall_any = (can_fall_left || can_fall_right);
7451 boolean can_fall_both = (can_fall_left && can_fall_right);
7452 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7454 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7456 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7457 can_fall_right = FALSE;
7458 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7459 can_fall_left = FALSE;
7460 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7461 can_fall_right = FALSE;
7462 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7463 can_fall_left = FALSE;
7465 can_fall_any = (can_fall_left || can_fall_right);
7466 can_fall_both = FALSE;
7471 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7472 can_fall_right = FALSE; /* slip down on left side */
7474 can_fall_left = !(can_fall_right = RND(2));
7476 can_fall_both = FALSE;
7481 /* if not determined otherwise, prefer left side for slipping down */
7482 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7483 started_moving = TRUE;
7486 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7488 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7489 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7490 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7491 int belt_dir = game.belt_dir[belt_nr];
7493 if ((belt_dir == MV_LEFT && left_is_free) ||
7494 (belt_dir == MV_RIGHT && right_is_free))
7496 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7498 InitMovingField(x, y, belt_dir);
7499 started_moving = TRUE;
7501 Pushed[x][y] = TRUE;
7502 Pushed[nextx][y] = TRUE;
7504 GfxAction[x][y] = ACTION_DEFAULT;
7508 MovDir[x][y] = 0; /* if element was moving, stop it */
7513 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7514 if (CAN_MOVE(element) && !started_moving)
7516 int move_pattern = element_info[element].move_pattern;
7519 Moving2Blocked(x, y, &newx, &newy);
7521 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7524 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7525 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7527 WasJustMoving[x][y] = 0;
7528 CheckCollision[x][y] = 0;
7530 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7532 if (Feld[x][y] != element) /* element has changed */
7536 if (!MovDelay[x][y]) /* start new movement phase */
7538 /* all objects that can change their move direction after each step
7539 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7541 if (element != EL_YAMYAM &&
7542 element != EL_DARK_YAMYAM &&
7543 element != EL_PACMAN &&
7544 !(move_pattern & MV_ANY_DIRECTION) &&
7545 move_pattern != MV_TURNING_LEFT &&
7546 move_pattern != MV_TURNING_RIGHT &&
7547 move_pattern != MV_TURNING_LEFT_RIGHT &&
7548 move_pattern != MV_TURNING_RIGHT_LEFT &&
7549 move_pattern != MV_TURNING_RANDOM)
7553 if (MovDelay[x][y] && (element == EL_BUG ||
7554 element == EL_SPACESHIP ||
7555 element == EL_SP_SNIKSNAK ||
7556 element == EL_SP_ELECTRON ||
7557 element == EL_MOLE))
7558 TEST_DrawLevelField(x, y);
7562 if (MovDelay[x][y]) /* wait some time before next movement */
7566 if (element == EL_ROBOT ||
7567 element == EL_YAMYAM ||
7568 element == EL_DARK_YAMYAM)
7570 DrawLevelElementAnimationIfNeeded(x, y, element);
7571 PlayLevelSoundAction(x, y, ACTION_WAITING);
7573 else if (element == EL_SP_ELECTRON)
7574 DrawLevelElementAnimationIfNeeded(x, y, element);
7575 else if (element == EL_DRAGON)
7578 int dir = MovDir[x][y];
7579 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7580 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7581 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7582 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7583 dir == MV_UP ? IMG_FLAMES_1_UP :
7584 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7585 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7587 GfxAction[x][y] = ACTION_ATTACKING;
7589 if (IS_PLAYER(x, y))
7590 DrawPlayerField(x, y);
7592 TEST_DrawLevelField(x, y);
7594 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7596 for (i = 1; i <= 3; i++)
7598 int xx = x + i * dx;
7599 int yy = y + i * dy;
7600 int sx = SCREENX(xx);
7601 int sy = SCREENY(yy);
7602 int flame_graphic = graphic + (i - 1);
7604 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7609 int flamed = MovingOrBlocked2Element(xx, yy);
7611 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7614 RemoveMovingField(xx, yy);
7616 ChangeDelay[xx][yy] = 0;
7618 Feld[xx][yy] = EL_FLAMES;
7620 if (IN_SCR_FIELD(sx, sy))
7622 TEST_DrawLevelFieldCrumbled(xx, yy);
7623 DrawGraphic(sx, sy, flame_graphic, frame);
7628 if (Feld[xx][yy] == EL_FLAMES)
7629 Feld[xx][yy] = EL_EMPTY;
7630 TEST_DrawLevelField(xx, yy);
7635 if (MovDelay[x][y]) /* element still has to wait some time */
7637 PlayLevelSoundAction(x, y, ACTION_WAITING);
7643 /* now make next step */
7645 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7647 if (DONT_COLLIDE_WITH(element) &&
7648 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7649 !PLAYER_ENEMY_PROTECTED(newx, newy))
7651 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7656 else if (CAN_MOVE_INTO_ACID(element) &&
7657 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7658 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7659 (MovDir[x][y] == MV_DOWN ||
7660 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7662 SplashAcid(newx, newy);
7663 Store[x][y] = EL_ACID;
7665 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7667 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7668 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7669 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7670 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7673 TEST_DrawLevelField(x, y);
7675 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7676 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7677 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7679 local_player->friends_still_needed--;
7680 if (!local_player->friends_still_needed &&
7681 !local_player->GameOver && AllPlayersGone)
7682 PlayerWins(local_player);
7686 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7688 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7689 TEST_DrawLevelField(newx, newy);
7691 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7693 else if (!IS_FREE(newx, newy))
7695 GfxAction[x][y] = ACTION_WAITING;
7697 if (IS_PLAYER(x, y))
7698 DrawPlayerField(x, y);
7700 TEST_DrawLevelField(x, y);
7705 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7707 if (IS_FOOD_PIG(Feld[newx][newy]))
7709 if (IS_MOVING(newx, newy))
7710 RemoveMovingField(newx, newy);
7713 Feld[newx][newy] = EL_EMPTY;
7714 TEST_DrawLevelField(newx, newy);
7717 PlayLevelSound(x, y, SND_PIG_DIGGING);
7719 else if (!IS_FREE(newx, newy))
7721 if (IS_PLAYER(x, y))
7722 DrawPlayerField(x, y);
7724 TEST_DrawLevelField(x, y);
7729 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7731 if (Store[x][y] != EL_EMPTY)
7733 boolean can_clone = FALSE;
7736 /* check if element to clone is still there */
7737 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7739 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7747 /* cannot clone or target field not free anymore -- do not clone */
7748 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7749 Store[x][y] = EL_EMPTY;
7752 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7754 if (IS_MV_DIAGONAL(MovDir[x][y]))
7756 int diagonal_move_dir = MovDir[x][y];
7757 int stored = Store[x][y];
7758 int change_delay = 8;
7761 /* android is moving diagonally */
7763 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7765 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7766 GfxElement[x][y] = EL_EMC_ANDROID;
7767 GfxAction[x][y] = ACTION_SHRINKING;
7768 GfxDir[x][y] = diagonal_move_dir;
7769 ChangeDelay[x][y] = change_delay;
7771 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7774 DrawLevelGraphicAnimation(x, y, graphic);
7775 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7777 if (Feld[newx][newy] == EL_ACID)
7779 SplashAcid(newx, newy);
7784 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7786 Store[newx][newy] = EL_EMC_ANDROID;
7787 GfxElement[newx][newy] = EL_EMC_ANDROID;
7788 GfxAction[newx][newy] = ACTION_GROWING;
7789 GfxDir[newx][newy] = diagonal_move_dir;
7790 ChangeDelay[newx][newy] = change_delay;
7792 graphic = el_act_dir2img(GfxElement[newx][newy],
7793 GfxAction[newx][newy], GfxDir[newx][newy]);
7795 DrawLevelGraphicAnimation(newx, newy, graphic);
7796 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7802 Feld[newx][newy] = EL_EMPTY;
7803 TEST_DrawLevelField(newx, newy);
7805 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7808 else if (!IS_FREE(newx, newy))
7813 else if (IS_CUSTOM_ELEMENT(element) &&
7814 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7816 if (!DigFieldByCE(newx, newy, element))
7819 if (move_pattern & MV_MAZE_RUNNER_STYLE)
7821 RunnerVisit[x][y] = FrameCounter;
7822 PlayerVisit[x][y] /= 8; /* expire player visit path */
7825 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7827 if (!IS_FREE(newx, newy))
7829 if (IS_PLAYER(x, y))
7830 DrawPlayerField(x, y);
7832 TEST_DrawLevelField(x, y);
7838 boolean wanna_flame = !RND(10);
7839 int dx = newx - x, dy = newy - y;
7840 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7841 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7842 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7843 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7844 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7845 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7848 IS_CLASSIC_ENEMY(element1) ||
7849 IS_CLASSIC_ENEMY(element2)) &&
7850 element1 != EL_DRAGON && element2 != EL_DRAGON &&
7851 element1 != EL_FLAMES && element2 != EL_FLAMES)
7853 ResetGfxAnimation(x, y);
7854 GfxAction[x][y] = ACTION_ATTACKING;
7856 if (IS_PLAYER(x, y))
7857 DrawPlayerField(x, y);
7859 TEST_DrawLevelField(x, y);
7861 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7863 MovDelay[x][y] = 50;
7865 Feld[newx][newy] = EL_FLAMES;
7866 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7867 Feld[newx1][newy1] = EL_FLAMES;
7868 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7869 Feld[newx2][newy2] = EL_FLAMES;
7875 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7876 Feld[newx][newy] == EL_DIAMOND)
7878 if (IS_MOVING(newx, newy))
7879 RemoveMovingField(newx, newy);
7882 Feld[newx][newy] = EL_EMPTY;
7883 TEST_DrawLevelField(newx, newy);
7886 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7888 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7889 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7891 if (AmoebaNr[newx][newy])
7893 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7894 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7895 Feld[newx][newy] == EL_BD_AMOEBA)
7896 AmoebaCnt[AmoebaNr[newx][newy]]--;
7899 if (IS_MOVING(newx, newy))
7901 RemoveMovingField(newx, newy);
7905 Feld[newx][newy] = EL_EMPTY;
7906 TEST_DrawLevelField(newx, newy);
7909 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7911 else if ((element == EL_PACMAN || element == EL_MOLE)
7912 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7914 if (AmoebaNr[newx][newy])
7916 AmoebaCnt2[AmoebaNr[newx][newy]]--;
7917 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7918 Feld[newx][newy] == EL_BD_AMOEBA)
7919 AmoebaCnt[AmoebaNr[newx][newy]]--;
7922 if (element == EL_MOLE)
7924 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7925 PlayLevelSound(x, y, SND_MOLE_DIGGING);
7927 ResetGfxAnimation(x, y);
7928 GfxAction[x][y] = ACTION_DIGGING;
7929 TEST_DrawLevelField(x, y);
7931 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
7933 return; /* wait for shrinking amoeba */
7935 else /* element == EL_PACMAN */
7937 Feld[newx][newy] = EL_EMPTY;
7938 TEST_DrawLevelField(newx, newy);
7939 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7942 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7943 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7944 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7946 /* wait for shrinking amoeba to completely disappear */
7949 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7951 /* object was running against a wall */
7955 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
7956 DrawLevelElementAnimation(x, y, element);
7958 if (DONT_TOUCH(element))
7959 TestIfBadThingTouchesPlayer(x, y);
7964 InitMovingField(x, y, MovDir[x][y]);
7966 PlayLevelSoundAction(x, y, ACTION_MOVING);
7970 ContinueMoving(x, y);
7973 void ContinueMoving(int x, int y)
7975 int element = Feld[x][y];
7976 struct ElementInfo *ei = &element_info[element];
7977 int direction = MovDir[x][y];
7978 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7979 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
7980 int newx = x + dx, newy = y + dy;
7981 int stored = Store[x][y];
7982 int stored_new = Store[newx][newy];
7983 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
7984 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7985 boolean last_line = (newy == lev_fieldy - 1);
7987 MovPos[x][y] += getElementMoveStepsize(x, y);
7989 if (pushed_by_player) /* special case: moving object pushed by player */
7990 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7992 if (ABS(MovPos[x][y]) < TILEX)
7994 TEST_DrawLevelField(x, y);
7996 return; /* element is still moving */
7999 /* element reached destination field */
8001 Feld[x][y] = EL_EMPTY;
8002 Feld[newx][newy] = element;
8003 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8005 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8007 element = Feld[newx][newy] = EL_ACID;
8009 else if (element == EL_MOLE)
8011 Feld[x][y] = EL_SAND;
8013 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8015 else if (element == EL_QUICKSAND_FILLING)
8017 element = Feld[newx][newy] = get_next_element(element);
8018 Store[newx][newy] = Store[x][y];
8020 else if (element == EL_QUICKSAND_EMPTYING)
8022 Feld[x][y] = get_next_element(element);
8023 element = Feld[newx][newy] = Store[x][y];
8025 else if (element == EL_QUICKSAND_FAST_FILLING)
8027 element = Feld[newx][newy] = get_next_element(element);
8028 Store[newx][newy] = Store[x][y];
8030 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8032 Feld[x][y] = get_next_element(element);
8033 element = Feld[newx][newy] = Store[x][y];
8035 else if (element == EL_MAGIC_WALL_FILLING)
8037 element = Feld[newx][newy] = get_next_element(element);
8038 if (!game.magic_wall_active)
8039 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8040 Store[newx][newy] = Store[x][y];
8042 else if (element == EL_MAGIC_WALL_EMPTYING)
8044 Feld[x][y] = get_next_element(element);
8045 if (!game.magic_wall_active)
8046 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8047 element = Feld[newx][newy] = Store[x][y];
8049 InitField(newx, newy, FALSE);
8051 else if (element == EL_BD_MAGIC_WALL_FILLING)
8053 element = Feld[newx][newy] = get_next_element(element);
8054 if (!game.magic_wall_active)
8055 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8056 Store[newx][newy] = Store[x][y];
8058 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8060 Feld[x][y] = get_next_element(element);
8061 if (!game.magic_wall_active)
8062 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8063 element = Feld[newx][newy] = Store[x][y];
8065 InitField(newx, newy, FALSE);
8067 else if (element == EL_DC_MAGIC_WALL_FILLING)
8069 element = Feld[newx][newy] = get_next_element(element);
8070 if (!game.magic_wall_active)
8071 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8072 Store[newx][newy] = Store[x][y];
8074 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8076 Feld[x][y] = get_next_element(element);
8077 if (!game.magic_wall_active)
8078 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8079 element = Feld[newx][newy] = Store[x][y];
8081 InitField(newx, newy, FALSE);
8083 else if (element == EL_AMOEBA_DROPPING)
8085 Feld[x][y] = get_next_element(element);
8086 element = Feld[newx][newy] = Store[x][y];
8088 else if (element == EL_SOKOBAN_OBJECT)
8091 Feld[x][y] = Back[x][y];
8093 if (Back[newx][newy])
8094 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8096 Back[x][y] = Back[newx][newy] = 0;
8099 Store[x][y] = EL_EMPTY;
8104 MovDelay[newx][newy] = 0;
8106 if (CAN_CHANGE_OR_HAS_ACTION(element))
8108 /* copy element change control values to new field */
8109 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8110 ChangePage[newx][newy] = ChangePage[x][y];
8111 ChangeCount[newx][newy] = ChangeCount[x][y];
8112 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8115 CustomValue[newx][newy] = CustomValue[x][y];
8117 ChangeDelay[x][y] = 0;
8118 ChangePage[x][y] = -1;
8119 ChangeCount[x][y] = 0;
8120 ChangeEvent[x][y] = -1;
8122 CustomValue[x][y] = 0;
8124 /* copy animation control values to new field */
8125 GfxFrame[newx][newy] = GfxFrame[x][y];
8126 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8127 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8128 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8130 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8132 /* some elements can leave other elements behind after moving */
8133 if (ei->move_leave_element != EL_EMPTY &&
8134 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8135 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8137 int move_leave_element = ei->move_leave_element;
8139 /* this makes it possible to leave the removed element again */
8140 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8141 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8143 Feld[x][y] = move_leave_element;
8145 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8146 MovDir[x][y] = direction;
8148 InitField(x, y, FALSE);
8150 if (GFX_CRUMBLED(Feld[x][y]))
8151 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8153 if (ELEM_IS_PLAYER(move_leave_element))
8154 RelocatePlayer(x, y, move_leave_element);
8157 /* do this after checking for left-behind element */
8158 ResetGfxAnimation(x, y); /* reset animation values for old field */
8160 if (!CAN_MOVE(element) ||
8161 (CAN_FALL(element) && direction == MV_DOWN &&
8162 (element == EL_SPRING ||
8163 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8164 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8165 GfxDir[x][y] = MovDir[newx][newy] = 0;
8167 TEST_DrawLevelField(x, y);
8168 TEST_DrawLevelField(newx, newy);
8170 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8172 /* prevent pushed element from moving on in pushed direction */
8173 if (pushed_by_player && CAN_MOVE(element) &&
8174 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8175 !(element_info[element].move_pattern & direction))
8176 TurnRound(newx, newy);
8178 /* prevent elements on conveyor belt from moving on in last direction */
8179 if (pushed_by_conveyor && CAN_FALL(element) &&
8180 direction & MV_HORIZONTAL)
8181 MovDir[newx][newy] = 0;
8183 if (!pushed_by_player)
8185 int nextx = newx + dx, nexty = newy + dy;
8186 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8188 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8190 if (CAN_FALL(element) && direction == MV_DOWN)
8191 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8193 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8194 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8196 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8197 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8200 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8202 TestIfBadThingTouchesPlayer(newx, newy);
8203 TestIfBadThingTouchesFriend(newx, newy);
8205 if (!IS_CUSTOM_ELEMENT(element))
8206 TestIfBadThingTouchesOtherBadThing(newx, newy);
8208 else if (element == EL_PENGUIN)
8209 TestIfFriendTouchesBadThing(newx, newy);
8211 if (DONT_GET_HIT_BY(element))
8213 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8216 /* give the player one last chance (one more frame) to move away */
8217 if (CAN_FALL(element) && direction == MV_DOWN &&
8218 (last_line || (!IS_FREE(x, newy + 1) &&
8219 (!IS_PLAYER(x, newy + 1) ||
8220 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8223 if (pushed_by_player && !game.use_change_when_pushing_bug)
8225 int push_side = MV_DIR_OPPOSITE(direction);
8226 struct PlayerInfo *player = PLAYERINFO(x, y);
8228 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8229 player->index_bit, push_side);
8230 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8231 player->index_bit, push_side);
8234 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8235 MovDelay[newx][newy] = 1;
8237 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8239 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8240 TestIfElementHitsCustomElement(newx, newy, direction);
8241 TestIfPlayerTouchesCustomElement(newx, newy);
8242 TestIfElementTouchesCustomElement(newx, newy);
8244 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8245 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8246 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8247 MV_DIR_OPPOSITE(direction));
8250 int AmoebeNachbarNr(int ax, int ay)
8253 int element = Feld[ax][ay];
8255 static int xy[4][2] =
8263 for (i = 0; i < NUM_DIRECTIONS; i++)
8265 int x = ax + xy[i][0];
8266 int y = ay + xy[i][1];
8268 if (!IN_LEV_FIELD(x, y))
8271 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8272 group_nr = AmoebaNr[x][y];
8278 void AmoebenVereinigen(int ax, int ay)
8280 int i, x, y, xx, yy;
8281 int new_group_nr = AmoebaNr[ax][ay];
8282 static int xy[4][2] =
8290 if (new_group_nr == 0)
8293 for (i = 0; i < NUM_DIRECTIONS; i++)
8298 if (!IN_LEV_FIELD(x, y))
8301 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8302 Feld[x][y] == EL_BD_AMOEBA ||
8303 Feld[x][y] == EL_AMOEBA_DEAD) &&
8304 AmoebaNr[x][y] != new_group_nr)
8306 int old_group_nr = AmoebaNr[x][y];
8308 if (old_group_nr == 0)
8311 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8312 AmoebaCnt[old_group_nr] = 0;
8313 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8314 AmoebaCnt2[old_group_nr] = 0;
8316 SCAN_PLAYFIELD(xx, yy)
8318 if (AmoebaNr[xx][yy] == old_group_nr)
8319 AmoebaNr[xx][yy] = new_group_nr;
8325 void AmoebeUmwandeln(int ax, int ay)
8329 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8331 int group_nr = AmoebaNr[ax][ay];
8336 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8337 printf("AmoebeUmwandeln(): This should never happen!\n");
8342 SCAN_PLAYFIELD(x, y)
8344 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8347 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8351 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8352 SND_AMOEBA_TURNING_TO_GEM :
8353 SND_AMOEBA_TURNING_TO_ROCK));
8358 static int xy[4][2] =
8366 for (i = 0; i < NUM_DIRECTIONS; i++)
8371 if (!IN_LEV_FIELD(x, y))
8374 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8376 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8377 SND_AMOEBA_TURNING_TO_GEM :
8378 SND_AMOEBA_TURNING_TO_ROCK));
8385 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8388 int group_nr = AmoebaNr[ax][ay];
8389 boolean done = FALSE;
8394 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8395 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8400 SCAN_PLAYFIELD(x, y)
8402 if (AmoebaNr[x][y] == group_nr &&
8403 (Feld[x][y] == EL_AMOEBA_DEAD ||
8404 Feld[x][y] == EL_BD_AMOEBA ||
8405 Feld[x][y] == EL_AMOEBA_GROWING))
8408 Feld[x][y] = new_element;
8409 InitField(x, y, FALSE);
8410 TEST_DrawLevelField(x, y);
8416 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8417 SND_BD_AMOEBA_TURNING_TO_ROCK :
8418 SND_BD_AMOEBA_TURNING_TO_GEM));
8421 void AmoebeWaechst(int x, int y)
8423 static unsigned int sound_delay = 0;
8424 static unsigned int sound_delay_value = 0;
8426 if (!MovDelay[x][y]) /* start new growing cycle */
8430 if (DelayReached(&sound_delay, sound_delay_value))
8432 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8433 sound_delay_value = 30;
8437 if (MovDelay[x][y]) /* wait some time before growing bigger */
8440 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8442 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8443 6 - MovDelay[x][y]);
8445 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8448 if (!MovDelay[x][y])
8450 Feld[x][y] = Store[x][y];
8452 TEST_DrawLevelField(x, y);
8457 void AmoebaDisappearing(int x, int y)
8459 static unsigned int sound_delay = 0;
8460 static unsigned int sound_delay_value = 0;
8462 if (!MovDelay[x][y]) /* start new shrinking cycle */
8466 if (DelayReached(&sound_delay, sound_delay_value))
8467 sound_delay_value = 30;
8470 if (MovDelay[x][y]) /* wait some time before shrinking */
8473 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8475 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8476 6 - MovDelay[x][y]);
8478 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8481 if (!MovDelay[x][y])
8483 Feld[x][y] = EL_EMPTY;
8484 TEST_DrawLevelField(x, y);
8486 /* don't let mole enter this field in this cycle;
8487 (give priority to objects falling to this field from above) */
8493 void AmoebeAbleger(int ax, int ay)
8496 int element = Feld[ax][ay];
8497 int graphic = el2img(element);
8498 int newax = ax, neway = ay;
8499 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8500 static int xy[4][2] =
8508 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8510 Feld[ax][ay] = EL_AMOEBA_DEAD;
8511 TEST_DrawLevelField(ax, ay);
8515 if (IS_ANIMATED(graphic))
8516 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8518 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8519 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8521 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8524 if (MovDelay[ax][ay])
8528 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8531 int x = ax + xy[start][0];
8532 int y = ay + xy[start][1];
8534 if (!IN_LEV_FIELD(x, y))
8537 if (IS_FREE(x, y) ||
8538 CAN_GROW_INTO(Feld[x][y]) ||
8539 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8540 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8546 if (newax == ax && neway == ay)
8549 else /* normal or "filled" (BD style) amoeba */
8552 boolean waiting_for_player = FALSE;
8554 for (i = 0; i < NUM_DIRECTIONS; i++)
8556 int j = (start + i) % 4;
8557 int x = ax + xy[j][0];
8558 int y = ay + xy[j][1];
8560 if (!IN_LEV_FIELD(x, y))
8563 if (IS_FREE(x, y) ||
8564 CAN_GROW_INTO(Feld[x][y]) ||
8565 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8566 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8572 else if (IS_PLAYER(x, y))
8573 waiting_for_player = TRUE;
8576 if (newax == ax && neway == ay) /* amoeba cannot grow */
8578 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8580 Feld[ax][ay] = EL_AMOEBA_DEAD;
8581 TEST_DrawLevelField(ax, ay);
8582 AmoebaCnt[AmoebaNr[ax][ay]]--;
8584 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8586 if (element == EL_AMOEBA_FULL)
8587 AmoebeUmwandeln(ax, ay);
8588 else if (element == EL_BD_AMOEBA)
8589 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8594 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8596 /* amoeba gets larger by growing in some direction */
8598 int new_group_nr = AmoebaNr[ax][ay];
8601 if (new_group_nr == 0)
8603 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8604 printf("AmoebeAbleger(): This should never happen!\n");
8609 AmoebaNr[newax][neway] = new_group_nr;
8610 AmoebaCnt[new_group_nr]++;
8611 AmoebaCnt2[new_group_nr]++;
8613 /* if amoeba touches other amoeba(s) after growing, unify them */
8614 AmoebenVereinigen(newax, neway);
8616 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8618 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8624 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8625 (neway == lev_fieldy - 1 && newax != ax))
8627 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8628 Store[newax][neway] = element;
8630 else if (neway == ay || element == EL_EMC_DRIPPER)
8632 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8634 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8638 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8639 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8640 Store[ax][ay] = EL_AMOEBA_DROP;
8641 ContinueMoving(ax, ay);
8645 TEST_DrawLevelField(newax, neway);
8648 void Life(int ax, int ay)
8652 int element = Feld[ax][ay];
8653 int graphic = el2img(element);
8654 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8656 boolean changed = FALSE;
8658 if (IS_ANIMATED(graphic))
8659 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8664 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8665 MovDelay[ax][ay] = life_time;
8667 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8670 if (MovDelay[ax][ay])
8674 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8676 int xx = ax+x1, yy = ay+y1;
8679 if (!IN_LEV_FIELD(xx, yy))
8682 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8684 int x = xx+x2, y = yy+y2;
8686 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8689 if (((Feld[x][y] == element ||
8690 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8692 (IS_FREE(x, y) && Stop[x][y]))
8696 if (xx == ax && yy == ay) /* field in the middle */
8698 if (nachbarn < life_parameter[0] ||
8699 nachbarn > life_parameter[1])
8701 Feld[xx][yy] = EL_EMPTY;
8703 TEST_DrawLevelField(xx, yy);
8704 Stop[xx][yy] = TRUE;
8708 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8709 { /* free border field */
8710 if (nachbarn >= life_parameter[2] &&
8711 nachbarn <= life_parameter[3])
8713 Feld[xx][yy] = element;
8714 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8716 TEST_DrawLevelField(xx, yy);
8717 Stop[xx][yy] = TRUE;
8724 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8725 SND_GAME_OF_LIFE_GROWING);
8728 static void InitRobotWheel(int x, int y)
8730 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8733 static void RunRobotWheel(int x, int y)
8735 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8738 static void StopRobotWheel(int x, int y)
8740 if (ZX == x && ZY == y)
8744 game.robot_wheel_active = FALSE;
8748 static void InitTimegateWheel(int x, int y)
8750 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8753 static void RunTimegateWheel(int x, int y)
8755 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8758 static void InitMagicBallDelay(int x, int y)
8760 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8763 static void ActivateMagicBall(int bx, int by)
8767 if (level.ball_random)
8769 int pos_border = RND(8); /* select one of the eight border elements */
8770 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8771 int xx = pos_content % 3;
8772 int yy = pos_content / 3;
8777 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8778 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8782 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8784 int xx = x - bx + 1;
8785 int yy = y - by + 1;
8787 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8788 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8792 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8795 void CheckExit(int x, int y)
8797 if (local_player->gems_still_needed > 0 ||
8798 local_player->sokobanfields_still_needed > 0 ||
8799 local_player->lights_still_needed > 0)
8801 int element = Feld[x][y];
8802 int graphic = el2img(element);
8804 if (IS_ANIMATED(graphic))
8805 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8810 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8813 Feld[x][y] = EL_EXIT_OPENING;
8815 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8818 void CheckExitEM(int x, int y)
8820 if (local_player->gems_still_needed > 0 ||
8821 local_player->sokobanfields_still_needed > 0 ||
8822 local_player->lights_still_needed > 0)
8824 int element = Feld[x][y];
8825 int graphic = el2img(element);
8827 if (IS_ANIMATED(graphic))
8828 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8833 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8836 Feld[x][y] = EL_EM_EXIT_OPENING;
8838 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8841 void CheckExitSteel(int x, int y)
8843 if (local_player->gems_still_needed > 0 ||
8844 local_player->sokobanfields_still_needed > 0 ||
8845 local_player->lights_still_needed > 0)
8847 int element = Feld[x][y];
8848 int graphic = el2img(element);
8850 if (IS_ANIMATED(graphic))
8851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8856 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8859 Feld[x][y] = EL_STEEL_EXIT_OPENING;
8861 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8864 void CheckExitSteelEM(int x, int y)
8866 if (local_player->gems_still_needed > 0 ||
8867 local_player->sokobanfields_still_needed > 0 ||
8868 local_player->lights_still_needed > 0)
8870 int element = Feld[x][y];
8871 int graphic = el2img(element);
8873 if (IS_ANIMATED(graphic))
8874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8879 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8882 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8884 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8887 void CheckExitSP(int x, int y)
8889 if (local_player->gems_still_needed > 0)
8891 int element = Feld[x][y];
8892 int graphic = el2img(element);
8894 if (IS_ANIMATED(graphic))
8895 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8900 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8903 Feld[x][y] = EL_SP_EXIT_OPENING;
8905 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8908 static void CloseAllOpenTimegates()
8912 SCAN_PLAYFIELD(x, y)
8914 int element = Feld[x][y];
8916 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8918 Feld[x][y] = EL_TIMEGATE_CLOSING;
8920 PlayLevelSoundAction(x, y, ACTION_CLOSING);
8925 void DrawTwinkleOnField(int x, int y)
8927 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8930 if (Feld[x][y] == EL_BD_DIAMOND)
8933 if (MovDelay[x][y] == 0) /* next animation frame */
8934 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8936 if (MovDelay[x][y] != 0) /* wait some time before next frame */
8940 DrawLevelElementAnimation(x, y, Feld[x][y]);
8942 if (MovDelay[x][y] != 0)
8944 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8945 10 - MovDelay[x][y]);
8947 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8952 void MauerWaechst(int x, int y)
8956 if (!MovDelay[x][y]) /* next animation frame */
8957 MovDelay[x][y] = 3 * delay;
8959 if (MovDelay[x][y]) /* wait some time before next frame */
8963 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8965 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8966 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8968 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8971 if (!MovDelay[x][y])
8973 if (MovDir[x][y] == MV_LEFT)
8975 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8976 TEST_DrawLevelField(x - 1, y);
8978 else if (MovDir[x][y] == MV_RIGHT)
8980 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8981 TEST_DrawLevelField(x + 1, y);
8983 else if (MovDir[x][y] == MV_UP)
8985 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8986 TEST_DrawLevelField(x, y - 1);
8990 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8991 TEST_DrawLevelField(x, y + 1);
8994 Feld[x][y] = Store[x][y];
8996 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8997 TEST_DrawLevelField(x, y);
9002 void MauerAbleger(int ax, int ay)
9004 int element = Feld[ax][ay];
9005 int graphic = el2img(element);
9006 boolean oben_frei = FALSE, unten_frei = FALSE;
9007 boolean links_frei = FALSE, rechts_frei = FALSE;
9008 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9009 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9010 boolean new_wall = FALSE;
9012 if (IS_ANIMATED(graphic))
9013 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9015 if (!MovDelay[ax][ay]) /* start building new wall */
9016 MovDelay[ax][ay] = 6;
9018 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9021 if (MovDelay[ax][ay])
9025 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9027 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9029 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9031 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9034 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9035 element == EL_EXPANDABLE_WALL_ANY)
9039 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9040 Store[ax][ay-1] = element;
9041 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9042 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9043 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9044 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9049 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9050 Store[ax][ay+1] = element;
9051 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9052 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9053 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9054 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9059 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9060 element == EL_EXPANDABLE_WALL_ANY ||
9061 element == EL_EXPANDABLE_WALL ||
9062 element == EL_BD_EXPANDABLE_WALL)
9066 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9067 Store[ax-1][ay] = element;
9068 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9069 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9070 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9071 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9077 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9078 Store[ax+1][ay] = element;
9079 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9080 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9081 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9082 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9087 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9088 TEST_DrawLevelField(ax, ay);
9090 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9092 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9093 unten_massiv = TRUE;
9094 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9095 links_massiv = TRUE;
9096 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9097 rechts_massiv = TRUE;
9099 if (((oben_massiv && unten_massiv) ||
9100 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9101 element == EL_EXPANDABLE_WALL) &&
9102 ((links_massiv && rechts_massiv) ||
9103 element == EL_EXPANDABLE_WALL_VERTICAL))
9104 Feld[ax][ay] = EL_WALL;
9107 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9110 void MauerAblegerStahl(int ax, int ay)
9112 int element = Feld[ax][ay];
9113 int graphic = el2img(element);
9114 boolean oben_frei = FALSE, unten_frei = FALSE;
9115 boolean links_frei = FALSE, rechts_frei = FALSE;
9116 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9117 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9118 boolean new_wall = FALSE;
9120 if (IS_ANIMATED(graphic))
9121 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9123 if (!MovDelay[ax][ay]) /* start building new wall */
9124 MovDelay[ax][ay] = 6;
9126 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9129 if (MovDelay[ax][ay])
9133 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9135 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9137 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9139 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9142 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9143 element == EL_EXPANDABLE_STEELWALL_ANY)
9147 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9148 Store[ax][ay-1] = element;
9149 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9150 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9151 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9152 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9157 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9158 Store[ax][ay+1] = element;
9159 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9160 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9161 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9162 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9167 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9168 element == EL_EXPANDABLE_STEELWALL_ANY)
9172 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9173 Store[ax-1][ay] = element;
9174 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9175 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9176 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9177 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9183 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9184 Store[ax+1][ay] = element;
9185 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9186 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9187 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9188 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9193 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9195 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9196 unten_massiv = TRUE;
9197 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9198 links_massiv = TRUE;
9199 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9200 rechts_massiv = TRUE;
9202 if (((oben_massiv && unten_massiv) ||
9203 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9204 ((links_massiv && rechts_massiv) ||
9205 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9206 Feld[ax][ay] = EL_STEELWALL;
9209 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9212 void CheckForDragon(int x, int y)
9215 boolean dragon_found = FALSE;
9216 static int xy[4][2] =
9224 for (i = 0; i < NUM_DIRECTIONS; i++)
9226 for (j = 0; j < 4; j++)
9228 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9230 if (IN_LEV_FIELD(xx, yy) &&
9231 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9233 if (Feld[xx][yy] == EL_DRAGON)
9234 dragon_found = TRUE;
9243 for (i = 0; i < NUM_DIRECTIONS; i++)
9245 for (j = 0; j < 3; j++)
9247 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9249 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9251 Feld[xx][yy] = EL_EMPTY;
9252 TEST_DrawLevelField(xx, yy);
9261 static void InitBuggyBase(int x, int y)
9263 int element = Feld[x][y];
9264 int activating_delay = FRAMES_PER_SECOND / 4;
9267 (element == EL_SP_BUGGY_BASE ?
9268 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9269 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9271 element == EL_SP_BUGGY_BASE_ACTIVE ?
9272 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9275 static void WarnBuggyBase(int x, int y)
9278 static int xy[4][2] =
9286 for (i = 0; i < NUM_DIRECTIONS; i++)
9288 int xx = x + xy[i][0];
9289 int yy = y + xy[i][1];
9291 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9293 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9300 static void InitTrap(int x, int y)
9302 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9305 static void ActivateTrap(int x, int y)
9307 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9310 static void ChangeActiveTrap(int x, int y)
9312 int graphic = IMG_TRAP_ACTIVE;
9314 /* if new animation frame was drawn, correct crumbled sand border */
9315 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9316 TEST_DrawLevelFieldCrumbled(x, y);
9319 static int getSpecialActionElement(int element, int number, int base_element)
9321 return (element != EL_EMPTY ? element :
9322 number != -1 ? base_element + number - 1 :
9326 static int getModifiedActionNumber(int value_old, int operator, int operand,
9327 int value_min, int value_max)
9329 int value_new = (operator == CA_MODE_SET ? operand :
9330 operator == CA_MODE_ADD ? value_old + operand :
9331 operator == CA_MODE_SUBTRACT ? value_old - operand :
9332 operator == CA_MODE_MULTIPLY ? value_old * operand :
9333 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9334 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9337 return (value_new < value_min ? value_min :
9338 value_new > value_max ? value_max :
9342 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9344 struct ElementInfo *ei = &element_info[element];
9345 struct ElementChangeInfo *change = &ei->change_page[page];
9346 int target_element = change->target_element;
9347 int action_type = change->action_type;
9348 int action_mode = change->action_mode;
9349 int action_arg = change->action_arg;
9350 int action_element = change->action_element;
9353 if (!change->has_action)
9356 /* ---------- determine action paramater values -------------------------- */
9358 int level_time_value =
9359 (level.time > 0 ? TimeLeft :
9362 int action_arg_element_raw =
9363 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9364 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9365 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9366 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9367 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9368 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9369 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9371 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9373 int action_arg_direction =
9374 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9375 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9376 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9377 change->actual_trigger_side :
9378 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9379 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9382 int action_arg_number_min =
9383 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9386 int action_arg_number_max =
9387 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9388 action_type == CA_SET_LEVEL_GEMS ? 999 :
9389 action_type == CA_SET_LEVEL_TIME ? 9999 :
9390 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9391 action_type == CA_SET_CE_VALUE ? 9999 :
9392 action_type == CA_SET_CE_SCORE ? 9999 :
9395 int action_arg_number_reset =
9396 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9397 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9398 action_type == CA_SET_LEVEL_TIME ? level.time :
9399 action_type == CA_SET_LEVEL_SCORE ? 0 :
9400 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9401 action_type == CA_SET_CE_SCORE ? 0 :
9404 int action_arg_number =
9405 (action_arg <= CA_ARG_MAX ? action_arg :
9406 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9407 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9408 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9409 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9410 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9411 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9412 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9413 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9414 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9415 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9416 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9417 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9418 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9419 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9420 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9421 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9422 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9423 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9424 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9425 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9426 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9429 int action_arg_number_old =
9430 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9431 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9432 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9433 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9434 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9437 int action_arg_number_new =
9438 getModifiedActionNumber(action_arg_number_old,
9439 action_mode, action_arg_number,
9440 action_arg_number_min, action_arg_number_max);
9442 int trigger_player_bits =
9443 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9444 change->actual_trigger_player_bits : change->trigger_player);
9446 int action_arg_player_bits =
9447 (action_arg >= CA_ARG_PLAYER_1 &&
9448 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9449 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9450 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9453 /* ---------- execute action -------------------------------------------- */
9455 switch (action_type)
9462 /* ---------- level actions ------------------------------------------- */
9464 case CA_RESTART_LEVEL:
9466 game.restart_level = TRUE;
9471 case CA_SHOW_ENVELOPE:
9473 int element = getSpecialActionElement(action_arg_element,
9474 action_arg_number, EL_ENVELOPE_1);
9476 if (IS_ENVELOPE(element))
9477 local_player->show_envelope = element;
9482 case CA_SET_LEVEL_TIME:
9484 if (level.time > 0) /* only modify limited time value */
9486 TimeLeft = action_arg_number_new;
9488 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9490 DisplayGameControlValues();
9492 if (!TimeLeft && setup.time_limit)
9493 for (i = 0; i < MAX_PLAYERS; i++)
9494 KillPlayer(&stored_player[i]);
9500 case CA_SET_LEVEL_SCORE:
9502 local_player->score = action_arg_number_new;
9504 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9506 DisplayGameControlValues();
9511 case CA_SET_LEVEL_GEMS:
9513 local_player->gems_still_needed = action_arg_number_new;
9515 game.snapshot.collected_item = TRUE;
9517 game_panel_controls[GAME_PANEL_GEMS].value =
9518 local_player->gems_still_needed;
9520 DisplayGameControlValues();
9525 case CA_SET_LEVEL_WIND:
9527 game.wind_direction = action_arg_direction;
9532 case CA_SET_LEVEL_RANDOM_SEED:
9534 /* ensure that setting a new random seed while playing is predictable */
9535 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9540 /* ---------- player actions ------------------------------------------ */
9542 case CA_MOVE_PLAYER:
9544 /* automatically move to the next field in specified direction */
9545 for (i = 0; i < MAX_PLAYERS; i++)
9546 if (trigger_player_bits & (1 << i))
9547 stored_player[i].programmed_action = action_arg_direction;
9552 case CA_EXIT_PLAYER:
9554 for (i = 0; i < MAX_PLAYERS; i++)
9555 if (action_arg_player_bits & (1 << i))
9556 PlayerWins(&stored_player[i]);
9561 case CA_KILL_PLAYER:
9563 for (i = 0; i < MAX_PLAYERS; i++)
9564 if (action_arg_player_bits & (1 << i))
9565 KillPlayer(&stored_player[i]);
9570 case CA_SET_PLAYER_KEYS:
9572 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9573 int element = getSpecialActionElement(action_arg_element,
9574 action_arg_number, EL_KEY_1);
9576 if (IS_KEY(element))
9578 for (i = 0; i < MAX_PLAYERS; i++)
9580 if (trigger_player_bits & (1 << i))
9582 stored_player[i].key[KEY_NR(element)] = key_state;
9584 DrawGameDoorValues();
9592 case CA_SET_PLAYER_SPEED:
9594 for (i = 0; i < MAX_PLAYERS; i++)
9596 if (trigger_player_bits & (1 << i))
9598 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9600 if (action_arg == CA_ARG_SPEED_FASTER &&
9601 stored_player[i].cannot_move)
9603 action_arg_number = STEPSIZE_VERY_SLOW;
9605 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9606 action_arg == CA_ARG_SPEED_FASTER)
9608 action_arg_number = 2;
9609 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9612 else if (action_arg == CA_ARG_NUMBER_RESET)
9614 action_arg_number = level.initial_player_stepsize[i];
9618 getModifiedActionNumber(move_stepsize,
9621 action_arg_number_min,
9622 action_arg_number_max);
9624 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9631 case CA_SET_PLAYER_SHIELD:
9633 for (i = 0; i < MAX_PLAYERS; i++)
9635 if (trigger_player_bits & (1 << i))
9637 if (action_arg == CA_ARG_SHIELD_OFF)
9639 stored_player[i].shield_normal_time_left = 0;
9640 stored_player[i].shield_deadly_time_left = 0;
9642 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9644 stored_player[i].shield_normal_time_left = 999999;
9646 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9648 stored_player[i].shield_normal_time_left = 999999;
9649 stored_player[i].shield_deadly_time_left = 999999;
9657 case CA_SET_PLAYER_GRAVITY:
9659 for (i = 0; i < MAX_PLAYERS; i++)
9661 if (trigger_player_bits & (1 << i))
9663 stored_player[i].gravity =
9664 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9665 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9666 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9667 stored_player[i].gravity);
9674 case CA_SET_PLAYER_ARTWORK:
9676 for (i = 0; i < MAX_PLAYERS; i++)
9678 if (trigger_player_bits & (1 << i))
9680 int artwork_element = action_arg_element;
9682 if (action_arg == CA_ARG_ELEMENT_RESET)
9684 (level.use_artwork_element[i] ? level.artwork_element[i] :
9685 stored_player[i].element_nr);
9687 if (stored_player[i].artwork_element != artwork_element)
9688 stored_player[i].Frame = 0;
9690 stored_player[i].artwork_element = artwork_element;
9692 SetPlayerWaiting(&stored_player[i], FALSE);
9694 /* set number of special actions for bored and sleeping animation */
9695 stored_player[i].num_special_action_bored =
9696 get_num_special_action(artwork_element,
9697 ACTION_BORING_1, ACTION_BORING_LAST);
9698 stored_player[i].num_special_action_sleeping =
9699 get_num_special_action(artwork_element,
9700 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9707 case CA_SET_PLAYER_INVENTORY:
9709 for (i = 0; i < MAX_PLAYERS; i++)
9711 struct PlayerInfo *player = &stored_player[i];
9714 if (trigger_player_bits & (1 << i))
9716 int inventory_element = action_arg_element;
9718 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9719 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9720 action_arg == CA_ARG_ELEMENT_ACTION)
9722 int element = inventory_element;
9723 int collect_count = element_info[element].collect_count_initial;
9725 if (!IS_CUSTOM_ELEMENT(element))
9728 if (collect_count == 0)
9729 player->inventory_infinite_element = element;
9731 for (k = 0; k < collect_count; k++)
9732 if (player->inventory_size < MAX_INVENTORY_SIZE)
9733 player->inventory_element[player->inventory_size++] =
9736 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9737 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9738 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9740 if (player->inventory_infinite_element != EL_UNDEFINED &&
9741 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9742 action_arg_element_raw))
9743 player->inventory_infinite_element = EL_UNDEFINED;
9745 for (k = 0, j = 0; j < player->inventory_size; j++)
9747 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9748 action_arg_element_raw))
9749 player->inventory_element[k++] = player->inventory_element[j];
9752 player->inventory_size = k;
9754 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9756 if (player->inventory_size > 0)
9758 for (j = 0; j < player->inventory_size - 1; j++)
9759 player->inventory_element[j] = player->inventory_element[j + 1];
9761 player->inventory_size--;
9764 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9766 if (player->inventory_size > 0)
9767 player->inventory_size--;
9769 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9771 player->inventory_infinite_element = EL_UNDEFINED;
9772 player->inventory_size = 0;
9774 else if (action_arg == CA_ARG_INVENTORY_RESET)
9776 player->inventory_infinite_element = EL_UNDEFINED;
9777 player->inventory_size = 0;
9779 if (level.use_initial_inventory[i])
9781 for (j = 0; j < level.initial_inventory_size[i]; j++)
9783 int element = level.initial_inventory_content[i][j];
9784 int collect_count = element_info[element].collect_count_initial;
9786 if (!IS_CUSTOM_ELEMENT(element))
9789 if (collect_count == 0)
9790 player->inventory_infinite_element = element;
9792 for (k = 0; k < collect_count; k++)
9793 if (player->inventory_size < MAX_INVENTORY_SIZE)
9794 player->inventory_element[player->inventory_size++] =
9805 /* ---------- CE actions ---------------------------------------------- */
9807 case CA_SET_CE_VALUE:
9809 int last_ce_value = CustomValue[x][y];
9811 CustomValue[x][y] = action_arg_number_new;
9813 if (CustomValue[x][y] != last_ce_value)
9815 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9816 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9818 if (CustomValue[x][y] == 0)
9820 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9821 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9828 case CA_SET_CE_SCORE:
9830 int last_ce_score = ei->collect_score;
9832 ei->collect_score = action_arg_number_new;
9834 if (ei->collect_score != last_ce_score)
9836 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9837 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9839 if (ei->collect_score == 0)
9843 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9844 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9847 This is a very special case that seems to be a mixture between
9848 CheckElementChange() and CheckTriggeredElementChange(): while
9849 the first one only affects single elements that are triggered
9850 directly, the second one affects multiple elements in the playfield
9851 that are triggered indirectly by another element. This is a third
9852 case: Changing the CE score always affects multiple identical CEs,
9853 so every affected CE must be checked, not only the single CE for
9854 which the CE score was changed in the first place (as every instance
9855 of that CE shares the same CE score, and therefore also can change)!
9857 SCAN_PLAYFIELD(xx, yy)
9859 if (Feld[xx][yy] == element)
9860 CheckElementChange(xx, yy, element, EL_UNDEFINED,
9861 CE_SCORE_GETS_ZERO);
9869 case CA_SET_CE_ARTWORK:
9871 int artwork_element = action_arg_element;
9872 boolean reset_frame = FALSE;
9875 if (action_arg == CA_ARG_ELEMENT_RESET)
9876 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9879 if (ei->gfx_element != artwork_element)
9882 ei->gfx_element = artwork_element;
9884 SCAN_PLAYFIELD(xx, yy)
9886 if (Feld[xx][yy] == element)
9890 ResetGfxAnimation(xx, yy);
9891 ResetRandomAnimationValue(xx, yy);
9894 TEST_DrawLevelField(xx, yy);
9901 /* ---------- engine actions ------------------------------------------ */
9903 case CA_SET_ENGINE_SCAN_MODE:
9905 InitPlayfieldScanMode(action_arg);
9915 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9917 int old_element = Feld[x][y];
9918 int new_element = GetElementFromGroupElement(element);
9919 int previous_move_direction = MovDir[x][y];
9920 int last_ce_value = CustomValue[x][y];
9921 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9922 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9923 boolean add_player_onto_element = (new_element_is_player &&
9924 new_element != EL_SOKOBAN_FIELD_PLAYER &&
9925 IS_WALKABLE(old_element));
9927 if (!add_player_onto_element)
9929 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9930 RemoveMovingField(x, y);
9934 Feld[x][y] = new_element;
9936 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9937 MovDir[x][y] = previous_move_direction;
9939 if (element_info[new_element].use_last_ce_value)
9940 CustomValue[x][y] = last_ce_value;
9942 InitField_WithBug1(x, y, FALSE);
9944 new_element = Feld[x][y]; /* element may have changed */
9946 ResetGfxAnimation(x, y);
9947 ResetRandomAnimationValue(x, y);
9949 TEST_DrawLevelField(x, y);
9951 if (GFX_CRUMBLED(new_element))
9952 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9955 /* check if element under the player changes from accessible to unaccessible
9956 (needed for special case of dropping element which then changes) */
9957 /* (must be checked after creating new element for walkable group elements) */
9958 if (IS_PLAYER(x, y) && !player_explosion_protected &&
9959 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9966 /* "ChangeCount" not set yet to allow "entered by player" change one time */
9967 if (new_element_is_player)
9968 RelocatePlayer(x, y, new_element);
9971 ChangeCount[x][y]++; /* count number of changes in the same frame */
9973 TestIfBadThingTouchesPlayer(x, y);
9974 TestIfPlayerTouchesCustomElement(x, y);
9975 TestIfElementTouchesCustomElement(x, y);
9978 static void CreateField(int x, int y, int element)
9980 CreateFieldExt(x, y, element, FALSE);
9983 static void CreateElementFromChange(int x, int y, int element)
9985 element = GET_VALID_RUNTIME_ELEMENT(element);
9987 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9989 int old_element = Feld[x][y];
9991 /* prevent changed element from moving in same engine frame
9992 unless both old and new element can either fall or move */
9993 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9994 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9998 CreateFieldExt(x, y, element, TRUE);
10001 static boolean ChangeElement(int x, int y, int element, int page)
10003 struct ElementInfo *ei = &element_info[element];
10004 struct ElementChangeInfo *change = &ei->change_page[page];
10005 int ce_value = CustomValue[x][y];
10006 int ce_score = ei->collect_score;
10007 int target_element;
10008 int old_element = Feld[x][y];
10010 /* always use default change event to prevent running into a loop */
10011 if (ChangeEvent[x][y] == -1)
10012 ChangeEvent[x][y] = CE_DELAY;
10014 if (ChangeEvent[x][y] == CE_DELAY)
10016 /* reset actual trigger element, trigger player and action element */
10017 change->actual_trigger_element = EL_EMPTY;
10018 change->actual_trigger_player = EL_EMPTY;
10019 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10020 change->actual_trigger_side = CH_SIDE_NONE;
10021 change->actual_trigger_ce_value = 0;
10022 change->actual_trigger_ce_score = 0;
10025 /* do not change elements more than a specified maximum number of changes */
10026 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10029 ChangeCount[x][y]++; /* count number of changes in the same frame */
10031 if (change->explode)
10038 if (change->use_target_content)
10040 boolean complete_replace = TRUE;
10041 boolean can_replace[3][3];
10044 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10047 boolean is_walkable;
10048 boolean is_diggable;
10049 boolean is_collectible;
10050 boolean is_removable;
10051 boolean is_destructible;
10052 int ex = x + xx - 1;
10053 int ey = y + yy - 1;
10054 int content_element = change->target_content.e[xx][yy];
10057 can_replace[xx][yy] = TRUE;
10059 if (ex == x && ey == y) /* do not check changing element itself */
10062 if (content_element == EL_EMPTY_SPACE)
10064 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10069 if (!IN_LEV_FIELD(ex, ey))
10071 can_replace[xx][yy] = FALSE;
10072 complete_replace = FALSE;
10079 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10080 e = MovingOrBlocked2Element(ex, ey);
10082 is_empty = (IS_FREE(ex, ey) ||
10083 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10085 is_walkable = (is_empty || IS_WALKABLE(e));
10086 is_diggable = (is_empty || IS_DIGGABLE(e));
10087 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10088 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10089 is_removable = (is_diggable || is_collectible);
10091 can_replace[xx][yy] =
10092 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10093 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10094 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10095 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10096 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10097 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10098 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10100 if (!can_replace[xx][yy])
10101 complete_replace = FALSE;
10104 if (!change->only_if_complete || complete_replace)
10106 boolean something_has_changed = FALSE;
10108 if (change->only_if_complete && change->use_random_replace &&
10109 RND(100) < change->random_percentage)
10112 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10114 int ex = x + xx - 1;
10115 int ey = y + yy - 1;
10116 int content_element;
10118 if (can_replace[xx][yy] && (!change->use_random_replace ||
10119 RND(100) < change->random_percentage))
10121 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10122 RemoveMovingField(ex, ey);
10124 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10126 content_element = change->target_content.e[xx][yy];
10127 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10128 ce_value, ce_score);
10130 CreateElementFromChange(ex, ey, target_element);
10132 something_has_changed = TRUE;
10134 /* for symmetry reasons, freeze newly created border elements */
10135 if (ex != x || ey != y)
10136 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10140 if (something_has_changed)
10142 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10143 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10149 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10150 ce_value, ce_score);
10152 if (element == EL_DIAGONAL_GROWING ||
10153 element == EL_DIAGONAL_SHRINKING)
10155 target_element = Store[x][y];
10157 Store[x][y] = EL_EMPTY;
10160 CreateElementFromChange(x, y, target_element);
10162 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10163 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10166 /* this uses direct change before indirect change */
10167 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10172 static void HandleElementChange(int x, int y, int page)
10174 int element = MovingOrBlocked2Element(x, y);
10175 struct ElementInfo *ei = &element_info[element];
10176 struct ElementChangeInfo *change = &ei->change_page[page];
10177 boolean handle_action_before_change = FALSE;
10180 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10181 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10184 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10185 x, y, element, element_info[element].token_name);
10186 printf("HandleElementChange(): This should never happen!\n");
10191 /* this can happen with classic bombs on walkable, changing elements */
10192 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10197 if (ChangeDelay[x][y] == 0) /* initialize element change */
10199 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10201 if (change->can_change)
10203 /* !!! not clear why graphic animation should be reset at all here !!! */
10204 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10205 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10208 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10210 When using an animation frame delay of 1 (this only happens with
10211 "sp_zonk.moving.left/right" in the classic graphics), the default
10212 (non-moving) animation shows wrong animation frames (while the
10213 moving animation, like "sp_zonk.moving.left/right", is correct,
10214 so this graphical bug never shows up with the classic graphics).
10215 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10216 be drawn instead of the correct frames 0,1,2,3. This is caused by
10217 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10218 an element change: First when the change delay ("ChangeDelay[][]")
10219 counter has reached zero after decrementing, then a second time in
10220 the next frame (after "GfxFrame[][]" was already incremented) when
10221 "ChangeDelay[][]" is reset to the initial delay value again.
10223 This causes frame 0 to be drawn twice, while the last frame won't
10224 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10226 As some animations may already be cleverly designed around this bug
10227 (at least the "Snake Bite" snake tail animation does this), it cannot
10228 simply be fixed here without breaking such existing animations.
10229 Unfortunately, it cannot easily be detected if a graphics set was
10230 designed "before" or "after" the bug was fixed. As a workaround,
10231 a new graphics set option "game.graphics_engine_version" was added
10232 to be able to specify the game's major release version for which the
10233 graphics set was designed, which can then be used to decide if the
10234 bugfix should be used (version 4 and above) or not (version 3 or
10235 below, or if no version was specified at all, as with old sets).
10237 (The wrong/fixed animation frames can be tested with the test level set
10238 "test_gfxframe" and level "000", which contains a specially prepared
10239 custom element at level position (x/y) == (11/9) which uses the zonk
10240 animation mentioned above. Using "game.graphics_engine_version: 4"
10241 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10242 This can also be seen from the debug output for this test element.)
10245 /* when a custom element is about to change (for example by change delay),
10246 do not reset graphic animation when the custom element is moving */
10247 if (game.graphics_engine_version < 4 &&
10250 ResetGfxAnimation(x, y);
10251 ResetRandomAnimationValue(x, y);
10254 if (change->pre_change_function)
10255 change->pre_change_function(x, y);
10259 ChangeDelay[x][y]--;
10261 if (ChangeDelay[x][y] != 0) /* continue element change */
10263 if (change->can_change)
10265 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10267 if (IS_ANIMATED(graphic))
10268 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10270 if (change->change_function)
10271 change->change_function(x, y);
10274 else /* finish element change */
10276 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10278 page = ChangePage[x][y];
10279 ChangePage[x][y] = -1;
10281 change = &ei->change_page[page];
10284 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10286 ChangeDelay[x][y] = 1; /* try change after next move step */
10287 ChangePage[x][y] = page; /* remember page to use for change */
10292 /* special case: set new level random seed before changing element */
10293 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10294 handle_action_before_change = TRUE;
10296 if (change->has_action && handle_action_before_change)
10297 ExecuteCustomElementAction(x, y, element, page);
10299 if (change->can_change)
10301 if (ChangeElement(x, y, element, page))
10303 if (change->post_change_function)
10304 change->post_change_function(x, y);
10308 if (change->has_action && !handle_action_before_change)
10309 ExecuteCustomElementAction(x, y, element, page);
10313 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10314 int trigger_element,
10316 int trigger_player,
10320 boolean change_done_any = FALSE;
10321 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10324 if (!(trigger_events[trigger_element][trigger_event]))
10327 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10329 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10331 int element = EL_CUSTOM_START + i;
10332 boolean change_done = FALSE;
10335 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10336 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10339 for (p = 0; p < element_info[element].num_change_pages; p++)
10341 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10343 if (change->can_change_or_has_action &&
10344 change->has_event[trigger_event] &&
10345 change->trigger_side & trigger_side &&
10346 change->trigger_player & trigger_player &&
10347 change->trigger_page & trigger_page_bits &&
10348 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10350 change->actual_trigger_element = trigger_element;
10351 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10352 change->actual_trigger_player_bits = trigger_player;
10353 change->actual_trigger_side = trigger_side;
10354 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10355 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10357 if ((change->can_change && !change_done) || change->has_action)
10361 SCAN_PLAYFIELD(x, y)
10363 if (Feld[x][y] == element)
10365 if (change->can_change && !change_done)
10367 /* if element already changed in this frame, not only prevent
10368 another element change (checked in ChangeElement()), but
10369 also prevent additional element actions for this element */
10371 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10372 !level.use_action_after_change_bug)
10375 ChangeDelay[x][y] = 1;
10376 ChangeEvent[x][y] = trigger_event;
10378 HandleElementChange(x, y, p);
10380 else if (change->has_action)
10382 /* if element already changed in this frame, not only prevent
10383 another element change (checked in ChangeElement()), but
10384 also prevent additional element actions for this element */
10386 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10387 !level.use_action_after_change_bug)
10390 ExecuteCustomElementAction(x, y, element, p);
10391 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10396 if (change->can_change)
10398 change_done = TRUE;
10399 change_done_any = TRUE;
10406 RECURSION_LOOP_DETECTION_END();
10408 return change_done_any;
10411 static boolean CheckElementChangeExt(int x, int y,
10413 int trigger_element,
10415 int trigger_player,
10418 boolean change_done = FALSE;
10421 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10422 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10425 if (Feld[x][y] == EL_BLOCKED)
10427 Blocked2Moving(x, y, &x, &y);
10428 element = Feld[x][y];
10431 /* check if element has already changed or is about to change after moving */
10432 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10433 Feld[x][y] != element) ||
10435 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10436 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10437 ChangePage[x][y] != -1)))
10440 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10442 for (p = 0; p < element_info[element].num_change_pages; p++)
10444 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10446 /* check trigger element for all events where the element that is checked
10447 for changing interacts with a directly adjacent element -- this is
10448 different to element changes that affect other elements to change on the
10449 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10450 boolean check_trigger_element =
10451 (trigger_event == CE_TOUCHING_X ||
10452 trigger_event == CE_HITTING_X ||
10453 trigger_event == CE_HIT_BY_X ||
10454 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10456 if (change->can_change_or_has_action &&
10457 change->has_event[trigger_event] &&
10458 change->trigger_side & trigger_side &&
10459 change->trigger_player & trigger_player &&
10460 (!check_trigger_element ||
10461 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10463 change->actual_trigger_element = trigger_element;
10464 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10465 change->actual_trigger_player_bits = trigger_player;
10466 change->actual_trigger_side = trigger_side;
10467 change->actual_trigger_ce_value = CustomValue[x][y];
10468 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10470 /* special case: trigger element not at (x,y) position for some events */
10471 if (check_trigger_element)
10483 { 0, 0 }, { 0, 0 }, { 0, 0 },
10487 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10488 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10490 change->actual_trigger_ce_value = CustomValue[xx][yy];
10491 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10494 if (change->can_change && !change_done)
10496 ChangeDelay[x][y] = 1;
10497 ChangeEvent[x][y] = trigger_event;
10499 HandleElementChange(x, y, p);
10501 change_done = TRUE;
10503 else if (change->has_action)
10505 ExecuteCustomElementAction(x, y, element, p);
10506 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10511 RECURSION_LOOP_DETECTION_END();
10513 return change_done;
10516 static void PlayPlayerSound(struct PlayerInfo *player)
10518 int jx = player->jx, jy = player->jy;
10519 int sound_element = player->artwork_element;
10520 int last_action = player->last_action_waiting;
10521 int action = player->action_waiting;
10523 if (player->is_waiting)
10525 if (action != last_action)
10526 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10528 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10532 if (action != last_action)
10533 StopSound(element_info[sound_element].sound[last_action]);
10535 if (last_action == ACTION_SLEEPING)
10536 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10540 static void PlayAllPlayersSound()
10544 for (i = 0; i < MAX_PLAYERS; i++)
10545 if (stored_player[i].active)
10546 PlayPlayerSound(&stored_player[i]);
10549 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10551 boolean last_waiting = player->is_waiting;
10552 int move_dir = player->MovDir;
10554 player->dir_waiting = move_dir;
10555 player->last_action_waiting = player->action_waiting;
10559 if (!last_waiting) /* not waiting -> waiting */
10561 player->is_waiting = TRUE;
10563 player->frame_counter_bored =
10565 game.player_boring_delay_fixed +
10566 GetSimpleRandom(game.player_boring_delay_random);
10567 player->frame_counter_sleeping =
10569 game.player_sleeping_delay_fixed +
10570 GetSimpleRandom(game.player_sleeping_delay_random);
10572 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10575 if (game.player_sleeping_delay_fixed +
10576 game.player_sleeping_delay_random > 0 &&
10577 player->anim_delay_counter == 0 &&
10578 player->post_delay_counter == 0 &&
10579 FrameCounter >= player->frame_counter_sleeping)
10580 player->is_sleeping = TRUE;
10581 else if (game.player_boring_delay_fixed +
10582 game.player_boring_delay_random > 0 &&
10583 FrameCounter >= player->frame_counter_bored)
10584 player->is_bored = TRUE;
10586 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10587 player->is_bored ? ACTION_BORING :
10590 if (player->is_sleeping && player->use_murphy)
10592 /* special case for sleeping Murphy when leaning against non-free tile */
10594 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10595 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10596 !IS_MOVING(player->jx - 1, player->jy)))
10597 move_dir = MV_LEFT;
10598 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10599 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10600 !IS_MOVING(player->jx + 1, player->jy)))
10601 move_dir = MV_RIGHT;
10603 player->is_sleeping = FALSE;
10605 player->dir_waiting = move_dir;
10608 if (player->is_sleeping)
10610 if (player->num_special_action_sleeping > 0)
10612 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10614 int last_special_action = player->special_action_sleeping;
10615 int num_special_action = player->num_special_action_sleeping;
10616 int special_action =
10617 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10618 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10619 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10620 last_special_action + 1 : ACTION_SLEEPING);
10621 int special_graphic =
10622 el_act_dir2img(player->artwork_element, special_action, move_dir);
10624 player->anim_delay_counter =
10625 graphic_info[special_graphic].anim_delay_fixed +
10626 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10627 player->post_delay_counter =
10628 graphic_info[special_graphic].post_delay_fixed +
10629 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10631 player->special_action_sleeping = special_action;
10634 if (player->anim_delay_counter > 0)
10636 player->action_waiting = player->special_action_sleeping;
10637 player->anim_delay_counter--;
10639 else if (player->post_delay_counter > 0)
10641 player->post_delay_counter--;
10645 else if (player->is_bored)
10647 if (player->num_special_action_bored > 0)
10649 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10651 int special_action =
10652 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10653 int special_graphic =
10654 el_act_dir2img(player->artwork_element, special_action, move_dir);
10656 player->anim_delay_counter =
10657 graphic_info[special_graphic].anim_delay_fixed +
10658 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10659 player->post_delay_counter =
10660 graphic_info[special_graphic].post_delay_fixed +
10661 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10663 player->special_action_bored = special_action;
10666 if (player->anim_delay_counter > 0)
10668 player->action_waiting = player->special_action_bored;
10669 player->anim_delay_counter--;
10671 else if (player->post_delay_counter > 0)
10673 player->post_delay_counter--;
10678 else if (last_waiting) /* waiting -> not waiting */
10680 player->is_waiting = FALSE;
10681 player->is_bored = FALSE;
10682 player->is_sleeping = FALSE;
10684 player->frame_counter_bored = -1;
10685 player->frame_counter_sleeping = -1;
10687 player->anim_delay_counter = 0;
10688 player->post_delay_counter = 0;
10690 player->dir_waiting = player->MovDir;
10691 player->action_waiting = ACTION_DEFAULT;
10693 player->special_action_bored = ACTION_DEFAULT;
10694 player->special_action_sleeping = ACTION_DEFAULT;
10698 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10700 if ((!player->is_moving && player->was_moving) ||
10701 (player->MovPos == 0 && player->was_moving) ||
10702 (player->is_snapping && !player->was_snapping) ||
10703 (player->is_dropping && !player->was_dropping))
10705 if (!CheckSaveEngineSnapshotToList())
10708 player->was_moving = FALSE;
10709 player->was_snapping = TRUE;
10710 player->was_dropping = TRUE;
10714 if (player->is_moving)
10715 player->was_moving = TRUE;
10717 if (!player->is_snapping)
10718 player->was_snapping = FALSE;
10720 if (!player->is_dropping)
10721 player->was_dropping = FALSE;
10725 static void CheckSingleStepMode(struct PlayerInfo *player)
10727 if (tape.single_step && tape.recording && !tape.pausing)
10729 /* as it is called "single step mode", just return to pause mode when the
10730 player stopped moving after one tile (or never starts moving at all) */
10731 if (!player->is_moving && !player->is_pushing)
10733 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10734 SnapField(player, 0, 0); /* stop snapping */
10738 CheckSaveEngineSnapshot(player);
10741 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10743 int left = player_action & JOY_LEFT;
10744 int right = player_action & JOY_RIGHT;
10745 int up = player_action & JOY_UP;
10746 int down = player_action & JOY_DOWN;
10747 int button1 = player_action & JOY_BUTTON_1;
10748 int button2 = player_action & JOY_BUTTON_2;
10749 int dx = (left ? -1 : right ? 1 : 0);
10750 int dy = (up ? -1 : down ? 1 : 0);
10752 if (!player->active || tape.pausing)
10758 SnapField(player, dx, dy);
10762 DropElement(player);
10764 MovePlayer(player, dx, dy);
10767 CheckSingleStepMode(player);
10769 SetPlayerWaiting(player, FALSE);
10771 return player_action;
10775 /* no actions for this player (no input at player's configured device) */
10777 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10778 SnapField(player, 0, 0);
10779 CheckGravityMovementWhenNotMoving(player);
10781 if (player->MovPos == 0)
10782 SetPlayerWaiting(player, TRUE);
10784 if (player->MovPos == 0) /* needed for tape.playing */
10785 player->is_moving = FALSE;
10787 player->is_dropping = FALSE;
10788 player->is_dropping_pressed = FALSE;
10789 player->drop_pressed_delay = 0;
10791 CheckSingleStepMode(player);
10797 static void CheckLevelTime()
10801 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10802 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10804 if (level.native_em_level->lev->home == 0) /* all players at home */
10806 PlayerWins(local_player);
10808 AllPlayersGone = TRUE;
10810 level.native_em_level->lev->home = -1;
10813 if (level.native_em_level->ply[0]->alive == 0 &&
10814 level.native_em_level->ply[1]->alive == 0 &&
10815 level.native_em_level->ply[2]->alive == 0 &&
10816 level.native_em_level->ply[3]->alive == 0) /* all dead */
10817 AllPlayersGone = TRUE;
10819 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10821 if (game_sp.LevelSolved &&
10822 !game_sp.GameOver) /* game won */
10824 PlayerWins(local_player);
10826 game_sp.GameOver = TRUE;
10828 AllPlayersGone = TRUE;
10831 if (game_sp.GameOver) /* game lost */
10832 AllPlayersGone = TRUE;
10835 if (TimeFrames >= FRAMES_PER_SECOND)
10840 for (i = 0; i < MAX_PLAYERS; i++)
10842 struct PlayerInfo *player = &stored_player[i];
10844 if (SHIELD_ON(player))
10846 player->shield_normal_time_left--;
10848 if (player->shield_deadly_time_left > 0)
10849 player->shield_deadly_time_left--;
10853 if (!local_player->LevelSolved && !level.use_step_counter)
10861 if (TimeLeft <= 10 && setup.time_limit)
10862 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10864 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10865 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10867 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10869 if (!TimeLeft && setup.time_limit)
10871 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10872 level.native_em_level->lev->killed_out_of_time = TRUE;
10874 for (i = 0; i < MAX_PLAYERS; i++)
10875 KillPlayer(&stored_player[i]);
10878 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10880 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10883 level.native_em_level->lev->time =
10884 (game.no_time_limit ? TimePlayed : TimeLeft);
10887 if (tape.recording || tape.playing)
10888 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10891 if (tape.recording || tape.playing)
10892 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10894 UpdateAndDisplayGameControlValues();
10897 void AdvanceFrameAndPlayerCounters(int player_nr)
10901 /* advance frame counters (global frame counter and time frame counter) */
10905 /* advance player counters (counters for move delay, move animation etc.) */
10906 for (i = 0; i < MAX_PLAYERS; i++)
10908 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10909 int move_delay_value = stored_player[i].move_delay_value;
10910 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10912 if (!advance_player_counters) /* not all players may be affected */
10915 if (move_frames == 0) /* less than one move per game frame */
10917 int stepsize = TILEX / move_delay_value;
10918 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10919 int count = (stored_player[i].is_moving ?
10920 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10922 if (count % delay == 0)
10926 stored_player[i].Frame += move_frames;
10928 if (stored_player[i].MovPos != 0)
10929 stored_player[i].StepFrame += move_frames;
10931 if (stored_player[i].move_delay > 0)
10932 stored_player[i].move_delay--;
10934 /* due to bugs in previous versions, counter must count up, not down */
10935 if (stored_player[i].push_delay != -1)
10936 stored_player[i].push_delay++;
10938 if (stored_player[i].drop_delay > 0)
10939 stored_player[i].drop_delay--;
10941 if (stored_player[i].is_dropping_pressed)
10942 stored_player[i].drop_pressed_delay++;
10946 void StartGameActions(boolean init_network_game, boolean record_tape,
10949 unsigned int new_random_seed = InitRND(random_seed);
10952 TapeStartRecording(new_random_seed);
10954 #if defined(NETWORK_AVALIABLE)
10955 if (init_network_game)
10957 SendToServer_StartPlaying();
10966 void GameActionsExt()
10969 static unsigned int game_frame_delay = 0;
10971 unsigned int game_frame_delay_value;
10972 byte *recorded_player_action;
10973 byte summarized_player_action = 0;
10974 byte tape_action[MAX_PLAYERS];
10977 /* detect endless loops, caused by custom element programming */
10978 if (recursion_loop_detected && recursion_loop_depth == 0)
10980 char *message = getStringCat3("Internal Error! Element ",
10981 EL_NAME(recursion_loop_element),
10982 " caused endless loop! Quit the game?");
10984 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10985 EL_NAME(recursion_loop_element));
10987 RequestQuitGameExt(FALSE, level_editor_test_game, message);
10989 recursion_loop_detected = FALSE; /* if game should be continued */
10996 if (game.restart_level)
10997 StartGameActions(options.network, setup.autorecord, level.random_seed);
10999 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11000 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11002 if (level.native_em_level->lev->home == 0) /* all players at home */
11004 PlayerWins(local_player);
11006 AllPlayersGone = TRUE;
11008 level.native_em_level->lev->home = -1;
11011 if (level.native_em_level->ply[0]->alive == 0 &&
11012 level.native_em_level->ply[1]->alive == 0 &&
11013 level.native_em_level->ply[2]->alive == 0 &&
11014 level.native_em_level->ply[3]->alive == 0) /* all dead */
11015 AllPlayersGone = TRUE;
11017 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11019 if (game_sp.LevelSolved &&
11020 !game_sp.GameOver) /* game won */
11022 PlayerWins(local_player);
11024 game_sp.GameOver = TRUE;
11026 AllPlayersGone = TRUE;
11029 if (game_sp.GameOver) /* game lost */
11030 AllPlayersGone = TRUE;
11033 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11036 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11039 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11042 game_frame_delay_value =
11043 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11045 if (tape.playing && tape.warp_forward && !tape.pausing)
11046 game_frame_delay_value = 0;
11048 SetVideoFrameDelay(game_frame_delay_value);
11052 /* ---------- main game synchronization point ---------- */
11054 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11056 printf("::: skip == %d\n", skip);
11059 /* ---------- main game synchronization point ---------- */
11061 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11065 if (network_playing && !network_player_action_received)
11067 /* try to get network player actions in time */
11069 #if defined(NETWORK_AVALIABLE)
11070 /* last chance to get network player actions without main loop delay */
11071 HandleNetworking();
11074 /* game was quit by network peer */
11075 if (game_status != GAME_MODE_PLAYING)
11078 if (!network_player_action_received)
11079 return; /* failed to get network player actions in time */
11081 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11087 /* at this point we know that we really continue executing the game */
11089 network_player_action_received = FALSE;
11091 /* when playing tape, read previously recorded player input from tape data */
11092 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11094 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11098 if (tape.set_centered_player)
11100 game.centered_player_nr_next = tape.centered_player_nr_next;
11101 game.set_centered_player = TRUE;
11104 for (i = 0; i < MAX_PLAYERS; i++)
11106 summarized_player_action |= stored_player[i].action;
11108 if (!network_playing && (game.team_mode || tape.playing))
11109 stored_player[i].effective_action = stored_player[i].action;
11112 #if defined(NETWORK_AVALIABLE)
11113 if (network_playing)
11114 SendToServer_MovePlayer(summarized_player_action);
11117 // summarize all actions at local players mapped input device position
11118 // (this allows using different input devices in single player mode)
11119 if (!options.network && !game.team_mode)
11120 stored_player[map_player_action[local_player->index_nr]].effective_action =
11121 summarized_player_action;
11123 if (tape.recording &&
11125 setup.input_on_focus &&
11126 game.centered_player_nr != -1)
11128 for (i = 0; i < MAX_PLAYERS; i++)
11129 stored_player[i].effective_action =
11130 (i == game.centered_player_nr ? summarized_player_action : 0);
11133 if (recorded_player_action != NULL)
11134 for (i = 0; i < MAX_PLAYERS; i++)
11135 stored_player[i].effective_action = recorded_player_action[i];
11137 for (i = 0; i < MAX_PLAYERS; i++)
11139 tape_action[i] = stored_player[i].effective_action;
11141 /* (this may happen in the RND game engine if a player was not present on
11142 the playfield on level start, but appeared later from a custom element */
11143 if (setup.team_mode &&
11146 !tape.player_participates[i])
11147 tape.player_participates[i] = TRUE;
11150 /* only record actions from input devices, but not programmed actions */
11151 if (tape.recording)
11152 TapeRecordAction(tape_action);
11154 #if USE_NEW_PLAYER_ASSIGNMENTS
11155 // !!! also map player actions in single player mode !!!
11156 // if (game.team_mode)
11159 byte mapped_action[MAX_PLAYERS];
11161 #if DEBUG_PLAYER_ACTIONS
11163 for (i = 0; i < MAX_PLAYERS; i++)
11164 printf(" %d, ", stored_player[i].effective_action);
11167 for (i = 0; i < MAX_PLAYERS; i++)
11168 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11170 for (i = 0; i < MAX_PLAYERS; i++)
11171 stored_player[i].effective_action = mapped_action[i];
11173 #if DEBUG_PLAYER_ACTIONS
11175 for (i = 0; i < MAX_PLAYERS; i++)
11176 printf(" %d, ", stored_player[i].effective_action);
11180 #if DEBUG_PLAYER_ACTIONS
11184 for (i = 0; i < MAX_PLAYERS; i++)
11185 printf(" %d, ", stored_player[i].effective_action);
11191 for (i = 0; i < MAX_PLAYERS; i++)
11193 // allow engine snapshot in case of changed movement attempt
11194 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11195 (stored_player[i].effective_action & KEY_MOTION))
11196 game.snapshot.changed_action = TRUE;
11198 // allow engine snapshot in case of snapping/dropping attempt
11199 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11200 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11201 game.snapshot.changed_action = TRUE;
11203 game.snapshot.last_action[i] = stored_player[i].effective_action;
11206 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11208 GameActions_EM_Main();
11210 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11212 GameActions_SP_Main();
11216 GameActions_RND_Main();
11219 BlitScreenToBitmap(backbuffer);
11223 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11225 if (options.debug) /* calculate frames per second */
11227 static unsigned int fps_counter = 0;
11228 static int fps_frames = 0;
11229 unsigned int fps_delay_ms = Counter() - fps_counter;
11233 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
11235 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11238 fps_counter = Counter();
11241 redraw_mask |= REDRAW_FPS;
11245 static void GameActions_CheckSaveEngineSnapshot()
11247 if (!game.snapshot.save_snapshot)
11250 // clear flag for saving snapshot _before_ saving snapshot
11251 game.snapshot.save_snapshot = FALSE;
11253 SaveEngineSnapshotToList();
11260 GameActions_CheckSaveEngineSnapshot();
11263 void GameActions_EM_Main()
11265 byte effective_action[MAX_PLAYERS];
11266 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11269 for (i = 0; i < MAX_PLAYERS; i++)
11270 effective_action[i] = stored_player[i].effective_action;
11272 GameActions_EM(effective_action, warp_mode);
11275 void GameActions_SP_Main()
11277 byte effective_action[MAX_PLAYERS];
11278 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11281 for (i = 0; i < MAX_PLAYERS; i++)
11282 effective_action[i] = stored_player[i].effective_action;
11284 GameActions_SP(effective_action, warp_mode);
11287 void GameActions_RND_Main()
11292 void GameActions_RND()
11294 int magic_wall_x = 0, magic_wall_y = 0;
11295 int i, x, y, element, graphic, last_gfx_frame;
11297 InitPlayfieldScanModeVars();
11299 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11301 SCAN_PLAYFIELD(x, y)
11303 ChangeCount[x][y] = 0;
11304 ChangeEvent[x][y] = -1;
11308 if (game.set_centered_player)
11310 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11312 /* switching to "all players" only possible if all players fit to screen */
11313 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11315 game.centered_player_nr_next = game.centered_player_nr;
11316 game.set_centered_player = FALSE;
11319 /* do not switch focus to non-existing (or non-active) player */
11320 if (game.centered_player_nr_next >= 0 &&
11321 !stored_player[game.centered_player_nr_next].active)
11323 game.centered_player_nr_next = game.centered_player_nr;
11324 game.set_centered_player = FALSE;
11328 if (game.set_centered_player &&
11329 ScreenMovPos == 0) /* screen currently aligned at tile position */
11333 if (game.centered_player_nr_next == -1)
11335 setScreenCenteredToAllPlayers(&sx, &sy);
11339 sx = stored_player[game.centered_player_nr_next].jx;
11340 sy = stored_player[game.centered_player_nr_next].jy;
11343 game.centered_player_nr = game.centered_player_nr_next;
11344 game.set_centered_player = FALSE;
11346 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11347 DrawGameDoorValues();
11350 for (i = 0; i < MAX_PLAYERS; i++)
11352 int actual_player_action = stored_player[i].effective_action;
11355 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11356 - rnd_equinox_tetrachloride 048
11357 - rnd_equinox_tetrachloride_ii 096
11358 - rnd_emanuel_schmieg 002
11359 - doctor_sloan_ww 001, 020
11361 if (stored_player[i].MovPos == 0)
11362 CheckGravityMovement(&stored_player[i]);
11365 /* overwrite programmed action with tape action */
11366 if (stored_player[i].programmed_action)
11367 actual_player_action = stored_player[i].programmed_action;
11369 PlayerActions(&stored_player[i], actual_player_action);
11371 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11374 ScrollScreen(NULL, SCROLL_GO_ON);
11376 /* for backwards compatibility, the following code emulates a fixed bug that
11377 occured when pushing elements (causing elements that just made their last
11378 pushing step to already (if possible) make their first falling step in the
11379 same game frame, which is bad); this code is also needed to use the famous
11380 "spring push bug" which is used in older levels and might be wanted to be
11381 used also in newer levels, but in this case the buggy pushing code is only
11382 affecting the "spring" element and no other elements */
11384 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11386 for (i = 0; i < MAX_PLAYERS; i++)
11388 struct PlayerInfo *player = &stored_player[i];
11389 int x = player->jx;
11390 int y = player->jy;
11392 if (player->active && player->is_pushing && player->is_moving &&
11394 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11395 Feld[x][y] == EL_SPRING))
11397 ContinueMoving(x, y);
11399 /* continue moving after pushing (this is actually a bug) */
11400 if (!IS_MOVING(x, y))
11401 Stop[x][y] = FALSE;
11406 SCAN_PLAYFIELD(x, y)
11408 ChangeCount[x][y] = 0;
11409 ChangeEvent[x][y] = -1;
11411 /* this must be handled before main playfield loop */
11412 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11415 if (MovDelay[x][y] <= 0)
11419 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11422 if (MovDelay[x][y] <= 0)
11425 TEST_DrawLevelField(x, y);
11427 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11432 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11434 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11435 printf("GameActions(): This should never happen!\n");
11437 ChangePage[x][y] = -1;
11441 Stop[x][y] = FALSE;
11442 if (WasJustMoving[x][y] > 0)
11443 WasJustMoving[x][y]--;
11444 if (WasJustFalling[x][y] > 0)
11445 WasJustFalling[x][y]--;
11446 if (CheckCollision[x][y] > 0)
11447 CheckCollision[x][y]--;
11448 if (CheckImpact[x][y] > 0)
11449 CheckImpact[x][y]--;
11453 /* reset finished pushing action (not done in ContinueMoving() to allow
11454 continuous pushing animation for elements with zero push delay) */
11455 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11457 ResetGfxAnimation(x, y);
11458 TEST_DrawLevelField(x, y);
11462 if (IS_BLOCKED(x, y))
11466 Blocked2Moving(x, y, &oldx, &oldy);
11467 if (!IS_MOVING(oldx, oldy))
11469 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11470 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11471 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11472 printf("GameActions(): This should never happen!\n");
11478 SCAN_PLAYFIELD(x, y)
11480 element = Feld[x][y];
11481 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11482 last_gfx_frame = GfxFrame[x][y];
11484 ResetGfxFrame(x, y);
11486 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11487 DrawLevelGraphicAnimation(x, y, graphic);
11489 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11490 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11491 ResetRandomAnimationValue(x, y);
11493 SetRandomAnimationValue(x, y);
11495 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11497 if (IS_INACTIVE(element))
11499 if (IS_ANIMATED(graphic))
11500 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11505 /* this may take place after moving, so 'element' may have changed */
11506 if (IS_CHANGING(x, y) &&
11507 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11509 int page = element_info[element].event_page_nr[CE_DELAY];
11511 HandleElementChange(x, y, page);
11513 element = Feld[x][y];
11514 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11517 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11521 element = Feld[x][y];
11522 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11524 if (IS_ANIMATED(graphic) &&
11525 !IS_MOVING(x, y) &&
11527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11529 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11530 TEST_DrawTwinkleOnField(x, y);
11532 else if (element == EL_ACID)
11535 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11537 else if ((element == EL_EXIT_OPEN ||
11538 element == EL_EM_EXIT_OPEN ||
11539 element == EL_SP_EXIT_OPEN ||
11540 element == EL_STEEL_EXIT_OPEN ||
11541 element == EL_EM_STEEL_EXIT_OPEN ||
11542 element == EL_SP_TERMINAL ||
11543 element == EL_SP_TERMINAL_ACTIVE ||
11544 element == EL_EXTRA_TIME ||
11545 element == EL_SHIELD_NORMAL ||
11546 element == EL_SHIELD_DEADLY) &&
11547 IS_ANIMATED(graphic))
11548 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11549 else if (IS_MOVING(x, y))
11550 ContinueMoving(x, y);
11551 else if (IS_ACTIVE_BOMB(element))
11552 CheckDynamite(x, y);
11553 else if (element == EL_AMOEBA_GROWING)
11554 AmoebeWaechst(x, y);
11555 else if (element == EL_AMOEBA_SHRINKING)
11556 AmoebaDisappearing(x, y);
11558 #if !USE_NEW_AMOEBA_CODE
11559 else if (IS_AMOEBALIVE(element))
11560 AmoebeAbleger(x, y);
11563 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11565 else if (element == EL_EXIT_CLOSED)
11567 else if (element == EL_EM_EXIT_CLOSED)
11569 else if (element == EL_STEEL_EXIT_CLOSED)
11570 CheckExitSteel(x, y);
11571 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11572 CheckExitSteelEM(x, y);
11573 else if (element == EL_SP_EXIT_CLOSED)
11575 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11576 element == EL_EXPANDABLE_STEELWALL_GROWING)
11577 MauerWaechst(x, y);
11578 else if (element == EL_EXPANDABLE_WALL ||
11579 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11580 element == EL_EXPANDABLE_WALL_VERTICAL ||
11581 element == EL_EXPANDABLE_WALL_ANY ||
11582 element == EL_BD_EXPANDABLE_WALL)
11583 MauerAbleger(x, y);
11584 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11585 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11586 element == EL_EXPANDABLE_STEELWALL_ANY)
11587 MauerAblegerStahl(x, y);
11588 else if (element == EL_FLAMES)
11589 CheckForDragon(x, y);
11590 else if (element == EL_EXPLOSION)
11591 ; /* drawing of correct explosion animation is handled separately */
11592 else if (element == EL_ELEMENT_SNAPPING ||
11593 element == EL_DIAGONAL_SHRINKING ||
11594 element == EL_DIAGONAL_GROWING)
11596 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11598 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11600 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11601 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11603 if (IS_BELT_ACTIVE(element))
11604 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11606 if (game.magic_wall_active)
11608 int jx = local_player->jx, jy = local_player->jy;
11610 /* play the element sound at the position nearest to the player */
11611 if ((element == EL_MAGIC_WALL_FULL ||
11612 element == EL_MAGIC_WALL_ACTIVE ||
11613 element == EL_MAGIC_WALL_EMPTYING ||
11614 element == EL_BD_MAGIC_WALL_FULL ||
11615 element == EL_BD_MAGIC_WALL_ACTIVE ||
11616 element == EL_BD_MAGIC_WALL_EMPTYING ||
11617 element == EL_DC_MAGIC_WALL_FULL ||
11618 element == EL_DC_MAGIC_WALL_ACTIVE ||
11619 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11620 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11628 #if USE_NEW_AMOEBA_CODE
11629 /* new experimental amoeba growth stuff */
11630 if (!(FrameCounter % 8))
11632 static unsigned int random = 1684108901;
11634 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11636 x = RND(lev_fieldx);
11637 y = RND(lev_fieldy);
11638 element = Feld[x][y];
11640 if (!IS_PLAYER(x,y) &&
11641 (element == EL_EMPTY ||
11642 CAN_GROW_INTO(element) ||
11643 element == EL_QUICKSAND_EMPTY ||
11644 element == EL_QUICKSAND_FAST_EMPTY ||
11645 element == EL_ACID_SPLASH_LEFT ||
11646 element == EL_ACID_SPLASH_RIGHT))
11648 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11649 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11650 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11651 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11652 Feld[x][y] = EL_AMOEBA_DROP;
11655 random = random * 129 + 1;
11660 game.explosions_delayed = FALSE;
11662 SCAN_PLAYFIELD(x, y)
11664 element = Feld[x][y];
11666 if (ExplodeField[x][y])
11667 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11668 else if (element == EL_EXPLOSION)
11669 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11671 ExplodeField[x][y] = EX_TYPE_NONE;
11674 game.explosions_delayed = TRUE;
11676 if (game.magic_wall_active)
11678 if (!(game.magic_wall_time_left % 4))
11680 int element = Feld[magic_wall_x][magic_wall_y];
11682 if (element == EL_BD_MAGIC_WALL_FULL ||
11683 element == EL_BD_MAGIC_WALL_ACTIVE ||
11684 element == EL_BD_MAGIC_WALL_EMPTYING)
11685 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11686 else if (element == EL_DC_MAGIC_WALL_FULL ||
11687 element == EL_DC_MAGIC_WALL_ACTIVE ||
11688 element == EL_DC_MAGIC_WALL_EMPTYING)
11689 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11691 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11694 if (game.magic_wall_time_left > 0)
11696 game.magic_wall_time_left--;
11698 if (!game.magic_wall_time_left)
11700 SCAN_PLAYFIELD(x, y)
11702 element = Feld[x][y];
11704 if (element == EL_MAGIC_WALL_ACTIVE ||
11705 element == EL_MAGIC_WALL_FULL)
11707 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11708 TEST_DrawLevelField(x, y);
11710 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11711 element == EL_BD_MAGIC_WALL_FULL)
11713 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11714 TEST_DrawLevelField(x, y);
11716 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11717 element == EL_DC_MAGIC_WALL_FULL)
11719 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11720 TEST_DrawLevelField(x, y);
11724 game.magic_wall_active = FALSE;
11729 if (game.light_time_left > 0)
11731 game.light_time_left--;
11733 if (game.light_time_left == 0)
11734 RedrawAllLightSwitchesAndInvisibleElements();
11737 if (game.timegate_time_left > 0)
11739 game.timegate_time_left--;
11741 if (game.timegate_time_left == 0)
11742 CloseAllOpenTimegates();
11745 if (game.lenses_time_left > 0)
11747 game.lenses_time_left--;
11749 if (game.lenses_time_left == 0)
11750 RedrawAllInvisibleElementsForLenses();
11753 if (game.magnify_time_left > 0)
11755 game.magnify_time_left--;
11757 if (game.magnify_time_left == 0)
11758 RedrawAllInvisibleElementsForMagnifier();
11761 for (i = 0; i < MAX_PLAYERS; i++)
11763 struct PlayerInfo *player = &stored_player[i];
11765 if (SHIELD_ON(player))
11767 if (player->shield_deadly_time_left)
11768 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11769 else if (player->shield_normal_time_left)
11770 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11774 #if USE_DELAYED_GFX_REDRAW
11775 SCAN_PLAYFIELD(x, y)
11777 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11779 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11780 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11782 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11783 DrawLevelField(x, y);
11785 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11786 DrawLevelFieldCrumbled(x, y);
11788 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11789 DrawLevelFieldCrumbledNeighbours(x, y);
11791 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11792 DrawTwinkleOnField(x, y);
11795 GfxRedraw[x][y] = GFX_REDRAW_NONE;
11800 PlayAllPlayersSound();
11802 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11804 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11806 local_player->show_envelope = 0;
11809 /* use random number generator in every frame to make it less predictable */
11810 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11814 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11816 int min_x = x, min_y = y, max_x = x, max_y = y;
11819 for (i = 0; i < MAX_PLAYERS; i++)
11821 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11823 if (!stored_player[i].active || &stored_player[i] == player)
11826 min_x = MIN(min_x, jx);
11827 min_y = MIN(min_y, jy);
11828 max_x = MAX(max_x, jx);
11829 max_y = MAX(max_y, jy);
11832 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11835 static boolean AllPlayersInVisibleScreen()
11839 for (i = 0; i < MAX_PLAYERS; i++)
11841 int jx = stored_player[i].jx, jy = stored_player[i].jy;
11843 if (!stored_player[i].active)
11846 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11853 void ScrollLevel(int dx, int dy)
11855 int scroll_offset = 2 * TILEX_VAR;
11858 BlitBitmap(drawto_field, drawto_field,
11859 FX + TILEX_VAR * (dx == -1) - scroll_offset,
11860 FY + TILEY_VAR * (dy == -1) - scroll_offset,
11861 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11862 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11863 FX + TILEX_VAR * (dx == 1) - scroll_offset,
11864 FY + TILEY_VAR * (dy == 1) - scroll_offset);
11868 x = (dx == 1 ? BX1 : BX2);
11869 for (y = BY1; y <= BY2; y++)
11870 DrawScreenField(x, y);
11875 y = (dy == 1 ? BY1 : BY2);
11876 for (x = BX1; x <= BX2; x++)
11877 DrawScreenField(x, y);
11880 redraw_mask |= REDRAW_FIELD;
11883 static boolean canFallDown(struct PlayerInfo *player)
11885 int jx = player->jx, jy = player->jy;
11887 return (IN_LEV_FIELD(jx, jy + 1) &&
11888 (IS_FREE(jx, jy + 1) ||
11889 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11890 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11891 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11894 static boolean canPassField(int x, int y, int move_dir)
11896 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11897 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11898 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11899 int nextx = x + dx;
11900 int nexty = y + dy;
11901 int element = Feld[x][y];
11903 return (IS_PASSABLE_FROM(element, opposite_dir) &&
11904 !CAN_MOVE(element) &&
11905 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11906 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11907 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11910 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11912 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11913 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11914 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
11918 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11919 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11920 (IS_DIGGABLE(Feld[newx][newy]) ||
11921 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11922 canPassField(newx, newy, move_dir)));
11925 static void CheckGravityMovement(struct PlayerInfo *player)
11927 if (player->gravity && !player->programmed_action)
11929 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11930 int move_dir_vertical = player->effective_action & MV_VERTICAL;
11931 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11932 int jx = player->jx, jy = player->jy;
11933 boolean player_is_moving_to_valid_field =
11934 (!player_is_snapping &&
11935 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11936 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11937 boolean player_can_fall_down = canFallDown(player);
11939 if (player_can_fall_down &&
11940 !player_is_moving_to_valid_field)
11941 player->programmed_action = MV_DOWN;
11945 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11947 return CheckGravityMovement(player);
11949 if (player->gravity && !player->programmed_action)
11951 int jx = player->jx, jy = player->jy;
11952 boolean field_under_player_is_free =
11953 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11954 boolean player_is_standing_on_valid_field =
11955 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11956 (IS_WALKABLE(Feld[jx][jy]) &&
11957 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11959 if (field_under_player_is_free && !player_is_standing_on_valid_field)
11960 player->programmed_action = MV_DOWN;
11965 MovePlayerOneStep()
11966 -----------------------------------------------------------------------------
11967 dx, dy: direction (non-diagonal) to try to move the player to
11968 real_dx, real_dy: direction as read from input device (can be diagonal)
11971 boolean MovePlayerOneStep(struct PlayerInfo *player,
11972 int dx, int dy, int real_dx, int real_dy)
11974 int jx = player->jx, jy = player->jy;
11975 int new_jx = jx + dx, new_jy = jy + dy;
11977 boolean player_can_move = !player->cannot_move;
11979 if (!player->active || (!dx && !dy))
11980 return MP_NO_ACTION;
11982 player->MovDir = (dx < 0 ? MV_LEFT :
11983 dx > 0 ? MV_RIGHT :
11985 dy > 0 ? MV_DOWN : MV_NONE);
11987 if (!IN_LEV_FIELD(new_jx, new_jy))
11988 return MP_NO_ACTION;
11990 if (!player_can_move)
11992 if (player->MovPos == 0)
11994 player->is_moving = FALSE;
11995 player->is_digging = FALSE;
11996 player->is_collecting = FALSE;
11997 player->is_snapping = FALSE;
11998 player->is_pushing = FALSE;
12002 if (!options.network && game.centered_player_nr == -1 &&
12003 !AllPlayersInSight(player, new_jx, new_jy))
12004 return MP_NO_ACTION;
12006 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12007 if (can_move != MP_MOVING)
12010 /* check if DigField() has caused relocation of the player */
12011 if (player->jx != jx || player->jy != jy)
12012 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12014 StorePlayer[jx][jy] = 0;
12015 player->last_jx = jx;
12016 player->last_jy = jy;
12017 player->jx = new_jx;
12018 player->jy = new_jy;
12019 StorePlayer[new_jx][new_jy] = player->element_nr;
12021 if (player->move_delay_value_next != -1)
12023 player->move_delay_value = player->move_delay_value_next;
12024 player->move_delay_value_next = -1;
12028 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12030 player->step_counter++;
12032 PlayerVisit[jx][jy] = FrameCounter;
12034 player->is_moving = TRUE;
12037 /* should better be called in MovePlayer(), but this breaks some tapes */
12038 ScrollPlayer(player, SCROLL_INIT);
12044 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12046 int jx = player->jx, jy = player->jy;
12047 int old_jx = jx, old_jy = jy;
12048 int moved = MP_NO_ACTION;
12050 if (!player->active)
12055 if (player->MovPos == 0)
12057 player->is_moving = FALSE;
12058 player->is_digging = FALSE;
12059 player->is_collecting = FALSE;
12060 player->is_snapping = FALSE;
12061 player->is_pushing = FALSE;
12067 if (player->move_delay > 0)
12070 player->move_delay = -1; /* set to "uninitialized" value */
12072 /* store if player is automatically moved to next field */
12073 player->is_auto_moving = (player->programmed_action != MV_NONE);
12075 /* remove the last programmed player action */
12076 player->programmed_action = 0;
12078 if (player->MovPos)
12080 /* should only happen if pre-1.2 tape recordings are played */
12081 /* this is only for backward compatibility */
12083 int original_move_delay_value = player->move_delay_value;
12086 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12090 /* scroll remaining steps with finest movement resolution */
12091 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12093 while (player->MovPos)
12095 ScrollPlayer(player, SCROLL_GO_ON);
12096 ScrollScreen(NULL, SCROLL_GO_ON);
12098 AdvanceFrameAndPlayerCounters(player->index_nr);
12101 BackToFront_WithFrameDelay(0);
12104 player->move_delay_value = original_move_delay_value;
12107 player->is_active = FALSE;
12109 if (player->last_move_dir & MV_HORIZONTAL)
12111 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12112 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12116 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12117 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12120 if (!moved && !player->is_active)
12122 player->is_moving = FALSE;
12123 player->is_digging = FALSE;
12124 player->is_collecting = FALSE;
12125 player->is_snapping = FALSE;
12126 player->is_pushing = FALSE;
12132 if (moved & MP_MOVING && !ScreenMovPos &&
12133 (player->index_nr == game.centered_player_nr ||
12134 game.centered_player_nr == -1))
12136 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12137 int offset = game.scroll_delay_value;
12139 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12141 /* actual player has left the screen -- scroll in that direction */
12142 if (jx != old_jx) /* player has moved horizontally */
12143 scroll_x += (jx - old_jx);
12144 else /* player has moved vertically */
12145 scroll_y += (jy - old_jy);
12149 if (jx != old_jx) /* player has moved horizontally */
12151 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12152 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12153 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12155 /* don't scroll over playfield boundaries */
12156 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12157 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12159 /* don't scroll more than one field at a time */
12160 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12162 /* don't scroll against the player's moving direction */
12163 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12164 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12165 scroll_x = old_scroll_x;
12167 else /* player has moved vertically */
12169 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12170 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12171 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12173 /* don't scroll over playfield boundaries */
12174 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12175 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12177 /* don't scroll more than one field at a time */
12178 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12180 /* don't scroll against the player's moving direction */
12181 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12182 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12183 scroll_y = old_scroll_y;
12187 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12189 if (!options.network && game.centered_player_nr == -1 &&
12190 !AllPlayersInVisibleScreen())
12192 scroll_x = old_scroll_x;
12193 scroll_y = old_scroll_y;
12197 ScrollScreen(player, SCROLL_INIT);
12198 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12203 player->StepFrame = 0;
12205 if (moved & MP_MOVING)
12207 if (old_jx != jx && old_jy == jy)
12208 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12209 else if (old_jx == jx && old_jy != jy)
12210 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12212 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12214 player->last_move_dir = player->MovDir;
12215 player->is_moving = TRUE;
12216 player->is_snapping = FALSE;
12217 player->is_switching = FALSE;
12218 player->is_dropping = FALSE;
12219 player->is_dropping_pressed = FALSE;
12220 player->drop_pressed_delay = 0;
12223 /* should better be called here than above, but this breaks some tapes */
12224 ScrollPlayer(player, SCROLL_INIT);
12229 CheckGravityMovementWhenNotMoving(player);
12231 player->is_moving = FALSE;
12233 /* at this point, the player is allowed to move, but cannot move right now
12234 (e.g. because of something blocking the way) -- ensure that the player
12235 is also allowed to move in the next frame (in old versions before 3.1.1,
12236 the player was forced to wait again for eight frames before next try) */
12238 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12239 player->move_delay = 0; /* allow direct movement in the next frame */
12242 if (player->move_delay == -1) /* not yet initialized by DigField() */
12243 player->move_delay = player->move_delay_value;
12245 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12247 TestIfPlayerTouchesBadThing(jx, jy);
12248 TestIfPlayerTouchesCustomElement(jx, jy);
12251 if (!player->active)
12252 RemovePlayer(player);
12257 void ScrollPlayer(struct PlayerInfo *player, int mode)
12259 int jx = player->jx, jy = player->jy;
12260 int last_jx = player->last_jx, last_jy = player->last_jy;
12261 int move_stepsize = TILEX / player->move_delay_value;
12263 if (!player->active)
12266 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12269 if (mode == SCROLL_INIT)
12271 player->actual_frame_counter = FrameCounter;
12272 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12274 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12275 Feld[last_jx][last_jy] == EL_EMPTY)
12277 int last_field_block_delay = 0; /* start with no blocking at all */
12278 int block_delay_adjustment = player->block_delay_adjustment;
12280 /* if player blocks last field, add delay for exactly one move */
12281 if (player->block_last_field)
12283 last_field_block_delay += player->move_delay_value;
12285 /* when blocking enabled, prevent moving up despite gravity */
12286 if (player->gravity && player->MovDir == MV_UP)
12287 block_delay_adjustment = -1;
12290 /* add block delay adjustment (also possible when not blocking) */
12291 last_field_block_delay += block_delay_adjustment;
12293 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12294 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12297 if (player->MovPos != 0) /* player has not yet reached destination */
12300 else if (!FrameReached(&player->actual_frame_counter, 1))
12303 if (player->MovPos != 0)
12305 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12306 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12308 /* before DrawPlayer() to draw correct player graphic for this case */
12309 if (player->MovPos == 0)
12310 CheckGravityMovement(player);
12313 if (player->MovPos == 0) /* player reached destination field */
12315 if (player->move_delay_reset_counter > 0)
12317 player->move_delay_reset_counter--;
12319 if (player->move_delay_reset_counter == 0)
12321 /* continue with normal speed after quickly moving through gate */
12322 HALVE_PLAYER_SPEED(player);
12324 /* be able to make the next move without delay */
12325 player->move_delay = 0;
12329 player->last_jx = jx;
12330 player->last_jy = jy;
12332 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12333 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12334 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12335 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12336 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12337 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12338 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12339 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12341 DrawPlayer(player); /* needed here only to cleanup last field */
12342 RemovePlayer(player);
12344 if (local_player->friends_still_needed == 0 ||
12345 IS_SP_ELEMENT(Feld[jx][jy]))
12346 PlayerWins(player);
12349 /* this breaks one level: "machine", level 000 */
12351 int move_direction = player->MovDir;
12352 int enter_side = MV_DIR_OPPOSITE(move_direction);
12353 int leave_side = move_direction;
12354 int old_jx = last_jx;
12355 int old_jy = last_jy;
12356 int old_element = Feld[old_jx][old_jy];
12357 int new_element = Feld[jx][jy];
12359 if (IS_CUSTOM_ELEMENT(old_element))
12360 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12362 player->index_bit, leave_side);
12364 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12365 CE_PLAYER_LEAVES_X,
12366 player->index_bit, leave_side);
12368 if (IS_CUSTOM_ELEMENT(new_element))
12369 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12370 player->index_bit, enter_side);
12372 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12373 CE_PLAYER_ENTERS_X,
12374 player->index_bit, enter_side);
12376 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12377 CE_MOVE_OF_X, move_direction);
12380 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12382 TestIfPlayerTouchesBadThing(jx, jy);
12383 TestIfPlayerTouchesCustomElement(jx, jy);
12385 /* needed because pushed element has not yet reached its destination,
12386 so it would trigger a change event at its previous field location */
12387 if (!player->is_pushing)
12388 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12390 if (!player->active)
12391 RemovePlayer(player);
12394 if (!local_player->LevelSolved && level.use_step_counter)
12404 if (TimeLeft <= 10 && setup.time_limit)
12405 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12407 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12409 DisplayGameControlValues();
12411 if (!TimeLeft && setup.time_limit)
12412 for (i = 0; i < MAX_PLAYERS; i++)
12413 KillPlayer(&stored_player[i]);
12415 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12417 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12419 DisplayGameControlValues();
12423 if (tape.single_step && tape.recording && !tape.pausing &&
12424 !player->programmed_action)
12425 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12427 if (!player->programmed_action)
12428 CheckSaveEngineSnapshot(player);
12432 void ScrollScreen(struct PlayerInfo *player, int mode)
12434 static unsigned int screen_frame_counter = 0;
12436 if (mode == SCROLL_INIT)
12438 /* set scrolling step size according to actual player's moving speed */
12439 ScrollStepSize = TILEX / player->move_delay_value;
12441 screen_frame_counter = FrameCounter;
12442 ScreenMovDir = player->MovDir;
12443 ScreenMovPos = player->MovPos;
12444 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12447 else if (!FrameReached(&screen_frame_counter, 1))
12452 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12453 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12454 redraw_mask |= REDRAW_FIELD;
12457 ScreenMovDir = MV_NONE;
12460 void TestIfPlayerTouchesCustomElement(int x, int y)
12462 static int xy[4][2] =
12469 static int trigger_sides[4][2] =
12471 /* center side border side */
12472 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12473 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12474 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12475 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12477 static int touch_dir[4] =
12479 MV_LEFT | MV_RIGHT,
12484 int center_element = Feld[x][y]; /* should always be non-moving! */
12487 for (i = 0; i < NUM_DIRECTIONS; i++)
12489 int xx = x + xy[i][0];
12490 int yy = y + xy[i][1];
12491 int center_side = trigger_sides[i][0];
12492 int border_side = trigger_sides[i][1];
12493 int border_element;
12495 if (!IN_LEV_FIELD(xx, yy))
12498 if (IS_PLAYER(x, y)) /* player found at center element */
12500 struct PlayerInfo *player = PLAYERINFO(x, y);
12502 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12503 border_element = Feld[xx][yy]; /* may be moving! */
12504 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12505 border_element = Feld[xx][yy];
12506 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12507 border_element = MovingOrBlocked2Element(xx, yy);
12509 continue; /* center and border element do not touch */
12511 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12512 player->index_bit, border_side);
12513 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12514 CE_PLAYER_TOUCHES_X,
12515 player->index_bit, border_side);
12518 /* use player element that is initially defined in the level playfield,
12519 not the player element that corresponds to the runtime player number
12520 (example: a level that contains EL_PLAYER_3 as the only player would
12521 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12522 int player_element = PLAYERINFO(x, y)->initial_element;
12524 CheckElementChangeBySide(xx, yy, border_element, player_element,
12525 CE_TOUCHING_X, border_side);
12528 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12530 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12532 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12534 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12535 continue; /* center and border element do not touch */
12538 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12539 player->index_bit, center_side);
12540 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12541 CE_PLAYER_TOUCHES_X,
12542 player->index_bit, center_side);
12545 /* use player element that is initially defined in the level playfield,
12546 not the player element that corresponds to the runtime player number
12547 (example: a level that contains EL_PLAYER_3 as the only player would
12548 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12549 int player_element = PLAYERINFO(xx, yy)->initial_element;
12551 CheckElementChangeBySide(x, y, center_element, player_element,
12552 CE_TOUCHING_X, center_side);
12560 void TestIfElementTouchesCustomElement(int x, int y)
12562 static int xy[4][2] =
12569 static int trigger_sides[4][2] =
12571 /* center side border side */
12572 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12573 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12574 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12575 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12577 static int touch_dir[4] =
12579 MV_LEFT | MV_RIGHT,
12584 boolean change_center_element = FALSE;
12585 int center_element = Feld[x][y]; /* should always be non-moving! */
12586 int border_element_old[NUM_DIRECTIONS];
12589 for (i = 0; i < NUM_DIRECTIONS; i++)
12591 int xx = x + xy[i][0];
12592 int yy = y + xy[i][1];
12593 int border_element;
12595 border_element_old[i] = -1;
12597 if (!IN_LEV_FIELD(xx, yy))
12600 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12601 border_element = Feld[xx][yy]; /* may be moving! */
12602 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12603 border_element = Feld[xx][yy];
12604 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12605 border_element = MovingOrBlocked2Element(xx, yy);
12607 continue; /* center and border element do not touch */
12609 border_element_old[i] = border_element;
12612 for (i = 0; i < NUM_DIRECTIONS; i++)
12614 int xx = x + xy[i][0];
12615 int yy = y + xy[i][1];
12616 int center_side = trigger_sides[i][0];
12617 int border_element = border_element_old[i];
12619 if (border_element == -1)
12622 /* check for change of border element */
12623 CheckElementChangeBySide(xx, yy, border_element, center_element,
12624 CE_TOUCHING_X, center_side);
12626 /* (center element cannot be player, so we dont have to check this here) */
12629 for (i = 0; i < NUM_DIRECTIONS; i++)
12631 int xx = x + xy[i][0];
12632 int yy = y + xy[i][1];
12633 int border_side = trigger_sides[i][1];
12634 int border_element = border_element_old[i];
12636 if (border_element == -1)
12639 /* check for change of center element (but change it only once) */
12640 if (!change_center_element)
12641 change_center_element =
12642 CheckElementChangeBySide(x, y, center_element, border_element,
12643 CE_TOUCHING_X, border_side);
12645 if (IS_PLAYER(xx, yy))
12647 /* use player element that is initially defined in the level playfield,
12648 not the player element that corresponds to the runtime player number
12649 (example: a level that contains EL_PLAYER_3 as the only player would
12650 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12651 int player_element = PLAYERINFO(xx, yy)->initial_element;
12653 CheckElementChangeBySide(x, y, center_element, player_element,
12654 CE_TOUCHING_X, border_side);
12659 void TestIfElementHitsCustomElement(int x, int y, int direction)
12661 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12662 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12663 int hitx = x + dx, hity = y + dy;
12664 int hitting_element = Feld[x][y];
12665 int touched_element;
12667 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12670 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12671 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12673 if (IN_LEV_FIELD(hitx, hity))
12675 int opposite_direction = MV_DIR_OPPOSITE(direction);
12676 int hitting_side = direction;
12677 int touched_side = opposite_direction;
12678 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12679 MovDir[hitx][hity] != direction ||
12680 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12686 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12687 CE_HITTING_X, touched_side);
12689 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12690 CE_HIT_BY_X, hitting_side);
12692 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12693 CE_HIT_BY_SOMETHING, opposite_direction);
12695 if (IS_PLAYER(hitx, hity))
12697 /* use player element that is initially defined in the level playfield,
12698 not the player element that corresponds to the runtime player number
12699 (example: a level that contains EL_PLAYER_3 as the only player would
12700 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12701 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12703 CheckElementChangeBySide(x, y, hitting_element, player_element,
12704 CE_HITTING_X, touched_side);
12709 /* "hitting something" is also true when hitting the playfield border */
12710 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12711 CE_HITTING_SOMETHING, direction);
12714 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12716 int i, kill_x = -1, kill_y = -1;
12718 int bad_element = -1;
12719 static int test_xy[4][2] =
12726 static int test_dir[4] =
12734 for (i = 0; i < NUM_DIRECTIONS; i++)
12736 int test_x, test_y, test_move_dir, test_element;
12738 test_x = good_x + test_xy[i][0];
12739 test_y = good_y + test_xy[i][1];
12741 if (!IN_LEV_FIELD(test_x, test_y))
12745 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12747 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12749 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12750 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12752 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12753 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
12757 bad_element = test_element;
12763 if (kill_x != -1 || kill_y != -1)
12765 if (IS_PLAYER(good_x, good_y))
12767 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12769 if (player->shield_deadly_time_left > 0 &&
12770 !IS_INDESTRUCTIBLE(bad_element))
12771 Bang(kill_x, kill_y);
12772 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12773 KillPlayer(player);
12776 Bang(good_x, good_y);
12780 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12782 int i, kill_x = -1, kill_y = -1;
12783 int bad_element = Feld[bad_x][bad_y];
12784 static int test_xy[4][2] =
12791 static int touch_dir[4] =
12793 MV_LEFT | MV_RIGHT,
12798 static int test_dir[4] =
12806 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
12809 for (i = 0; i < NUM_DIRECTIONS; i++)
12811 int test_x, test_y, test_move_dir, test_element;
12813 test_x = bad_x + test_xy[i][0];
12814 test_y = bad_y + test_xy[i][1];
12816 if (!IN_LEV_FIELD(test_x, test_y))
12820 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12822 test_element = Feld[test_x][test_y];
12824 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12825 2nd case: DONT_TOUCH style bad thing does not move away from good thing
12827 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
12828 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
12830 /* good thing is player or penguin that does not move away */
12831 if (IS_PLAYER(test_x, test_y))
12833 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12835 if (bad_element == EL_ROBOT && player->is_moving)
12836 continue; /* robot does not kill player if he is moving */
12838 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12840 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12841 continue; /* center and border element do not touch */
12849 else if (test_element == EL_PENGUIN)
12859 if (kill_x != -1 || kill_y != -1)
12861 if (IS_PLAYER(kill_x, kill_y))
12863 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12865 if (player->shield_deadly_time_left > 0 &&
12866 !IS_INDESTRUCTIBLE(bad_element))
12867 Bang(bad_x, bad_y);
12868 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12869 KillPlayer(player);
12872 Bang(kill_x, kill_y);
12876 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12878 int bad_element = Feld[bad_x][bad_y];
12879 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12880 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
12881 int test_x = bad_x + dx, test_y = bad_y + dy;
12882 int test_move_dir, test_element;
12883 int kill_x = -1, kill_y = -1;
12885 if (!IN_LEV_FIELD(test_x, test_y))
12889 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12891 test_element = Feld[test_x][test_y];
12893 if (test_move_dir != bad_move_dir)
12895 /* good thing can be player or penguin that does not move away */
12896 if (IS_PLAYER(test_x, test_y))
12898 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12900 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12901 player as being hit when he is moving towards the bad thing, because
12902 the "get hit by" condition would be lost after the player stops) */
12903 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12904 return; /* player moves away from bad thing */
12909 else if (test_element == EL_PENGUIN)
12916 if (kill_x != -1 || kill_y != -1)
12918 if (IS_PLAYER(kill_x, kill_y))
12920 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12922 if (player->shield_deadly_time_left > 0 &&
12923 !IS_INDESTRUCTIBLE(bad_element))
12924 Bang(bad_x, bad_y);
12925 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12926 KillPlayer(player);
12929 Bang(kill_x, kill_y);
12933 void TestIfPlayerTouchesBadThing(int x, int y)
12935 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12938 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12940 TestIfGoodThingHitsBadThing(x, y, move_dir);
12943 void TestIfBadThingTouchesPlayer(int x, int y)
12945 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12948 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12950 TestIfBadThingHitsGoodThing(x, y, move_dir);
12953 void TestIfFriendTouchesBadThing(int x, int y)
12955 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12958 void TestIfBadThingTouchesFriend(int x, int y)
12960 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12963 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12965 int i, kill_x = bad_x, kill_y = bad_y;
12966 static int xy[4][2] =
12974 for (i = 0; i < NUM_DIRECTIONS; i++)
12978 x = bad_x + xy[i][0];
12979 y = bad_y + xy[i][1];
12980 if (!IN_LEV_FIELD(x, y))
12983 element = Feld[x][y];
12984 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12985 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12993 if (kill_x != bad_x || kill_y != bad_y)
12994 Bang(bad_x, bad_y);
12997 void KillPlayer(struct PlayerInfo *player)
12999 int jx = player->jx, jy = player->jy;
13001 if (!player->active)
13005 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13006 player->killed, player->active, player->reanimated);
13009 /* the following code was introduced to prevent an infinite loop when calling
13011 -> CheckTriggeredElementChangeExt()
13012 -> ExecuteCustomElementAction()
13014 -> (infinitely repeating the above sequence of function calls)
13015 which occurs when killing the player while having a CE with the setting
13016 "kill player X when explosion of <player X>"; the solution using a new
13017 field "player->killed" was chosen for backwards compatibility, although
13018 clever use of the fields "player->active" etc. would probably also work */
13020 if (player->killed)
13024 player->killed = TRUE;
13026 /* remove accessible field at the player's position */
13027 Feld[jx][jy] = EL_EMPTY;
13029 /* deactivate shield (else Bang()/Explode() would not work right) */
13030 player->shield_normal_time_left = 0;
13031 player->shield_deadly_time_left = 0;
13034 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13035 player->killed, player->active, player->reanimated);
13041 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13042 player->killed, player->active, player->reanimated);
13045 if (player->reanimated) /* killed player may have been reanimated */
13046 player->killed = player->reanimated = FALSE;
13048 BuryPlayer(player);
13051 static void KillPlayerUnlessEnemyProtected(int x, int y)
13053 if (!PLAYER_ENEMY_PROTECTED(x, y))
13054 KillPlayer(PLAYERINFO(x, y));
13057 static void KillPlayerUnlessExplosionProtected(int x, int y)
13059 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13060 KillPlayer(PLAYERINFO(x, y));
13063 void BuryPlayer(struct PlayerInfo *player)
13065 int jx = player->jx, jy = player->jy;
13067 if (!player->active)
13070 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13071 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13073 player->GameOver = TRUE;
13074 RemovePlayer(player);
13077 void RemovePlayer(struct PlayerInfo *player)
13079 int jx = player->jx, jy = player->jy;
13080 int i, found = FALSE;
13082 player->present = FALSE;
13083 player->active = FALSE;
13085 if (!ExplodeField[jx][jy])
13086 StorePlayer[jx][jy] = 0;
13088 if (player->is_moving)
13089 TEST_DrawLevelField(player->last_jx, player->last_jy);
13091 for (i = 0; i < MAX_PLAYERS; i++)
13092 if (stored_player[i].active)
13096 AllPlayersGone = TRUE;
13102 static void setFieldForSnapping(int x, int y, int element, int direction)
13104 struct ElementInfo *ei = &element_info[element];
13105 int direction_bit = MV_DIR_TO_BIT(direction);
13106 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13107 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13108 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13110 Feld[x][y] = EL_ELEMENT_SNAPPING;
13111 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13113 ResetGfxAnimation(x, y);
13115 GfxElement[x][y] = element;
13116 GfxAction[x][y] = action;
13117 GfxDir[x][y] = direction;
13118 GfxFrame[x][y] = -1;
13122 =============================================================================
13123 checkDiagonalPushing()
13124 -----------------------------------------------------------------------------
13125 check if diagonal input device direction results in pushing of object
13126 (by checking if the alternative direction is walkable, diggable, ...)
13127 =============================================================================
13130 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13131 int x, int y, int real_dx, int real_dy)
13133 int jx, jy, dx, dy, xx, yy;
13135 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13138 /* diagonal direction: check alternative direction */
13143 xx = jx + (dx == 0 ? real_dx : 0);
13144 yy = jy + (dy == 0 ? real_dy : 0);
13146 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13150 =============================================================================
13152 -----------------------------------------------------------------------------
13153 x, y: field next to player (non-diagonal) to try to dig to
13154 real_dx, real_dy: direction as read from input device (can be diagonal)
13155 =============================================================================
13158 static int DigField(struct PlayerInfo *player,
13159 int oldx, int oldy, int x, int y,
13160 int real_dx, int real_dy, int mode)
13162 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13163 boolean player_was_pushing = player->is_pushing;
13164 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13165 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13166 int jx = oldx, jy = oldy;
13167 int dx = x - jx, dy = y - jy;
13168 int nextx = x + dx, nexty = y + dy;
13169 int move_direction = (dx == -1 ? MV_LEFT :
13170 dx == +1 ? MV_RIGHT :
13172 dy == +1 ? MV_DOWN : MV_NONE);
13173 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13174 int dig_side = MV_DIR_OPPOSITE(move_direction);
13175 int old_element = Feld[jx][jy];
13176 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13179 if (is_player) /* function can also be called by EL_PENGUIN */
13181 if (player->MovPos == 0)
13183 player->is_digging = FALSE;
13184 player->is_collecting = FALSE;
13187 if (player->MovPos == 0) /* last pushing move finished */
13188 player->is_pushing = FALSE;
13190 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13192 player->is_switching = FALSE;
13193 player->push_delay = -1;
13195 return MP_NO_ACTION;
13199 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13200 old_element = Back[jx][jy];
13202 /* in case of element dropped at player position, check background */
13203 else if (Back[jx][jy] != EL_EMPTY &&
13204 game.engine_version >= VERSION_IDENT(2,2,0,0))
13205 old_element = Back[jx][jy];
13207 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13208 return MP_NO_ACTION; /* field has no opening in this direction */
13210 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13211 return MP_NO_ACTION; /* field has no opening in this direction */
13213 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13217 Feld[jx][jy] = player->artwork_element;
13218 InitMovingField(jx, jy, MV_DOWN);
13219 Store[jx][jy] = EL_ACID;
13220 ContinueMoving(jx, jy);
13221 BuryPlayer(player);
13223 return MP_DONT_RUN_INTO;
13226 if (player_can_move && DONT_RUN_INTO(element))
13228 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13230 return MP_DONT_RUN_INTO;
13233 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13234 return MP_NO_ACTION;
13236 collect_count = element_info[element].collect_count_initial;
13238 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13239 return MP_NO_ACTION;
13241 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13242 player_can_move = player_can_move_or_snap;
13244 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13245 game.engine_version >= VERSION_IDENT(2,2,0,0))
13247 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13248 player->index_bit, dig_side);
13249 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13250 player->index_bit, dig_side);
13252 if (element == EL_DC_LANDMINE)
13255 if (Feld[x][y] != element) /* field changed by snapping */
13258 return MP_NO_ACTION;
13261 if (player->gravity && is_player && !player->is_auto_moving &&
13262 canFallDown(player) && move_direction != MV_DOWN &&
13263 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13264 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13266 if (player_can_move &&
13267 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13269 int sound_element = SND_ELEMENT(element);
13270 int sound_action = ACTION_WALKING;
13272 if (IS_RND_GATE(element))
13274 if (!player->key[RND_GATE_NR(element)])
13275 return MP_NO_ACTION;
13277 else if (IS_RND_GATE_GRAY(element))
13279 if (!player->key[RND_GATE_GRAY_NR(element)])
13280 return MP_NO_ACTION;
13282 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13284 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13285 return MP_NO_ACTION;
13287 else if (element == EL_EXIT_OPEN ||
13288 element == EL_EM_EXIT_OPEN ||
13289 element == EL_EM_EXIT_OPENING ||
13290 element == EL_STEEL_EXIT_OPEN ||
13291 element == EL_EM_STEEL_EXIT_OPEN ||
13292 element == EL_EM_STEEL_EXIT_OPENING ||
13293 element == EL_SP_EXIT_OPEN ||
13294 element == EL_SP_EXIT_OPENING)
13296 sound_action = ACTION_PASSING; /* player is passing exit */
13298 else if (element == EL_EMPTY)
13300 sound_action = ACTION_MOVING; /* nothing to walk on */
13303 /* play sound from background or player, whatever is available */
13304 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13305 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13307 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13309 else if (player_can_move &&
13310 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13312 if (!ACCESS_FROM(element, opposite_direction))
13313 return MP_NO_ACTION; /* field not accessible from this direction */
13315 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13316 return MP_NO_ACTION;
13318 if (IS_EM_GATE(element))
13320 if (!player->key[EM_GATE_NR(element)])
13321 return MP_NO_ACTION;
13323 else if (IS_EM_GATE_GRAY(element))
13325 if (!player->key[EM_GATE_GRAY_NR(element)])
13326 return MP_NO_ACTION;
13328 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13330 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13331 return MP_NO_ACTION;
13333 else if (IS_EMC_GATE(element))
13335 if (!player->key[EMC_GATE_NR(element)])
13336 return MP_NO_ACTION;
13338 else if (IS_EMC_GATE_GRAY(element))
13340 if (!player->key[EMC_GATE_GRAY_NR(element)])
13341 return MP_NO_ACTION;
13343 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13345 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13346 return MP_NO_ACTION;
13348 else if (element == EL_DC_GATE_WHITE ||
13349 element == EL_DC_GATE_WHITE_GRAY ||
13350 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13352 if (player->num_white_keys == 0)
13353 return MP_NO_ACTION;
13355 player->num_white_keys--;
13357 else if (IS_SP_PORT(element))
13359 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13360 element == EL_SP_GRAVITY_PORT_RIGHT ||
13361 element == EL_SP_GRAVITY_PORT_UP ||
13362 element == EL_SP_GRAVITY_PORT_DOWN)
13363 player->gravity = !player->gravity;
13364 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13365 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13366 element == EL_SP_GRAVITY_ON_PORT_UP ||
13367 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13368 player->gravity = TRUE;
13369 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13370 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13371 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13372 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13373 player->gravity = FALSE;
13376 /* automatically move to the next field with double speed */
13377 player->programmed_action = move_direction;
13379 if (player->move_delay_reset_counter == 0)
13381 player->move_delay_reset_counter = 2; /* two double speed steps */
13383 DOUBLE_PLAYER_SPEED(player);
13386 PlayLevelSoundAction(x, y, ACTION_PASSING);
13388 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13392 if (mode != DF_SNAP)
13394 GfxElement[x][y] = GFX_ELEMENT(element);
13395 player->is_digging = TRUE;
13398 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13400 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13401 player->index_bit, dig_side);
13403 if (mode == DF_SNAP)
13405 if (level.block_snap_field)
13406 setFieldForSnapping(x, y, element, move_direction);
13408 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13410 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13411 player->index_bit, dig_side);
13414 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13418 if (is_player && mode != DF_SNAP)
13420 GfxElement[x][y] = element;
13421 player->is_collecting = TRUE;
13424 if (element == EL_SPEED_PILL)
13426 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13428 else if (element == EL_EXTRA_TIME && level.time > 0)
13430 TimeLeft += level.extra_time;
13432 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13434 DisplayGameControlValues();
13436 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13438 player->shield_normal_time_left += level.shield_normal_time;
13439 if (element == EL_SHIELD_DEADLY)
13440 player->shield_deadly_time_left += level.shield_deadly_time;
13442 else if (element == EL_DYNAMITE ||
13443 element == EL_EM_DYNAMITE ||
13444 element == EL_SP_DISK_RED)
13446 if (player->inventory_size < MAX_INVENTORY_SIZE)
13447 player->inventory_element[player->inventory_size++] = element;
13449 DrawGameDoorValues();
13451 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13453 player->dynabomb_count++;
13454 player->dynabombs_left++;
13456 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13458 player->dynabomb_size++;
13460 else if (element == EL_DYNABOMB_INCREASE_POWER)
13462 player->dynabomb_xl = TRUE;
13464 else if (IS_KEY(element))
13466 player->key[KEY_NR(element)] = TRUE;
13468 DrawGameDoorValues();
13470 else if (element == EL_DC_KEY_WHITE)
13472 player->num_white_keys++;
13474 /* display white keys? */
13475 /* DrawGameDoorValues(); */
13477 else if (IS_ENVELOPE(element))
13479 player->show_envelope = element;
13481 else if (element == EL_EMC_LENSES)
13483 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13485 RedrawAllInvisibleElementsForLenses();
13487 else if (element == EL_EMC_MAGNIFIER)
13489 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13491 RedrawAllInvisibleElementsForMagnifier();
13493 else if (IS_DROPPABLE(element) ||
13494 IS_THROWABLE(element)) /* can be collected and dropped */
13498 if (collect_count == 0)
13499 player->inventory_infinite_element = element;
13501 for (i = 0; i < collect_count; i++)
13502 if (player->inventory_size < MAX_INVENTORY_SIZE)
13503 player->inventory_element[player->inventory_size++] = element;
13505 DrawGameDoorValues();
13507 else if (collect_count > 0)
13509 local_player->gems_still_needed -= collect_count;
13510 if (local_player->gems_still_needed < 0)
13511 local_player->gems_still_needed = 0;
13513 game.snapshot.collected_item = TRUE;
13515 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13517 DisplayGameControlValues();
13520 RaiseScoreElement(element);
13521 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13524 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13525 player->index_bit, dig_side);
13527 if (mode == DF_SNAP)
13529 if (level.block_snap_field)
13530 setFieldForSnapping(x, y, element, move_direction);
13532 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13534 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13535 player->index_bit, dig_side);
13538 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13540 if (mode == DF_SNAP && element != EL_BD_ROCK)
13541 return MP_NO_ACTION;
13543 if (CAN_FALL(element) && dy)
13544 return MP_NO_ACTION;
13546 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13547 !(element == EL_SPRING && level.use_spring_bug))
13548 return MP_NO_ACTION;
13550 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13551 ((move_direction & MV_VERTICAL &&
13552 ((element_info[element].move_pattern & MV_LEFT &&
13553 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13554 (element_info[element].move_pattern & MV_RIGHT &&
13555 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13556 (move_direction & MV_HORIZONTAL &&
13557 ((element_info[element].move_pattern & MV_UP &&
13558 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13559 (element_info[element].move_pattern & MV_DOWN &&
13560 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13561 return MP_NO_ACTION;
13563 /* do not push elements already moving away faster than player */
13564 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13565 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13566 return MP_NO_ACTION;
13568 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13570 if (player->push_delay_value == -1 || !player_was_pushing)
13571 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13573 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13575 if (player->push_delay_value == -1)
13576 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13578 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13580 if (!player->is_pushing)
13581 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13584 player->is_pushing = TRUE;
13585 player->is_active = TRUE;
13587 if (!(IN_LEV_FIELD(nextx, nexty) &&
13588 (IS_FREE(nextx, nexty) ||
13589 (IS_SB_ELEMENT(element) &&
13590 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13591 (IS_CUSTOM_ELEMENT(element) &&
13592 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13593 return MP_NO_ACTION;
13595 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13596 return MP_NO_ACTION;
13598 if (player->push_delay == -1) /* new pushing; restart delay */
13599 player->push_delay = 0;
13601 if (player->push_delay < player->push_delay_value &&
13602 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13603 element != EL_SPRING && element != EL_BALLOON)
13605 /* make sure that there is no move delay before next try to push */
13606 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13607 player->move_delay = 0;
13609 return MP_NO_ACTION;
13612 if (IS_CUSTOM_ELEMENT(element) &&
13613 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13615 if (!DigFieldByCE(nextx, nexty, element))
13616 return MP_NO_ACTION;
13619 if (IS_SB_ELEMENT(element))
13621 if (element == EL_SOKOBAN_FIELD_FULL)
13623 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13624 local_player->sokobanfields_still_needed++;
13627 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13629 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13630 local_player->sokobanfields_still_needed--;
13633 Feld[x][y] = EL_SOKOBAN_OBJECT;
13635 if (Back[x][y] == Back[nextx][nexty])
13636 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13637 else if (Back[x][y] != 0)
13638 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13641 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13644 if (local_player->sokobanfields_still_needed == 0 &&
13645 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13647 PlayerWins(player);
13649 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13653 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13655 InitMovingField(x, y, move_direction);
13656 GfxAction[x][y] = ACTION_PUSHING;
13658 if (mode == DF_SNAP)
13659 ContinueMoving(x, y);
13661 MovPos[x][y] = (dx != 0 ? dx : dy);
13663 Pushed[x][y] = TRUE;
13664 Pushed[nextx][nexty] = TRUE;
13666 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13667 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13669 player->push_delay_value = -1; /* get new value later */
13671 /* check for element change _after_ element has been pushed */
13672 if (game.use_change_when_pushing_bug)
13674 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13675 player->index_bit, dig_side);
13676 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13677 player->index_bit, dig_side);
13680 else if (IS_SWITCHABLE(element))
13682 if (PLAYER_SWITCHING(player, x, y))
13684 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13685 player->index_bit, dig_side);
13690 player->is_switching = TRUE;
13691 player->switch_x = x;
13692 player->switch_y = y;
13694 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13696 if (element == EL_ROBOT_WHEEL)
13698 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13702 game.robot_wheel_active = TRUE;
13704 TEST_DrawLevelField(x, y);
13706 else if (element == EL_SP_TERMINAL)
13710 SCAN_PLAYFIELD(xx, yy)
13712 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13716 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13718 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13720 ResetGfxAnimation(xx, yy);
13721 TEST_DrawLevelField(xx, yy);
13725 else if (IS_BELT_SWITCH(element))
13727 ToggleBeltSwitch(x, y);
13729 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13730 element == EL_SWITCHGATE_SWITCH_DOWN ||
13731 element == EL_DC_SWITCHGATE_SWITCH_UP ||
13732 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13734 ToggleSwitchgateSwitch(x, y);
13736 else if (element == EL_LIGHT_SWITCH ||
13737 element == EL_LIGHT_SWITCH_ACTIVE)
13739 ToggleLightSwitch(x, y);
13741 else if (element == EL_TIMEGATE_SWITCH ||
13742 element == EL_DC_TIMEGATE_SWITCH)
13744 ActivateTimegateSwitch(x, y);
13746 else if (element == EL_BALLOON_SWITCH_LEFT ||
13747 element == EL_BALLOON_SWITCH_RIGHT ||
13748 element == EL_BALLOON_SWITCH_UP ||
13749 element == EL_BALLOON_SWITCH_DOWN ||
13750 element == EL_BALLOON_SWITCH_NONE ||
13751 element == EL_BALLOON_SWITCH_ANY)
13753 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
13754 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13755 element == EL_BALLOON_SWITCH_UP ? MV_UP :
13756 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
13757 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
13760 else if (element == EL_LAMP)
13762 Feld[x][y] = EL_LAMP_ACTIVE;
13763 local_player->lights_still_needed--;
13765 ResetGfxAnimation(x, y);
13766 TEST_DrawLevelField(x, y);
13768 else if (element == EL_TIME_ORB_FULL)
13770 Feld[x][y] = EL_TIME_ORB_EMPTY;
13772 if (level.time > 0 || level.use_time_orb_bug)
13774 TimeLeft += level.time_orb_time;
13775 game.no_time_limit = FALSE;
13777 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13779 DisplayGameControlValues();
13782 ResetGfxAnimation(x, y);
13783 TEST_DrawLevelField(x, y);
13785 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13786 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13790 game.ball_state = !game.ball_state;
13792 SCAN_PLAYFIELD(xx, yy)
13794 int e = Feld[xx][yy];
13796 if (game.ball_state)
13798 if (e == EL_EMC_MAGIC_BALL)
13799 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13800 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13801 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13805 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13806 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13807 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13808 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13813 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13814 player->index_bit, dig_side);
13816 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13817 player->index_bit, dig_side);
13819 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13820 player->index_bit, dig_side);
13826 if (!PLAYER_SWITCHING(player, x, y))
13828 player->is_switching = TRUE;
13829 player->switch_x = x;
13830 player->switch_y = y;
13832 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13833 player->index_bit, dig_side);
13834 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13835 player->index_bit, dig_side);
13837 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13838 player->index_bit, dig_side);
13839 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13840 player->index_bit, dig_side);
13843 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13844 player->index_bit, dig_side);
13845 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13846 player->index_bit, dig_side);
13848 return MP_NO_ACTION;
13851 player->push_delay = -1;
13853 if (is_player) /* function can also be called by EL_PENGUIN */
13855 if (Feld[x][y] != element) /* really digged/collected something */
13857 player->is_collecting = !player->is_digging;
13858 player->is_active = TRUE;
13865 static boolean DigFieldByCE(int x, int y, int digging_element)
13867 int element = Feld[x][y];
13869 if (!IS_FREE(x, y))
13871 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13872 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13875 /* no element can dig solid indestructible elements */
13876 if (IS_INDESTRUCTIBLE(element) &&
13877 !IS_DIGGABLE(element) &&
13878 !IS_COLLECTIBLE(element))
13881 if (AmoebaNr[x][y] &&
13882 (element == EL_AMOEBA_FULL ||
13883 element == EL_BD_AMOEBA ||
13884 element == EL_AMOEBA_GROWING))
13886 AmoebaCnt[AmoebaNr[x][y]]--;
13887 AmoebaCnt2[AmoebaNr[x][y]]--;
13890 if (IS_MOVING(x, y))
13891 RemoveMovingField(x, y);
13895 TEST_DrawLevelField(x, y);
13898 /* if digged element was about to explode, prevent the explosion */
13899 ExplodeField[x][y] = EX_TYPE_NONE;
13901 PlayLevelSoundAction(x, y, action);
13904 Store[x][y] = EL_EMPTY;
13906 /* this makes it possible to leave the removed element again */
13907 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13908 Store[x][y] = element;
13913 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13915 int jx = player->jx, jy = player->jy;
13916 int x = jx + dx, y = jy + dy;
13917 int snap_direction = (dx == -1 ? MV_LEFT :
13918 dx == +1 ? MV_RIGHT :
13920 dy == +1 ? MV_DOWN : MV_NONE);
13921 boolean can_continue_snapping = (level.continuous_snapping &&
13922 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13924 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13927 if (!player->active || !IN_LEV_FIELD(x, y))
13935 if (player->MovPos == 0)
13936 player->is_pushing = FALSE;
13938 player->is_snapping = FALSE;
13940 if (player->MovPos == 0)
13942 player->is_moving = FALSE;
13943 player->is_digging = FALSE;
13944 player->is_collecting = FALSE;
13950 /* prevent snapping with already pressed snap key when not allowed */
13951 if (player->is_snapping && !can_continue_snapping)
13954 player->MovDir = snap_direction;
13956 if (player->MovPos == 0)
13958 player->is_moving = FALSE;
13959 player->is_digging = FALSE;
13960 player->is_collecting = FALSE;
13963 player->is_dropping = FALSE;
13964 player->is_dropping_pressed = FALSE;
13965 player->drop_pressed_delay = 0;
13967 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13970 player->is_snapping = TRUE;
13971 player->is_active = TRUE;
13973 if (player->MovPos == 0)
13975 player->is_moving = FALSE;
13976 player->is_digging = FALSE;
13977 player->is_collecting = FALSE;
13980 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
13981 TEST_DrawLevelField(player->last_jx, player->last_jy);
13983 TEST_DrawLevelField(x, y);
13988 static boolean DropElement(struct PlayerInfo *player)
13990 int old_element, new_element;
13991 int dropx = player->jx, dropy = player->jy;
13992 int drop_direction = player->MovDir;
13993 int drop_side = drop_direction;
13994 int drop_element = get_next_dropped_element(player);
13996 player->is_dropping_pressed = TRUE;
13998 /* do not drop an element on top of another element; when holding drop key
13999 pressed without moving, dropped element must move away before the next
14000 element can be dropped (this is especially important if the next element
14001 is dynamite, which can be placed on background for historical reasons) */
14002 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14005 if (IS_THROWABLE(drop_element))
14007 dropx += GET_DX_FROM_DIR(drop_direction);
14008 dropy += GET_DY_FROM_DIR(drop_direction);
14010 if (!IN_LEV_FIELD(dropx, dropy))
14014 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14015 new_element = drop_element; /* default: no change when dropping */
14017 /* check if player is active, not moving and ready to drop */
14018 if (!player->active || player->MovPos || player->drop_delay > 0)
14021 /* check if player has anything that can be dropped */
14022 if (new_element == EL_UNDEFINED)
14025 /* check if drop key was pressed long enough for EM style dynamite */
14026 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14029 /* check if anything can be dropped at the current position */
14030 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14033 /* collected custom elements can only be dropped on empty fields */
14034 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14037 if (old_element != EL_EMPTY)
14038 Back[dropx][dropy] = old_element; /* store old element on this field */
14040 ResetGfxAnimation(dropx, dropy);
14041 ResetRandomAnimationValue(dropx, dropy);
14043 if (player->inventory_size > 0 ||
14044 player->inventory_infinite_element != EL_UNDEFINED)
14046 if (player->inventory_size > 0)
14048 player->inventory_size--;
14050 DrawGameDoorValues();
14052 if (new_element == EL_DYNAMITE)
14053 new_element = EL_DYNAMITE_ACTIVE;
14054 else if (new_element == EL_EM_DYNAMITE)
14055 new_element = EL_EM_DYNAMITE_ACTIVE;
14056 else if (new_element == EL_SP_DISK_RED)
14057 new_element = EL_SP_DISK_RED_ACTIVE;
14060 Feld[dropx][dropy] = new_element;
14062 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14063 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14064 el2img(Feld[dropx][dropy]), 0);
14066 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14068 /* needed if previous element just changed to "empty" in the last frame */
14069 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14071 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14072 player->index_bit, drop_side);
14073 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14075 player->index_bit, drop_side);
14077 TestIfElementTouchesCustomElement(dropx, dropy);
14079 else /* player is dropping a dyna bomb */
14081 player->dynabombs_left--;
14083 Feld[dropx][dropy] = new_element;
14085 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14086 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14087 el2img(Feld[dropx][dropy]), 0);
14089 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14092 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14093 InitField_WithBug1(dropx, dropy, FALSE);
14095 new_element = Feld[dropx][dropy]; /* element might have changed */
14097 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14098 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14100 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14101 MovDir[dropx][dropy] = drop_direction;
14103 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14105 /* do not cause impact style collision by dropping elements that can fall */
14106 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14109 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14110 player->is_dropping = TRUE;
14112 player->drop_pressed_delay = 0;
14113 player->is_dropping_pressed = FALSE;
14115 player->drop_x = dropx;
14116 player->drop_y = dropy;
14121 /* ------------------------------------------------------------------------- */
14122 /* game sound playing functions */
14123 /* ------------------------------------------------------------------------- */
14125 static int *loop_sound_frame = NULL;
14126 static int *loop_sound_volume = NULL;
14128 void InitPlayLevelSound()
14130 int num_sounds = getSoundListSize();
14132 checked_free(loop_sound_frame);
14133 checked_free(loop_sound_volume);
14135 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14136 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14139 static void PlayLevelSound(int x, int y, int nr)
14141 int sx = SCREENX(x), sy = SCREENY(y);
14142 int volume, stereo_position;
14143 int max_distance = 8;
14144 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14146 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14147 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14150 if (!IN_LEV_FIELD(x, y) ||
14151 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14152 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14155 volume = SOUND_MAX_VOLUME;
14157 if (!IN_SCR_FIELD(sx, sy))
14159 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14160 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14162 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14165 stereo_position = (SOUND_MAX_LEFT +
14166 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14167 (SCR_FIELDX + 2 * max_distance));
14169 if (IS_LOOP_SOUND(nr))
14171 /* This assures that quieter loop sounds do not overwrite louder ones,
14172 while restarting sound volume comparison with each new game frame. */
14174 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14177 loop_sound_volume[nr] = volume;
14178 loop_sound_frame[nr] = FrameCounter;
14181 PlaySoundExt(nr, volume, stereo_position, type);
14184 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14186 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14187 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14188 y < LEVELY(BY1) ? LEVELY(BY1) :
14189 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14193 static void PlayLevelSoundAction(int x, int y, int action)
14195 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14198 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14200 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14202 if (sound_effect != SND_UNDEFINED)
14203 PlayLevelSound(x, y, sound_effect);
14206 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14209 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14211 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14212 PlayLevelSound(x, y, sound_effect);
14215 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14217 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14219 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14220 PlayLevelSound(x, y, sound_effect);
14223 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14225 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14227 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14228 StopSound(sound_effect);
14231 static void PlayLevelMusic()
14233 if (levelset.music[level_nr] != MUS_UNDEFINED)
14234 PlayMusic(levelset.music[level_nr]); /* from config file */
14236 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14239 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14241 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14242 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14243 int x = xx - 1 - offset;
14244 int y = yy - 1 - offset;
14249 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14253 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14257 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14261 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14265 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14269 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14273 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14276 case SAMPLE_android_clone:
14277 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14280 case SAMPLE_android_move:
14281 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14284 case SAMPLE_spring:
14285 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14289 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14293 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14296 case SAMPLE_eater_eat:
14297 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14301 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14304 case SAMPLE_collect:
14305 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14308 case SAMPLE_diamond:
14309 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14312 case SAMPLE_squash:
14313 /* !!! CHECK THIS !!! */
14315 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14317 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14321 case SAMPLE_wonderfall:
14322 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14326 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14330 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14334 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14338 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14342 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14346 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14349 case SAMPLE_wonder:
14350 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14354 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14357 case SAMPLE_exit_open:
14358 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14361 case SAMPLE_exit_leave:
14362 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14365 case SAMPLE_dynamite:
14366 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14370 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14374 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14378 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14382 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14386 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14390 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14394 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14399 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14401 int element = map_element_SP_to_RND(element_sp);
14402 int action = map_action_SP_to_RND(action_sp);
14403 int offset = (setup.sp_show_border_elements ? 0 : 1);
14404 int x = xx - offset;
14405 int y = yy - offset;
14407 PlayLevelSoundElementAction(x, y, element, action);
14410 void RaiseScore(int value)
14412 local_player->score += value;
14414 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14416 DisplayGameControlValues();
14419 void RaiseScoreElement(int element)
14424 case EL_BD_DIAMOND:
14425 case EL_EMERALD_YELLOW:
14426 case EL_EMERALD_RED:
14427 case EL_EMERALD_PURPLE:
14428 case EL_SP_INFOTRON:
14429 RaiseScore(level.score[SC_EMERALD]);
14432 RaiseScore(level.score[SC_DIAMOND]);
14435 RaiseScore(level.score[SC_CRYSTAL]);
14438 RaiseScore(level.score[SC_PEARL]);
14441 case EL_BD_BUTTERFLY:
14442 case EL_SP_ELECTRON:
14443 RaiseScore(level.score[SC_BUG]);
14446 case EL_BD_FIREFLY:
14447 case EL_SP_SNIKSNAK:
14448 RaiseScore(level.score[SC_SPACESHIP]);
14451 case EL_DARK_YAMYAM:
14452 RaiseScore(level.score[SC_YAMYAM]);
14455 RaiseScore(level.score[SC_ROBOT]);
14458 RaiseScore(level.score[SC_PACMAN]);
14461 RaiseScore(level.score[SC_NUT]);
14464 case EL_EM_DYNAMITE:
14465 case EL_SP_DISK_RED:
14466 case EL_DYNABOMB_INCREASE_NUMBER:
14467 case EL_DYNABOMB_INCREASE_SIZE:
14468 case EL_DYNABOMB_INCREASE_POWER:
14469 RaiseScore(level.score[SC_DYNAMITE]);
14471 case EL_SHIELD_NORMAL:
14472 case EL_SHIELD_DEADLY:
14473 RaiseScore(level.score[SC_SHIELD]);
14475 case EL_EXTRA_TIME:
14476 RaiseScore(level.extra_time_score);
14490 case EL_DC_KEY_WHITE:
14491 RaiseScore(level.score[SC_KEY]);
14494 RaiseScore(element_info[element].collect_score);
14499 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14501 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14503 /* closing door required in case of envelope style request dialogs */
14505 CloseDoor(DOOR_CLOSE_1);
14507 #if defined(NETWORK_AVALIABLE)
14508 if (options.network)
14509 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14514 FadeSkipNextFadeIn();
14516 SetGameStatus(GAME_MODE_MAIN);
14521 else /* continue playing the game */
14523 if (tape.playing && tape.deactivate_display)
14524 TapeDeactivateDisplayOff(TRUE);
14526 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14528 if (tape.playing && tape.deactivate_display)
14529 TapeDeactivateDisplayOn();
14533 void RequestQuitGame(boolean ask_if_really_quit)
14535 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14536 boolean skip_request = AllPlayersGone || quick_quit;
14538 RequestQuitGameExt(skip_request, quick_quit,
14539 "Do you really want to quit the game?");
14543 /* ------------------------------------------------------------------------- */
14544 /* random generator functions */
14545 /* ------------------------------------------------------------------------- */
14547 unsigned int InitEngineRandom_RND(int seed)
14549 game.num_random_calls = 0;
14551 return InitEngineRandom(seed);
14554 unsigned int RND(int max)
14558 game.num_random_calls++;
14560 return GetEngineRandom(max);
14567 /* ------------------------------------------------------------------------- */
14568 /* game engine snapshot handling functions */
14569 /* ------------------------------------------------------------------------- */
14571 struct EngineSnapshotInfo
14573 /* runtime values for custom element collect score */
14574 int collect_score[NUM_CUSTOM_ELEMENTS];
14576 /* runtime values for group element choice position */
14577 int choice_pos[NUM_GROUP_ELEMENTS];
14579 /* runtime values for belt position animations */
14580 int belt_graphic[4][NUM_BELT_PARTS];
14581 int belt_anim_mode[4][NUM_BELT_PARTS];
14584 static struct EngineSnapshotInfo engine_snapshot_rnd;
14585 static char *snapshot_level_identifier = NULL;
14586 static int snapshot_level_nr = -1;
14588 static void SaveEngineSnapshotValues_RND()
14590 static int belt_base_active_element[4] =
14592 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14593 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14594 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14595 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14599 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14601 int element = EL_CUSTOM_START + i;
14603 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14606 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14608 int element = EL_GROUP_START + i;
14610 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14613 for (i = 0; i < 4; i++)
14615 for (j = 0; j < NUM_BELT_PARTS; j++)
14617 int element = belt_base_active_element[i] + j;
14618 int graphic = el2img(element);
14619 int anim_mode = graphic_info[graphic].anim_mode;
14621 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14622 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14627 static void LoadEngineSnapshotValues_RND()
14629 unsigned int num_random_calls = game.num_random_calls;
14632 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14634 int element = EL_CUSTOM_START + i;
14636 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14639 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14641 int element = EL_GROUP_START + i;
14643 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14646 for (i = 0; i < 4; i++)
14648 for (j = 0; j < NUM_BELT_PARTS; j++)
14650 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14651 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14653 graphic_info[graphic].anim_mode = anim_mode;
14657 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14659 InitRND(tape.random_seed);
14660 for (i = 0; i < num_random_calls; i++)
14664 if (game.num_random_calls != num_random_calls)
14666 Error(ERR_INFO, "number of random calls out of sync");
14667 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14668 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14669 Error(ERR_EXIT, "this should not happen -- please debug");
14673 void FreeEngineSnapshotSingle()
14675 FreeSnapshotSingle();
14677 setString(&snapshot_level_identifier, NULL);
14678 snapshot_level_nr = -1;
14681 void FreeEngineSnapshotList()
14683 FreeSnapshotList();
14686 ListNode *SaveEngineSnapshotBuffers()
14688 ListNode *buffers = NULL;
14690 /* copy some special values to a structure better suited for the snapshot */
14692 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14693 SaveEngineSnapshotValues_RND();
14694 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14695 SaveEngineSnapshotValues_EM();
14696 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14697 SaveEngineSnapshotValues_SP(&buffers);
14699 /* save values stored in special snapshot structure */
14701 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14702 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14703 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14704 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14705 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14706 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14708 /* save further RND engine values */
14710 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14711 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14712 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14714 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14715 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14716 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14717 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14719 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14720 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14721 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14722 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14723 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14725 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14726 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14727 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14729 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14731 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14733 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14734 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14736 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14737 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14738 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14739 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14740 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14741 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14742 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14743 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14744 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14745 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14746 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14747 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14748 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14749 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14750 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14751 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14752 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14753 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14755 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14756 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14758 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14759 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14760 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14762 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14763 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14765 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14766 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14767 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14768 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14769 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14771 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14772 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14775 ListNode *node = engine_snapshot_list_rnd;
14778 while (node != NULL)
14780 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14785 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14791 void SaveEngineSnapshotSingle()
14793 ListNode *buffers = SaveEngineSnapshotBuffers();
14795 /* finally save all snapshot buffers to single snapshot */
14796 SaveSnapshotSingle(buffers);
14798 /* save level identification information */
14799 setString(&snapshot_level_identifier, leveldir_current->identifier);
14800 snapshot_level_nr = level_nr;
14803 boolean CheckSaveEngineSnapshotToList()
14805 boolean save_snapshot =
14806 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14807 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14808 game.snapshot.changed_action) ||
14809 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14810 game.snapshot.collected_item));
14812 game.snapshot.changed_action = FALSE;
14813 game.snapshot.collected_item = FALSE;
14814 game.snapshot.save_snapshot = save_snapshot;
14816 return save_snapshot;
14819 void SaveEngineSnapshotToList()
14821 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14825 ListNode *buffers = SaveEngineSnapshotBuffers();
14827 /* finally save all snapshot buffers to snapshot list */
14828 SaveSnapshotToList(buffers);
14831 void SaveEngineSnapshotToListInitial()
14833 FreeEngineSnapshotList();
14835 SaveEngineSnapshotToList();
14838 void LoadEngineSnapshotValues()
14840 /* restore special values from snapshot structure */
14842 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14843 LoadEngineSnapshotValues_RND();
14844 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14845 LoadEngineSnapshotValues_EM();
14846 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14847 LoadEngineSnapshotValues_SP();
14850 void LoadEngineSnapshotSingle()
14852 LoadSnapshotSingle();
14854 LoadEngineSnapshotValues();
14857 void LoadEngineSnapshot_Undo(int steps)
14859 LoadSnapshotFromList_Older(steps);
14861 LoadEngineSnapshotValues();
14864 void LoadEngineSnapshot_Redo(int steps)
14866 LoadSnapshotFromList_Newer(steps);
14868 LoadEngineSnapshotValues();
14871 boolean CheckEngineSnapshotSingle()
14873 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14874 snapshot_level_nr == level_nr);
14877 boolean CheckEngineSnapshotList()
14879 return CheckSnapshotList();
14883 /* ---------- new game button stuff ---------------------------------------- */
14891 } gamebutton_info[NUM_GAME_BUTTONS] =
14894 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
14895 GAME_CTRL_ID_STOP, "stop game"
14898 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
14899 GAME_CTRL_ID_PAUSE, "pause game"
14902 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
14903 GAME_CTRL_ID_PLAY, "play game"
14906 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
14907 GAME_CTRL_ID_UNDO, "undo step"
14910 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
14911 GAME_CTRL_ID_REDO, "redo step"
14914 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
14915 GAME_CTRL_ID_SAVE, "save game"
14918 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
14919 GAME_CTRL_ID_PAUSE2, "pause game"
14922 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
14923 GAME_CTRL_ID_LOAD, "load game"
14926 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
14927 SOUND_CTRL_ID_MUSIC, "background music on/off"
14930 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
14931 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
14934 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
14935 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
14939 void CreateGameButtons()
14943 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14945 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14946 struct XY *pos = gamebutton_info[i].pos;
14947 struct GadgetInfo *gi;
14950 unsigned int event_mask;
14951 int base_x = (tape.show_game_buttons ? VX : DX);
14952 int base_y = (tape.show_game_buttons ? VY : DY);
14953 int gd_x = gfx->src_x;
14954 int gd_y = gfx->src_y;
14955 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
14956 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
14957 int gd_xa = gfx->src_x + gfx->active_xoffset;
14958 int gd_ya = gfx->src_y + gfx->active_yoffset;
14959 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14960 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14963 if (gfx->bitmap == NULL)
14965 game_gadget[id] = NULL;
14970 if (id == GAME_CTRL_ID_STOP ||
14971 id == GAME_CTRL_ID_PLAY ||
14972 id == GAME_CTRL_ID_SAVE ||
14973 id == GAME_CTRL_ID_LOAD)
14975 button_type = GD_TYPE_NORMAL_BUTTON;
14977 event_mask = GD_EVENT_RELEASED;
14979 else if (id == GAME_CTRL_ID_UNDO ||
14980 id == GAME_CTRL_ID_REDO)
14982 button_type = GD_TYPE_NORMAL_BUTTON;
14984 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14988 button_type = GD_TYPE_CHECK_BUTTON;
14990 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14991 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14992 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14993 event_mask = GD_EVENT_PRESSED;
14996 gi = CreateGadget(GDI_CUSTOM_ID, id,
14997 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14998 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14999 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15000 GDI_WIDTH, gfx->width,
15001 GDI_HEIGHT, gfx->height,
15002 GDI_TYPE, button_type,
15003 GDI_STATE, GD_BUTTON_UNPRESSED,
15004 GDI_CHECKED, checked,
15005 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15006 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15007 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15008 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15009 GDI_DIRECT_DRAW, FALSE,
15010 GDI_EVENT_MASK, event_mask,
15011 GDI_CALLBACK_ACTION, HandleGameButtons,
15015 Error(ERR_EXIT, "cannot create gadget");
15017 game_gadget[id] = gi;
15021 void FreeGameButtons()
15025 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15026 FreeGadget(game_gadget[i]);
15029 static void UnmapGameButtonsAtSamePosition(int id)
15033 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15035 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15036 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15037 UnmapGadget(game_gadget[i]);
15040 static void UnmapGameButtonsAtSamePosition_All()
15042 if (setup.show_snapshot_buttons)
15044 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15045 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15046 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15050 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15051 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15052 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15056 static void MapGameButtonsAtSamePosition(int id)
15060 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15062 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15063 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15064 MapGadget(game_gadget[i]);
15066 UnmapGameButtonsAtSamePosition_All();
15069 void MapUndoRedoButtons()
15071 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15072 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15074 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15075 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15077 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15080 void UnmapUndoRedoButtons()
15082 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15083 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15085 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15086 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15088 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15091 void MapGameButtons()
15095 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15096 if (i != GAME_CTRL_ID_UNDO &&
15097 i != GAME_CTRL_ID_REDO)
15098 MapGadget(game_gadget[i]);
15100 UnmapGameButtonsAtSamePosition_All();
15102 RedrawGameButtons();
15105 void UnmapGameButtons()
15109 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15110 UnmapGadget(game_gadget[i]);
15113 void RedrawGameButtons()
15117 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15118 RedrawGadget(game_gadget[i]);
15120 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15121 redraw_mask &= ~REDRAW_ALL;
15124 void GameUndoRedoExt()
15126 ClearPlayerAction();
15128 tape.pausing = TRUE;
15131 UpdateAndDisplayGameControlValues();
15133 DrawCompleteVideoDisplay();
15134 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15135 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15136 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15141 void GameUndo(int steps)
15143 if (!CheckEngineSnapshotList())
15146 LoadEngineSnapshot_Undo(steps);
15151 void GameRedo(int steps)
15153 if (!CheckEngineSnapshotList())
15156 LoadEngineSnapshot_Redo(steps);
15161 static void HandleGameButtonsExt(int id, int button)
15163 static boolean game_undo_executed = FALSE;
15164 int steps = BUTTON_STEPSIZE(button);
15165 boolean handle_game_buttons =
15166 (game_status == GAME_MODE_PLAYING ||
15167 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15169 if (!handle_game_buttons)
15174 case GAME_CTRL_ID_STOP:
15175 if (game_status == GAME_MODE_MAIN)
15181 RequestQuitGame(TRUE);
15185 case GAME_CTRL_ID_PAUSE:
15186 case GAME_CTRL_ID_PAUSE2:
15187 if (options.network && game_status == GAME_MODE_PLAYING)
15189 #if defined(NETWORK_AVALIABLE)
15191 SendToServer_ContinuePlaying();
15193 SendToServer_PausePlaying();
15197 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15199 game_undo_executed = FALSE;
15203 case GAME_CTRL_ID_PLAY:
15204 if (game_status == GAME_MODE_MAIN)
15206 StartGameActions(options.network, setup.autorecord, level.random_seed);
15208 else if (tape.pausing)
15210 #if defined(NETWORK_AVALIABLE)
15211 if (options.network)
15212 SendToServer_ContinuePlaying();
15215 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15219 case GAME_CTRL_ID_UNDO:
15220 // Important: When using "save snapshot when collecting an item" mode,
15221 // load last (current) snapshot for first "undo" after pressing "pause"
15222 // (else the last-but-one snapshot would be loaded, because the snapshot
15223 // pointer already points to the last snapshot when pressing "pause",
15224 // which is fine for "every step/move" mode, but not for "every collect")
15225 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15226 !game_undo_executed)
15229 game_undo_executed = TRUE;
15234 case GAME_CTRL_ID_REDO:
15238 case GAME_CTRL_ID_SAVE:
15242 case GAME_CTRL_ID_LOAD:
15246 case SOUND_CTRL_ID_MUSIC:
15247 if (setup.sound_music)
15249 setup.sound_music = FALSE;
15253 else if (audio.music_available)
15255 setup.sound = setup.sound_music = TRUE;
15257 SetAudioMode(setup.sound);
15263 case SOUND_CTRL_ID_LOOPS:
15264 if (setup.sound_loops)
15265 setup.sound_loops = FALSE;
15266 else if (audio.loops_available)
15268 setup.sound = setup.sound_loops = TRUE;
15270 SetAudioMode(setup.sound);
15274 case SOUND_CTRL_ID_SIMPLE:
15275 if (setup.sound_simple)
15276 setup.sound_simple = FALSE;
15277 else if (audio.sound_available)
15279 setup.sound = setup.sound_simple = TRUE;
15281 SetAudioMode(setup.sound);
15290 static void HandleGameButtons(struct GadgetInfo *gi)
15292 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15295 void HandleSoundButtonKeys(Key key)
15298 if (key == setup.shortcut.sound_simple)
15299 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15300 else if (key == setup.shortcut.sound_loops)
15301 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15302 else if (key == setup.shortcut.sound_music)
15303 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);