1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_TIME_ANIM 35
126 #define GAME_PANEL_HEALTH 36
127 #define GAME_PANEL_HEALTH_ANIM 37
128 #define GAME_PANEL_FRAME 38
129 #define GAME_PANEL_SHIELD_NORMAL 39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 40
131 #define GAME_PANEL_SHIELD_DEADLY 41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 42
133 #define GAME_PANEL_EXIT 43
134 #define GAME_PANEL_EMC_MAGIC_BALL 44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45
136 #define GAME_PANEL_LIGHT_SWITCH 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 47
138 #define GAME_PANEL_TIMEGATE_SWITCH 48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 49
140 #define GAME_PANEL_SWITCHGATE_SWITCH 50
141 #define GAME_PANEL_EMC_LENSES 51
142 #define GAME_PANEL_EMC_LENSES_TIME 52
143 #define GAME_PANEL_EMC_MAGNIFIER 53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 54
145 #define GAME_PANEL_BALLOON_SWITCH 55
146 #define GAME_PANEL_DYNABOMB_NUMBER 56
147 #define GAME_PANEL_DYNABOMB_SIZE 57
148 #define GAME_PANEL_DYNABOMB_POWER 58
149 #define GAME_PANEL_PENGUINS 59
150 #define GAME_PANEL_SOKOBAN_OBJECTS 60
151 #define GAME_PANEL_SOKOBAN_FIELDS 61
152 #define GAME_PANEL_ROBOT_WHEEL 62
153 #define GAME_PANEL_CONVEYOR_BELT_1 63
154 #define GAME_PANEL_CONVEYOR_BELT_2 64
155 #define GAME_PANEL_CONVEYOR_BELT_3 65
156 #define GAME_PANEL_CONVEYOR_BELT_4 66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70
161 #define GAME_PANEL_MAGIC_WALL 71
162 #define GAME_PANEL_MAGIC_WALL_TIME 72
163 #define GAME_PANEL_GRAVITY_STATE 73
164 #define GAME_PANEL_GRAPHIC_1 74
165 #define GAME_PANEL_GRAPHIC_2 75
166 #define GAME_PANEL_GRAPHIC_3 76
167 #define GAME_PANEL_GRAPHIC_4 77
168 #define GAME_PANEL_GRAPHIC_5 78
169 #define GAME_PANEL_GRAPHIC_6 79
170 #define GAME_PANEL_GRAPHIC_7 80
171 #define GAME_PANEL_GRAPHIC_8 81
172 #define GAME_PANEL_ELEMENT_1 82
173 #define GAME_PANEL_ELEMENT_2 83
174 #define GAME_PANEL_ELEMENT_3 84
175 #define GAME_PANEL_ELEMENT_4 85
176 #define GAME_PANEL_ELEMENT_5 86
177 #define GAME_PANEL_ELEMENT_6 87
178 #define GAME_PANEL_ELEMENT_7 88
179 #define GAME_PANEL_ELEMENT_8 89
180 #define GAME_PANEL_ELEMENT_COUNT_1 90
181 #define GAME_PANEL_ELEMENT_COUNT_2 91
182 #define GAME_PANEL_ELEMENT_COUNT_3 92
183 #define GAME_PANEL_ELEMENT_COUNT_4 93
184 #define GAME_PANEL_ELEMENT_COUNT_5 94
185 #define GAME_PANEL_ELEMENT_COUNT_6 95
186 #define GAME_PANEL_ELEMENT_COUNT_7 96
187 #define GAME_PANEL_ELEMENT_COUNT_8 97
188 #define GAME_PANEL_CE_SCORE_1 98
189 #define GAME_PANEL_CE_SCORE_2 99
190 #define GAME_PANEL_CE_SCORE_3 100
191 #define GAME_PANEL_CE_SCORE_4 101
192 #define GAME_PANEL_CE_SCORE_5 102
193 #define GAME_PANEL_CE_SCORE_6 103
194 #define GAME_PANEL_CE_SCORE_7 104
195 #define GAME_PANEL_CE_SCORE_8 105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 113
204 #define GAME_PANEL_PLAYER_NAME 114
205 #define GAME_PANEL_LEVEL_NAME 115
206 #define GAME_PANEL_LEVEL_AUTHOR 116
208 #define NUM_GAME_PANEL_CONTROLS 117
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int graphic, graphic_active;
227 int value, last_value;
228 int frame, last_frame;
233 static struct GamePanelControlInfo game_panel_controls[] =
236 GAME_PANEL_LEVEL_NUMBER,
237 &game.panel.level_number,
246 GAME_PANEL_INVENTORY_COUNT,
247 &game.panel.inventory_count,
251 GAME_PANEL_INVENTORY_FIRST_1,
252 &game.panel.inventory_first[0],
256 GAME_PANEL_INVENTORY_FIRST_2,
257 &game.panel.inventory_first[1],
261 GAME_PANEL_INVENTORY_FIRST_3,
262 &game.panel.inventory_first[2],
266 GAME_PANEL_INVENTORY_FIRST_4,
267 &game.panel.inventory_first[3],
271 GAME_PANEL_INVENTORY_FIRST_5,
272 &game.panel.inventory_first[4],
276 GAME_PANEL_INVENTORY_FIRST_6,
277 &game.panel.inventory_first[5],
281 GAME_PANEL_INVENTORY_FIRST_7,
282 &game.panel.inventory_first[6],
286 GAME_PANEL_INVENTORY_FIRST_8,
287 &game.panel.inventory_first[7],
291 GAME_PANEL_INVENTORY_LAST_1,
292 &game.panel.inventory_last[0],
296 GAME_PANEL_INVENTORY_LAST_2,
297 &game.panel.inventory_last[1],
301 GAME_PANEL_INVENTORY_LAST_3,
302 &game.panel.inventory_last[2],
306 GAME_PANEL_INVENTORY_LAST_4,
307 &game.panel.inventory_last[3],
311 GAME_PANEL_INVENTORY_LAST_5,
312 &game.panel.inventory_last[4],
316 GAME_PANEL_INVENTORY_LAST_6,
317 &game.panel.inventory_last[5],
321 GAME_PANEL_INVENTORY_LAST_7,
322 &game.panel.inventory_last[6],
326 GAME_PANEL_INVENTORY_LAST_8,
327 &game.panel.inventory_last[7],
371 GAME_PANEL_KEY_WHITE,
372 &game.panel.key_white,
376 GAME_PANEL_KEY_WHITE_COUNT,
377 &game.panel.key_white_count,
386 GAME_PANEL_HIGHSCORE,
387 &game.panel.highscore,
411 GAME_PANEL_TIME_ANIM,
412 &game.panel.time_anim,
415 IMG_GFX_GAME_PANEL_TIME_ANIM,
416 IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
424 GAME_PANEL_HEALTH_ANIM,
425 &game.panel.health_anim,
428 IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429 IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
437 GAME_PANEL_SHIELD_NORMAL,
438 &game.panel.shield_normal,
442 GAME_PANEL_SHIELD_NORMAL_TIME,
443 &game.panel.shield_normal_time,
447 GAME_PANEL_SHIELD_DEADLY,
448 &game.panel.shield_deadly,
452 GAME_PANEL_SHIELD_DEADLY_TIME,
453 &game.panel.shield_deadly_time,
462 GAME_PANEL_EMC_MAGIC_BALL,
463 &game.panel.emc_magic_ball,
467 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468 &game.panel.emc_magic_ball_switch,
472 GAME_PANEL_LIGHT_SWITCH,
473 &game.panel.light_switch,
477 GAME_PANEL_LIGHT_SWITCH_TIME,
478 &game.panel.light_switch_time,
482 GAME_PANEL_TIMEGATE_SWITCH,
483 &game.panel.timegate_switch,
487 GAME_PANEL_TIMEGATE_SWITCH_TIME,
488 &game.panel.timegate_switch_time,
492 GAME_PANEL_SWITCHGATE_SWITCH,
493 &game.panel.switchgate_switch,
497 GAME_PANEL_EMC_LENSES,
498 &game.panel.emc_lenses,
502 GAME_PANEL_EMC_LENSES_TIME,
503 &game.panel.emc_lenses_time,
507 GAME_PANEL_EMC_MAGNIFIER,
508 &game.panel.emc_magnifier,
512 GAME_PANEL_EMC_MAGNIFIER_TIME,
513 &game.panel.emc_magnifier_time,
517 GAME_PANEL_BALLOON_SWITCH,
518 &game.panel.balloon_switch,
522 GAME_PANEL_DYNABOMB_NUMBER,
523 &game.panel.dynabomb_number,
527 GAME_PANEL_DYNABOMB_SIZE,
528 &game.panel.dynabomb_size,
532 GAME_PANEL_DYNABOMB_POWER,
533 &game.panel.dynabomb_power,
538 &game.panel.penguins,
542 GAME_PANEL_SOKOBAN_OBJECTS,
543 &game.panel.sokoban_objects,
547 GAME_PANEL_SOKOBAN_FIELDS,
548 &game.panel.sokoban_fields,
552 GAME_PANEL_ROBOT_WHEEL,
553 &game.panel.robot_wheel,
557 GAME_PANEL_CONVEYOR_BELT_1,
558 &game.panel.conveyor_belt[0],
562 GAME_PANEL_CONVEYOR_BELT_2,
563 &game.panel.conveyor_belt[1],
567 GAME_PANEL_CONVEYOR_BELT_3,
568 &game.panel.conveyor_belt[2],
572 GAME_PANEL_CONVEYOR_BELT_4,
573 &game.panel.conveyor_belt[3],
577 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578 &game.panel.conveyor_belt_switch[0],
582 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583 &game.panel.conveyor_belt_switch[1],
587 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588 &game.panel.conveyor_belt_switch[2],
592 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593 &game.panel.conveyor_belt_switch[3],
597 GAME_PANEL_MAGIC_WALL,
598 &game.panel.magic_wall,
602 GAME_PANEL_MAGIC_WALL_TIME,
603 &game.panel.magic_wall_time,
607 GAME_PANEL_GRAVITY_STATE,
608 &game.panel.gravity_state,
612 GAME_PANEL_GRAPHIC_1,
613 &game.panel.graphic[0],
617 GAME_PANEL_GRAPHIC_2,
618 &game.panel.graphic[1],
622 GAME_PANEL_GRAPHIC_3,
623 &game.panel.graphic[2],
627 GAME_PANEL_GRAPHIC_4,
628 &game.panel.graphic[3],
632 GAME_PANEL_GRAPHIC_5,
633 &game.panel.graphic[4],
637 GAME_PANEL_GRAPHIC_6,
638 &game.panel.graphic[5],
642 GAME_PANEL_GRAPHIC_7,
643 &game.panel.graphic[6],
647 GAME_PANEL_GRAPHIC_8,
648 &game.panel.graphic[7],
652 GAME_PANEL_ELEMENT_1,
653 &game.panel.element[0],
657 GAME_PANEL_ELEMENT_2,
658 &game.panel.element[1],
662 GAME_PANEL_ELEMENT_3,
663 &game.panel.element[2],
667 GAME_PANEL_ELEMENT_4,
668 &game.panel.element[3],
672 GAME_PANEL_ELEMENT_5,
673 &game.panel.element[4],
677 GAME_PANEL_ELEMENT_6,
678 &game.panel.element[5],
682 GAME_PANEL_ELEMENT_7,
683 &game.panel.element[6],
687 GAME_PANEL_ELEMENT_8,
688 &game.panel.element[7],
692 GAME_PANEL_ELEMENT_COUNT_1,
693 &game.panel.element_count[0],
697 GAME_PANEL_ELEMENT_COUNT_2,
698 &game.panel.element_count[1],
702 GAME_PANEL_ELEMENT_COUNT_3,
703 &game.panel.element_count[2],
707 GAME_PANEL_ELEMENT_COUNT_4,
708 &game.panel.element_count[3],
712 GAME_PANEL_ELEMENT_COUNT_5,
713 &game.panel.element_count[4],
717 GAME_PANEL_ELEMENT_COUNT_6,
718 &game.panel.element_count[5],
722 GAME_PANEL_ELEMENT_COUNT_7,
723 &game.panel.element_count[6],
727 GAME_PANEL_ELEMENT_COUNT_8,
728 &game.panel.element_count[7],
732 GAME_PANEL_CE_SCORE_1,
733 &game.panel.ce_score[0],
737 GAME_PANEL_CE_SCORE_2,
738 &game.panel.ce_score[1],
742 GAME_PANEL_CE_SCORE_3,
743 &game.panel.ce_score[2],
747 GAME_PANEL_CE_SCORE_4,
748 &game.panel.ce_score[3],
752 GAME_PANEL_CE_SCORE_5,
753 &game.panel.ce_score[4],
757 GAME_PANEL_CE_SCORE_6,
758 &game.panel.ce_score[5],
762 GAME_PANEL_CE_SCORE_7,
763 &game.panel.ce_score[6],
767 GAME_PANEL_CE_SCORE_8,
768 &game.panel.ce_score[7],
772 GAME_PANEL_CE_SCORE_1_ELEMENT,
773 &game.panel.ce_score_element[0],
777 GAME_PANEL_CE_SCORE_2_ELEMENT,
778 &game.panel.ce_score_element[1],
782 GAME_PANEL_CE_SCORE_3_ELEMENT,
783 &game.panel.ce_score_element[2],
787 GAME_PANEL_CE_SCORE_4_ELEMENT,
788 &game.panel.ce_score_element[3],
792 GAME_PANEL_CE_SCORE_5_ELEMENT,
793 &game.panel.ce_score_element[4],
797 GAME_PANEL_CE_SCORE_6_ELEMENT,
798 &game.panel.ce_score_element[5],
802 GAME_PANEL_CE_SCORE_7_ELEMENT,
803 &game.panel.ce_score_element[6],
807 GAME_PANEL_CE_SCORE_8_ELEMENT,
808 &game.panel.ce_score_element[7],
812 GAME_PANEL_PLAYER_NAME,
813 &game.panel.player_name,
817 GAME_PANEL_LEVEL_NAME,
818 &game.panel.level_name,
822 GAME_PANEL_LEVEL_AUTHOR,
823 &game.panel.level_author,
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING 3
836 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION 2
838 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF -1
842 #define INITIAL_MOVE_DELAY_ON 0
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED 32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED 4
848 #define MOVE_DELAY_MAX_SPEED 1
850 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
853 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
858 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
860 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define GAME_CTRL_ID_UNDO 3
1010 #define GAME_CTRL_ID_REDO 4
1011 #define GAME_CTRL_ID_SAVE 5
1012 #define GAME_CTRL_ID_PAUSE2 6
1013 #define GAME_CTRL_ID_LOAD 7
1014 #define SOUND_CTRL_ID_MUSIC 8
1015 #define SOUND_CTRL_ID_LOOPS 9
1016 #define SOUND_CTRL_ID_SIMPLE 10
1018 #define NUM_GAME_BUTTONS 11
1021 /* forward declaration for internal use */
1023 static void CreateField(int, int, int);
1025 static void ResetGfxAnimation(int, int);
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1051 static void HandleElementChange(int, int, int);
1052 static void ExecuteCustomElementAction(int, int, int, int);
1053 static boolean ChangeElement(int, int, int, int);
1055 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1056 #define CheckTriggeredElementChange(x, y, e, ev) \
1057 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1058 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1059 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1060 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1061 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1062 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1063 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1065 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1066 #define CheckElementChange(x, y, e, te, ev) \
1067 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1068 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1069 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1070 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1071 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1073 static void PlayLevelSound(int, int, int);
1074 static void PlayLevelSoundNearest(int, int, int);
1075 static void PlayLevelSoundAction(int, int, int);
1076 static void PlayLevelSoundElementAction(int, int, int, int);
1077 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1078 static void PlayLevelSoundActionIfLoop(int, int, int);
1079 static void StopLevelSoundActionIfLoop(int, int, int);
1080 static void PlayLevelMusic();
1081 static void FadeLevelSoundsAndMusic();
1083 static void HandleGameButtons(struct GadgetInfo *);
1085 int AmoebeNachbarNr(int, int);
1086 void AmoebeUmwandeln(int, int);
1087 void ContinueMoving(int, int);
1088 void Bang(int, int);
1089 void InitMovDir(int, int);
1090 void InitAmoebaNr(int, int);
1091 int NewHiScore(void);
1093 void TestIfGoodThingHitsBadThing(int, int, int);
1094 void TestIfBadThingHitsGoodThing(int, int, int);
1095 void TestIfPlayerTouchesBadThing(int, int);
1096 void TestIfPlayerRunsIntoBadThing(int, int, int);
1097 void TestIfBadThingTouchesPlayer(int, int);
1098 void TestIfBadThingRunsIntoPlayer(int, int, int);
1099 void TestIfFriendTouchesBadThing(int, int);
1100 void TestIfBadThingTouchesFriend(int, int);
1101 void TestIfBadThingTouchesOtherBadThing(int, int);
1102 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1108 static int getInvisibleActiveFromInvisibleElement(int);
1109 static int getInvisibleFromInvisibleActiveElement(int);
1111 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1113 /* for detection of endless loops, caused by custom element programming */
1114 /* (using maximal playfield width x 10 is just a rough approximation) */
1115 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1117 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1119 if (recursion_loop_detected) \
1122 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1124 recursion_loop_detected = TRUE; \
1125 recursion_loop_element = (e); \
1128 recursion_loop_depth++; \
1131 #define RECURSION_LOOP_DETECTION_END() \
1133 recursion_loop_depth--; \
1136 static int recursion_loop_depth;
1137 static boolean recursion_loop_detected;
1138 static boolean recursion_loop_element;
1140 static int map_player_action[MAX_PLAYERS];
1143 /* ------------------------------------------------------------------------- */
1144 /* definition of elements that automatically change to other elements after */
1145 /* a specified time, eventually calling a function when changing */
1146 /* ------------------------------------------------------------------------- */
1148 /* forward declaration for changer functions */
1149 static void InitBuggyBase(int, int);
1150 static void WarnBuggyBase(int, int);
1152 static void InitTrap(int, int);
1153 static void ActivateTrap(int, int);
1154 static void ChangeActiveTrap(int, int);
1156 static void InitRobotWheel(int, int);
1157 static void RunRobotWheel(int, int);
1158 static void StopRobotWheel(int, int);
1160 static void InitTimegateWheel(int, int);
1161 static void RunTimegateWheel(int, int);
1163 static void InitMagicBallDelay(int, int);
1164 static void ActivateMagicBall(int, int);
1166 struct ChangingElementInfo
1171 void (*pre_change_function)(int x, int y);
1172 void (*change_function)(int x, int y);
1173 void (*post_change_function)(int x, int y);
1176 static struct ChangingElementInfo change_delay_list[] =
1211 EL_STEEL_EXIT_OPENING,
1219 EL_STEEL_EXIT_CLOSING,
1220 EL_STEEL_EXIT_CLOSED,
1243 EL_EM_STEEL_EXIT_OPENING,
1244 EL_EM_STEEL_EXIT_OPEN,
1251 EL_EM_STEEL_EXIT_CLOSING,
1275 EL_SWITCHGATE_OPENING,
1283 EL_SWITCHGATE_CLOSING,
1284 EL_SWITCHGATE_CLOSED,
1291 EL_TIMEGATE_OPENING,
1299 EL_TIMEGATE_CLOSING,
1308 EL_ACID_SPLASH_LEFT,
1316 EL_ACID_SPLASH_RIGHT,
1325 EL_SP_BUGGY_BASE_ACTIVATING,
1332 EL_SP_BUGGY_BASE_ACTIVATING,
1333 EL_SP_BUGGY_BASE_ACTIVE,
1340 EL_SP_BUGGY_BASE_ACTIVE,
1364 EL_ROBOT_WHEEL_ACTIVE,
1372 EL_TIMEGATE_SWITCH_ACTIVE,
1380 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1381 EL_DC_TIMEGATE_SWITCH,
1388 EL_EMC_MAGIC_BALL_ACTIVE,
1389 EL_EMC_MAGIC_BALL_ACTIVE,
1396 EL_EMC_SPRING_BUMPER_ACTIVE,
1397 EL_EMC_SPRING_BUMPER,
1404 EL_DIAGONAL_SHRINKING,
1412 EL_DIAGONAL_GROWING,
1433 int push_delay_fixed, push_delay_random;
1437 { EL_SPRING, 0, 0 },
1438 { EL_BALLOON, 0, 0 },
1440 { EL_SOKOBAN_OBJECT, 2, 0 },
1441 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1442 { EL_SATELLITE, 2, 0 },
1443 { EL_SP_DISK_YELLOW, 2, 0 },
1445 { EL_UNDEFINED, 0, 0 },
1453 move_stepsize_list[] =
1455 { EL_AMOEBA_DROP, 2 },
1456 { EL_AMOEBA_DROPPING, 2 },
1457 { EL_QUICKSAND_FILLING, 1 },
1458 { EL_QUICKSAND_EMPTYING, 1 },
1459 { EL_QUICKSAND_FAST_FILLING, 2 },
1460 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1461 { EL_MAGIC_WALL_FILLING, 2 },
1462 { EL_MAGIC_WALL_EMPTYING, 2 },
1463 { EL_BD_MAGIC_WALL_FILLING, 2 },
1464 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1465 { EL_DC_MAGIC_WALL_FILLING, 2 },
1466 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1468 { EL_UNDEFINED, 0 },
1476 collect_count_list[] =
1479 { EL_BD_DIAMOND, 1 },
1480 { EL_EMERALD_YELLOW, 1 },
1481 { EL_EMERALD_RED, 1 },
1482 { EL_EMERALD_PURPLE, 1 },
1484 { EL_SP_INFOTRON, 1 },
1488 { EL_UNDEFINED, 0 },
1496 access_direction_list[] =
1498 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1499 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1500 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1501 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1502 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1503 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1504 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1505 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1506 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1507 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1508 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1510 { EL_SP_PORT_LEFT, MV_RIGHT },
1511 { EL_SP_PORT_RIGHT, MV_LEFT },
1512 { EL_SP_PORT_UP, MV_DOWN },
1513 { EL_SP_PORT_DOWN, MV_UP },
1514 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1515 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1516 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1517 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1518 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1519 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1520 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1521 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1522 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1523 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1524 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1525 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1530 { EL_UNDEFINED, MV_NONE }
1533 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1535 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1536 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1537 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1538 IS_JUST_CHANGING(x, y))
1540 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1542 /* static variables for playfield scan mode (scanning forward or backward) */
1543 static int playfield_scan_start_x = 0;
1544 static int playfield_scan_start_y = 0;
1545 static int playfield_scan_delta_x = 1;
1546 static int playfield_scan_delta_y = 1;
1548 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1549 (y) >= 0 && (y) <= lev_fieldy - 1; \
1550 (y) += playfield_scan_delta_y) \
1551 for ((x) = playfield_scan_start_x; \
1552 (x) >= 0 && (x) <= lev_fieldx - 1; \
1553 (x) += playfield_scan_delta_x)
1556 void DEBUG_SetMaximumDynamite()
1560 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1561 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1562 local_player->inventory_element[local_player->inventory_size++] =
1567 static void InitPlayfieldScanModeVars()
1569 if (game.use_reverse_scan_direction)
1571 playfield_scan_start_x = lev_fieldx - 1;
1572 playfield_scan_start_y = lev_fieldy - 1;
1574 playfield_scan_delta_x = -1;
1575 playfield_scan_delta_y = -1;
1579 playfield_scan_start_x = 0;
1580 playfield_scan_start_y = 0;
1582 playfield_scan_delta_x = 1;
1583 playfield_scan_delta_y = 1;
1587 static void InitPlayfieldScanMode(int mode)
1589 game.use_reverse_scan_direction =
1590 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1592 InitPlayfieldScanModeVars();
1595 static int get_move_delay_from_stepsize(int move_stepsize)
1598 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1600 /* make sure that stepsize value is always a power of 2 */
1601 move_stepsize = (1 << log_2(move_stepsize));
1603 return TILEX / move_stepsize;
1606 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1609 int player_nr = player->index_nr;
1610 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1611 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1613 /* do no immediately change move delay -- the player might just be moving */
1614 player->move_delay_value_next = move_delay;
1616 /* information if player can move must be set separately */
1617 player->cannot_move = cannot_move;
1621 player->move_delay = game.initial_move_delay[player_nr];
1622 player->move_delay_value = game.initial_move_delay_value[player_nr];
1624 player->move_delay_value_next = -1;
1626 player->move_delay_reset_counter = 0;
1630 void GetPlayerConfig()
1632 GameFrameDelay = setup.game_frame_delay;
1634 if (!audio.sound_available)
1635 setup.sound_simple = FALSE;
1637 if (!audio.loops_available)
1638 setup.sound_loops = FALSE;
1640 if (!audio.music_available)
1641 setup.sound_music = FALSE;
1643 if (!video.fullscreen_available)
1644 setup.fullscreen = FALSE;
1646 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1648 SetAudioMode(setup.sound);
1651 int GetElementFromGroupElement(int element)
1653 if (IS_GROUP_ELEMENT(element))
1655 struct ElementGroupInfo *group = element_info[element].group;
1656 int last_anim_random_frame = gfx.anim_random_frame;
1659 if (group->choice_mode == ANIM_RANDOM)
1660 gfx.anim_random_frame = RND(group->num_elements_resolved);
1662 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1663 group->choice_mode, 0,
1666 if (group->choice_mode == ANIM_RANDOM)
1667 gfx.anim_random_frame = last_anim_random_frame;
1669 group->choice_pos++;
1671 element = group->element_resolved[element_pos];
1677 static void InitPlayerField(int x, int y, int element, boolean init_game)
1679 if (element == EL_SP_MURPHY)
1683 if (stored_player[0].present)
1685 Feld[x][y] = EL_SP_MURPHY_CLONE;
1691 stored_player[0].initial_element = element;
1692 stored_player[0].use_murphy = TRUE;
1694 if (!level.use_artwork_element[0])
1695 stored_player[0].artwork_element = EL_SP_MURPHY;
1698 Feld[x][y] = EL_PLAYER_1;
1704 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1705 int jx = player->jx, jy = player->jy;
1707 player->present = TRUE;
1709 player->block_last_field = (element == EL_SP_MURPHY ?
1710 level.sp_block_last_field :
1711 level.block_last_field);
1713 /* ---------- initialize player's last field block delay --------------- */
1715 /* always start with reliable default value (no adjustment needed) */
1716 player->block_delay_adjustment = 0;
1718 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1719 if (player->block_last_field && element == EL_SP_MURPHY)
1720 player->block_delay_adjustment = 1;
1722 /* special case 2: in game engines before 3.1.1, blocking was different */
1723 if (game.use_block_last_field_bug)
1724 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1726 if (!options.network || player->connected)
1728 player->active = TRUE;
1730 /* remove potentially duplicate players */
1731 if (StorePlayer[jx][jy] == Feld[x][y])
1732 StorePlayer[jx][jy] = 0;
1734 StorePlayer[x][y] = Feld[x][y];
1736 #if DEBUG_INIT_PLAYER
1739 printf("- player element %d activated", player->element_nr);
1740 printf(" (local player is %d and currently %s)\n",
1741 local_player->element_nr,
1742 local_player->active ? "active" : "not active");
1747 Feld[x][y] = EL_EMPTY;
1749 player->jx = player->last_jx = x;
1750 player->jy = player->last_jy = y;
1755 int player_nr = GET_PLAYER_NR(element);
1756 struct PlayerInfo *player = &stored_player[player_nr];
1758 if (player->active && player->killed)
1759 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1763 static void InitField(int x, int y, boolean init_game)
1765 int element = Feld[x][y];
1774 InitPlayerField(x, y, element, init_game);
1777 case EL_SOKOBAN_FIELD_PLAYER:
1778 element = Feld[x][y] = EL_PLAYER_1;
1779 InitField(x, y, init_game);
1781 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1782 InitField(x, y, init_game);
1785 case EL_SOKOBAN_FIELD_EMPTY:
1786 local_player->sokobanfields_still_needed++;
1790 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1791 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1792 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1793 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1794 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1795 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1796 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1797 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1798 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1799 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808 case EL_SPACESHIP_RIGHT:
1809 case EL_SPACESHIP_UP:
1810 case EL_SPACESHIP_LEFT:
1811 case EL_SPACESHIP_DOWN:
1812 case EL_BD_BUTTERFLY:
1813 case EL_BD_BUTTERFLY_RIGHT:
1814 case EL_BD_BUTTERFLY_UP:
1815 case EL_BD_BUTTERFLY_LEFT:
1816 case EL_BD_BUTTERFLY_DOWN:
1818 case EL_BD_FIREFLY_RIGHT:
1819 case EL_BD_FIREFLY_UP:
1820 case EL_BD_FIREFLY_LEFT:
1821 case EL_BD_FIREFLY_DOWN:
1822 case EL_PACMAN_RIGHT:
1824 case EL_PACMAN_LEFT:
1825 case EL_PACMAN_DOWN:
1827 case EL_YAMYAM_LEFT:
1828 case EL_YAMYAM_RIGHT:
1830 case EL_YAMYAM_DOWN:
1831 case EL_DARK_YAMYAM:
1834 case EL_SP_SNIKSNAK:
1835 case EL_SP_ELECTRON:
1844 case EL_AMOEBA_FULL:
1849 case EL_AMOEBA_DROP:
1850 if (y == lev_fieldy - 1)
1852 Feld[x][y] = EL_AMOEBA_GROWING;
1853 Store[x][y] = EL_AMOEBA_WET;
1857 case EL_DYNAMITE_ACTIVE:
1858 case EL_SP_DISK_RED_ACTIVE:
1859 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1860 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1861 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1862 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1863 MovDelay[x][y] = 96;
1866 case EL_EM_DYNAMITE_ACTIVE:
1867 MovDelay[x][y] = 32;
1871 local_player->lights_still_needed++;
1875 local_player->friends_still_needed++;
1880 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1883 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1884 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1885 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1886 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1887 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1888 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1889 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1890 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1891 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1892 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1893 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1894 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1897 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1898 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1899 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1901 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1903 game.belt_dir[belt_nr] = belt_dir;
1904 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1906 else /* more than one switch -- set it like the first switch */
1908 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1913 case EL_LIGHT_SWITCH_ACTIVE:
1915 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1918 case EL_INVISIBLE_STEELWALL:
1919 case EL_INVISIBLE_WALL:
1920 case EL_INVISIBLE_SAND:
1921 if (game.light_time_left > 0 ||
1922 game.lenses_time_left > 0)
1923 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1926 case EL_EMC_MAGIC_BALL:
1927 if (game.ball_state)
1928 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1931 case EL_EMC_MAGIC_BALL_SWITCH:
1932 if (game.ball_state)
1933 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1936 case EL_TRIGGER_PLAYER:
1937 case EL_TRIGGER_ELEMENT:
1938 case EL_TRIGGER_CE_VALUE:
1939 case EL_TRIGGER_CE_SCORE:
1941 case EL_ANY_ELEMENT:
1942 case EL_CURRENT_CE_VALUE:
1943 case EL_CURRENT_CE_SCORE:
1960 /* reference elements should not be used on the playfield */
1961 Feld[x][y] = EL_EMPTY;
1965 if (IS_CUSTOM_ELEMENT(element))
1967 if (CAN_MOVE(element))
1970 if (!element_info[element].use_last_ce_value || init_game)
1971 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1973 else if (IS_GROUP_ELEMENT(element))
1975 Feld[x][y] = GetElementFromGroupElement(element);
1977 InitField(x, y, init_game);
1984 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1987 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1989 InitField(x, y, init_game);
1991 /* not needed to call InitMovDir() -- already done by InitField()! */
1992 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1993 CAN_MOVE(Feld[x][y]))
1997 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1999 int old_element = Feld[x][y];
2001 InitField(x, y, init_game);
2003 /* not needed to call InitMovDir() -- already done by InitField()! */
2004 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2005 CAN_MOVE(old_element) &&
2006 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2009 /* this case is in fact a combination of not less than three bugs:
2010 first, it calls InitMovDir() for elements that can move, although this is
2011 already done by InitField(); then, it checks the element that was at this
2012 field _before_ the call to InitField() (which can change it); lastly, it
2013 was not called for "mole with direction" elements, which were treated as
2014 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2018 static int get_key_element_from_nr(int key_nr)
2020 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2021 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2022 EL_EM_KEY_1 : EL_KEY_1);
2024 return key_base_element + key_nr;
2027 static int get_next_dropped_element(struct PlayerInfo *player)
2029 return (player->inventory_size > 0 ?
2030 player->inventory_element[player->inventory_size - 1] :
2031 player->inventory_infinite_element != EL_UNDEFINED ?
2032 player->inventory_infinite_element :
2033 player->dynabombs_left > 0 ?
2034 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2038 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2040 /* pos >= 0: get element from bottom of the stack;
2041 pos < 0: get element from top of the stack */
2045 int min_inventory_size = -pos;
2046 int inventory_pos = player->inventory_size - min_inventory_size;
2047 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2049 return (player->inventory_size >= min_inventory_size ?
2050 player->inventory_element[inventory_pos] :
2051 player->inventory_infinite_element != EL_UNDEFINED ?
2052 player->inventory_infinite_element :
2053 player->dynabombs_left >= min_dynabombs_left ?
2054 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2059 int min_dynabombs_left = pos + 1;
2060 int min_inventory_size = pos + 1 - player->dynabombs_left;
2061 int inventory_pos = pos - player->dynabombs_left;
2063 return (player->inventory_infinite_element != EL_UNDEFINED ?
2064 player->inventory_infinite_element :
2065 player->dynabombs_left >= min_dynabombs_left ?
2066 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 player->inventory_size >= min_inventory_size ?
2068 player->inventory_element[inventory_pos] :
2073 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2075 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2076 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2079 if (gpo1->sort_priority != gpo2->sort_priority)
2080 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2082 compare_result = gpo1->nr - gpo2->nr;
2084 return compare_result;
2087 int getPlayerInventorySize(int player_nr)
2089 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2090 return level.native_em_level->ply[player_nr]->dynamite;
2091 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2092 return level.native_sp_level->game_sp->red_disk_count;
2094 return stored_player[player_nr].inventory_size;
2097 void InitGameControlValues()
2101 for (i = 0; game_panel_controls[i].nr != -1; i++)
2103 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2104 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2105 struct TextPosInfo *pos = gpc->pos;
2107 int type = gpc->type;
2111 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2112 Error(ERR_EXIT, "this should not happen -- please debug");
2115 /* force update of game controls after initialization */
2116 gpc->value = gpc->last_value = -1;
2117 gpc->frame = gpc->last_frame = -1;
2118 gpc->gfx_frame = -1;
2120 /* determine panel value width for later calculation of alignment */
2121 if (type == TYPE_INTEGER || type == TYPE_STRING)
2123 pos->width = pos->size * getFontWidth(pos->font);
2124 pos->height = getFontHeight(pos->font);
2126 else if (type == TYPE_ELEMENT)
2128 pos->width = pos->size;
2129 pos->height = pos->size;
2132 /* fill structure for game panel draw order */
2134 gpo->sort_priority = pos->sort_priority;
2137 /* sort game panel controls according to sort_priority and control number */
2138 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2139 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2142 void UpdatePlayfieldElementCount()
2144 boolean use_element_count = FALSE;
2147 /* first check if it is needed at all to calculate playfield element count */
2148 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2149 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2150 use_element_count = TRUE;
2152 if (!use_element_count)
2155 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2156 element_info[i].element_count = 0;
2158 SCAN_PLAYFIELD(x, y)
2160 element_info[Feld[x][y]].element_count++;
2163 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2164 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2165 if (IS_IN_GROUP(j, i))
2166 element_info[EL_GROUP_START + i].element_count +=
2167 element_info[j].element_count;
2170 void UpdateGameControlValues()
2173 int time = (local_player->LevelSolved ?
2174 local_player->LevelSolved_CountingTime :
2175 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2176 level.native_em_level->lev->time :
2177 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2178 level.native_sp_level->game_sp->time_played :
2179 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2180 game_mm.energy_left :
2181 game.no_time_limit ? TimePlayed : TimeLeft);
2182 int score = (local_player->LevelSolved ?
2183 local_player->LevelSolved_CountingScore :
2184 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185 level.native_em_level->lev->score :
2186 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187 level.native_sp_level->game_sp->score :
2188 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2190 local_player->score);
2191 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2192 level.native_em_level->lev->required :
2193 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2194 level.native_sp_level->game_sp->infotrons_still_needed :
2195 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2196 game_mm.kettles_still_needed :
2197 local_player->gems_still_needed);
2198 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199 level.native_em_level->lev->required > 0 :
2200 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2202 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2203 game_mm.kettles_still_needed > 0 ||
2204 game_mm.lights_still_needed > 0 :
2205 local_player->gems_still_needed > 0 ||
2206 local_player->sokobanfields_still_needed > 0 ||
2207 local_player->lights_still_needed > 0);
2208 int health = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2209 MIN(MAX(0, 100 - game_mm.laser_overload_value), 100) : 100);
2211 UpdatePlayfieldElementCount();
2213 /* update game panel control values */
2215 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2218 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219 for (i = 0; i < MAX_NUM_KEYS; i++)
2220 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2224 if (game.centered_player_nr == -1)
2226 for (i = 0; i < MAX_PLAYERS; i++)
2228 /* only one player in Supaplex game engine */
2229 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2232 for (k = 0; k < MAX_NUM_KEYS; k++)
2234 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2236 if (level.native_em_level->ply[i]->keys & (1 << k))
2237 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238 get_key_element_from_nr(k);
2240 else if (stored_player[i].key[k])
2241 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242 get_key_element_from_nr(k);
2245 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2246 getPlayerInventorySize(i);
2248 if (stored_player[i].num_white_keys > 0)
2249 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2252 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2253 stored_player[i].num_white_keys;
2258 int player_nr = game.centered_player_nr;
2260 for (k = 0; k < MAX_NUM_KEYS; k++)
2262 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2264 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2265 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266 get_key_element_from_nr(k);
2268 else if (stored_player[player_nr].key[k])
2269 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2270 get_key_element_from_nr(k);
2273 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274 getPlayerInventorySize(player_nr);
2276 if (stored_player[player_nr].num_white_keys > 0)
2277 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2279 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2280 stored_player[player_nr].num_white_keys;
2283 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2285 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2286 get_inventory_element_from_pos(local_player, i);
2287 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2288 get_inventory_element_from_pos(local_player, -i - 1);
2291 game_panel_controls[GAME_PANEL_SCORE].value = score;
2292 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2294 game_panel_controls[GAME_PANEL_TIME].value = time;
2296 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2297 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2298 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2300 if (game.no_time_limit)
2301 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2303 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2305 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2306 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2308 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2310 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2311 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2313 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2314 local_player->shield_normal_time_left;
2315 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2316 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2318 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2319 local_player->shield_deadly_time_left;
2321 game_panel_controls[GAME_PANEL_EXIT].value =
2322 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2324 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2325 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2326 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2327 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2328 EL_EMC_MAGIC_BALL_SWITCH);
2330 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2331 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2332 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2333 game.light_time_left;
2335 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2336 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2337 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2338 game.timegate_time_left;
2340 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2341 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2343 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2344 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2345 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2346 game.lenses_time_left;
2348 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2349 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2350 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2351 game.magnify_time_left;
2353 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2354 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2355 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2356 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2357 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2358 EL_BALLOON_SWITCH_NONE);
2360 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2361 local_player->dynabomb_count;
2362 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2363 local_player->dynabomb_size;
2364 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2365 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2367 game_panel_controls[GAME_PANEL_PENGUINS].value =
2368 local_player->friends_still_needed;
2370 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2371 local_player->sokobanfields_still_needed;
2372 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2373 local_player->sokobanfields_still_needed;
2375 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2376 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2378 for (i = 0; i < NUM_BELTS; i++)
2380 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2381 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2382 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2383 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2384 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2387 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2388 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2389 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2390 game.magic_wall_time_left;
2392 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2393 local_player->gravity;
2395 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2396 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2398 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2399 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2400 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2401 game.panel.element[i].id : EL_UNDEFINED);
2403 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2405 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2406 element_info[game.panel.element_count[i].id].element_count : 0);
2408 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2409 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2410 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2411 element_info[game.panel.ce_score[i].id].collect_score : 0);
2413 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2415 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2416 element_info[game.panel.ce_score_element[i].id].collect_score :
2419 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2420 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2421 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2423 /* update game panel control frames */
2425 for (i = 0; game_panel_controls[i].nr != -1; i++)
2427 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2429 if (gpc->type == TYPE_ELEMENT)
2431 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2433 int last_anim_random_frame = gfx.anim_random_frame;
2434 int element = gpc->value;
2435 int graphic = el2panelimg(element);
2437 if (gpc->value != gpc->last_value)
2440 gpc->gfx_random = INIT_GFX_RANDOM();
2446 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2447 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2448 gpc->gfx_random = INIT_GFX_RANDOM();
2451 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2452 gfx.anim_random_frame = gpc->gfx_random;
2454 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2455 gpc->gfx_frame = element_info[element].collect_score;
2457 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2460 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2461 gfx.anim_random_frame = last_anim_random_frame;
2464 else if (gpc->type == TYPE_GRAPHIC)
2466 if (gpc->graphic != IMG_UNDEFINED)
2468 int last_anim_random_frame = gfx.anim_random_frame;
2469 int graphic = gpc->graphic;
2471 if (gpc->value != gpc->last_value)
2474 gpc->gfx_random = INIT_GFX_RANDOM();
2480 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2481 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2482 gpc->gfx_random = INIT_GFX_RANDOM();
2485 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2486 gfx.anim_random_frame = gpc->gfx_random;
2488 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2490 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491 gfx.anim_random_frame = last_anim_random_frame;
2497 void DisplayGameControlValues()
2499 boolean redraw_panel = FALSE;
2502 for (i = 0; game_panel_controls[i].nr != -1; i++)
2504 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2506 if (PANEL_DEACTIVATED(gpc->pos))
2509 if (gpc->value == gpc->last_value &&
2510 gpc->frame == gpc->last_frame)
2513 redraw_panel = TRUE;
2519 /* copy default game door content to main double buffer */
2521 /* !!! CHECK AGAIN !!! */
2522 SetPanelBackground();
2523 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2524 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2526 /* redraw game control buttons */
2527 RedrawGameButtons();
2529 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2531 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2533 int nr = game_panel_order[i].nr;
2534 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2535 struct TextPosInfo *pos = gpc->pos;
2536 int type = gpc->type;
2537 int value = gpc->value;
2538 int frame = gpc->frame;
2539 int size = pos->size;
2540 int font = pos->font;
2541 boolean draw_masked = pos->draw_masked;
2542 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2544 if (PANEL_DEACTIVATED(pos))
2547 gpc->last_value = value;
2548 gpc->last_frame = frame;
2550 if (type == TYPE_INTEGER)
2552 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2553 nr == GAME_PANEL_TIME)
2555 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2557 if (use_dynamic_size) /* use dynamic number of digits */
2559 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2560 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2561 int size2 = size1 + 1;
2562 int font1 = pos->font;
2563 int font2 = pos->font_alt;
2565 size = (value < value_change ? size1 : size2);
2566 font = (value < value_change ? font1 : font2);
2570 /* correct text size if "digits" is zero or less */
2572 size = strlen(int2str(value, size));
2574 /* dynamically correct text alignment */
2575 pos->width = size * getFontWidth(font);
2577 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2578 int2str(value, size), font, mask_mode);
2580 else if (type == TYPE_ELEMENT)
2582 int element, graphic;
2586 int dst_x = PANEL_XPOS(pos);
2587 int dst_y = PANEL_YPOS(pos);
2589 if (value != EL_UNDEFINED && value != EL_EMPTY)
2592 graphic = el2panelimg(value);
2594 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2596 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2599 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2602 width = graphic_info[graphic].width * size / TILESIZE;
2603 height = graphic_info[graphic].height * size / TILESIZE;
2606 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2609 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2613 else if (type == TYPE_GRAPHIC)
2615 int graphic = gpc->graphic;
2616 int graphic_active = gpc->graphic_active;
2620 int dst_x = PANEL_XPOS(pos);
2621 int dst_y = PANEL_YPOS(pos);
2622 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2623 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2625 if (graphic != IMG_UNDEFINED && !skip)
2627 if (pos->style == STYLE_REVERSE)
2628 value = 100 - value;
2630 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2632 if (pos->direction & MV_HORIZONTAL)
2634 width = graphic_info[graphic_active].width * value / 100;
2635 height = graphic_info[graphic_active].height;
2637 if (pos->direction == MV_LEFT)
2639 src_x += graphic_info[graphic_active].width - width;
2640 dst_x += graphic_info[graphic_active].width - width;
2645 width = graphic_info[graphic_active].width;
2646 height = graphic_info[graphic_active].height * value / 100;
2648 if (pos->direction == MV_UP)
2650 src_y += graphic_info[graphic_active].height - height;
2651 dst_y += graphic_info[graphic_active].height - height;
2656 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2659 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2662 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2664 if (pos->direction & MV_HORIZONTAL)
2666 if (pos->direction == MV_RIGHT)
2673 dst_x = PANEL_XPOS(pos);
2676 width = graphic_info[graphic].width - width;
2680 if (pos->direction == MV_DOWN)
2687 dst_y = PANEL_YPOS(pos);
2690 height = graphic_info[graphic].height - height;
2694 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2697 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2701 else if (type == TYPE_STRING)
2703 boolean active = (value != 0);
2704 char *state_normal = "off";
2705 char *state_active = "on";
2706 char *state = (active ? state_active : state_normal);
2707 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2708 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2709 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2710 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2712 if (nr == GAME_PANEL_GRAVITY_STATE)
2714 int font1 = pos->font; /* (used for normal state) */
2715 int font2 = pos->font_alt; /* (used for active state) */
2717 font = (active ? font2 : font1);
2726 /* don't truncate output if "chars" is zero or less */
2729 /* dynamically correct text alignment */
2730 pos->width = size * getFontWidth(font);
2733 s_cut = getStringCopyN(s, size);
2735 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736 s_cut, font, mask_mode);
2742 redraw_mask |= REDRAW_DOOR_1;
2745 SetGameStatus(GAME_MODE_PLAYING);
2748 void UpdateAndDisplayGameControlValues()
2750 if (tape.deactivate_display)
2753 UpdateGameControlValues();
2754 DisplayGameControlValues();
2757 void UpdateGameDoorValues()
2759 UpdateGameControlValues();
2762 void DrawGameDoorValues()
2764 DisplayGameControlValues();
2769 =============================================================================
2771 -----------------------------------------------------------------------------
2772 initialize game engine due to level / tape version number
2773 =============================================================================
2776 static void InitGameEngine()
2778 int i, j, k, l, x, y;
2780 /* set game engine from tape file when re-playing, else from level file */
2781 game.engine_version = (tape.playing ? tape.engine_version :
2782 level.game_version);
2784 /* set single or multi-player game mode (needed for re-playing tapes) */
2785 game.team_mode = setup.team_mode;
2789 int num_players = 0;
2791 for (i = 0; i < MAX_PLAYERS; i++)
2792 if (tape.player_participates[i])
2795 /* multi-player tapes contain input data for more than one player */
2796 game.team_mode = (num_players > 1);
2799 /* ---------------------------------------------------------------------- */
2800 /* set flags for bugs and changes according to active game engine version */
2801 /* ---------------------------------------------------------------------- */
2804 Summary of bugfix/change:
2805 Fixed handling for custom elements that change when pushed by the player.
2807 Fixed/changed in version:
2811 Before 3.1.0, custom elements that "change when pushing" changed directly
2812 after the player started pushing them (until then handled in "DigField()").
2813 Since 3.1.0, these custom elements are not changed until the "pushing"
2814 move of the element is finished (now handled in "ContinueMoving()").
2816 Affected levels/tapes:
2817 The first condition is generally needed for all levels/tapes before version
2818 3.1.0, which might use the old behaviour before it was changed; known tapes
2819 that are affected are some tapes from the level set "Walpurgis Gardens" by
2821 The second condition is an exception from the above case and is needed for
2822 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2823 above (including some development versions of 3.1.0), but before it was
2824 known that this change would break tapes like the above and was fixed in
2825 3.1.1, so that the changed behaviour was active although the engine version
2826 while recording maybe was before 3.1.0. There is at least one tape that is
2827 affected by this exception, which is the tape for the one-level set "Bug
2828 Machine" by Juergen Bonhagen.
2831 game.use_change_when_pushing_bug =
2832 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2834 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2835 tape.game_version < VERSION_IDENT(3,1,1,0)));
2838 Summary of bugfix/change:
2839 Fixed handling for blocking the field the player leaves when moving.
2841 Fixed/changed in version:
2845 Before 3.1.1, when "block last field when moving" was enabled, the field
2846 the player is leaving when moving was blocked for the time of the move,
2847 and was directly unblocked afterwards. This resulted in the last field
2848 being blocked for exactly one less than the number of frames of one player
2849 move. Additionally, even when blocking was disabled, the last field was
2850 blocked for exactly one frame.
2851 Since 3.1.1, due to changes in player movement handling, the last field
2852 is not blocked at all when blocking is disabled. When blocking is enabled,
2853 the last field is blocked for exactly the number of frames of one player
2854 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2855 last field is blocked for exactly one more than the number of frames of
2858 Affected levels/tapes:
2859 (!!! yet to be determined -- probably many !!!)
2862 game.use_block_last_field_bug =
2863 (game.engine_version < VERSION_IDENT(3,1,1,0));
2865 game_em.use_single_button =
2866 (game.engine_version > VERSION_IDENT(4,0,0,2));
2868 game_em.use_snap_key_bug =
2869 (game.engine_version < VERSION_IDENT(4,0,1,0));
2871 /* ---------------------------------------------------------------------- */
2873 /* set maximal allowed number of custom element changes per game frame */
2874 game.max_num_changes_per_frame = 1;
2876 /* default scan direction: scan playfield from top/left to bottom/right */
2877 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2879 /* dynamically adjust element properties according to game engine version */
2880 InitElementPropertiesEngine(game.engine_version);
2883 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2884 printf(" tape version == %06d [%s] [file: %06d]\n",
2885 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2887 printf(" => game.engine_version == %06d\n", game.engine_version);
2890 /* ---------- initialize player's initial move delay --------------------- */
2892 /* dynamically adjust player properties according to level information */
2893 for (i = 0; i < MAX_PLAYERS; i++)
2894 game.initial_move_delay_value[i] =
2895 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2897 /* dynamically adjust player properties according to game engine version */
2898 for (i = 0; i < MAX_PLAYERS; i++)
2899 game.initial_move_delay[i] =
2900 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2901 game.initial_move_delay_value[i] : 0);
2903 /* ---------- initialize player's initial push delay --------------------- */
2905 /* dynamically adjust player properties according to game engine version */
2906 game.initial_push_delay_value =
2907 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2909 /* ---------- initialize changing elements ------------------------------- */
2911 /* initialize changing elements information */
2912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2914 struct ElementInfo *ei = &element_info[i];
2916 /* this pointer might have been changed in the level editor */
2917 ei->change = &ei->change_page[0];
2919 if (!IS_CUSTOM_ELEMENT(i))
2921 ei->change->target_element = EL_EMPTY_SPACE;
2922 ei->change->delay_fixed = 0;
2923 ei->change->delay_random = 0;
2924 ei->change->delay_frames = 1;
2927 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2929 ei->has_change_event[j] = FALSE;
2931 ei->event_page_nr[j] = 0;
2932 ei->event_page[j] = &ei->change_page[0];
2936 /* add changing elements from pre-defined list */
2937 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2939 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2940 struct ElementInfo *ei = &element_info[ch_delay->element];
2942 ei->change->target_element = ch_delay->target_element;
2943 ei->change->delay_fixed = ch_delay->change_delay;
2945 ei->change->pre_change_function = ch_delay->pre_change_function;
2946 ei->change->change_function = ch_delay->change_function;
2947 ei->change->post_change_function = ch_delay->post_change_function;
2949 ei->change->can_change = TRUE;
2950 ei->change->can_change_or_has_action = TRUE;
2952 ei->has_change_event[CE_DELAY] = TRUE;
2954 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2955 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2958 /* ---------- initialize internal run-time variables --------------------- */
2960 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2962 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2964 for (j = 0; j < ei->num_change_pages; j++)
2966 ei->change_page[j].can_change_or_has_action =
2967 (ei->change_page[j].can_change |
2968 ei->change_page[j].has_action);
2972 /* add change events from custom element configuration */
2973 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2975 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2977 for (j = 0; j < ei->num_change_pages; j++)
2979 if (!ei->change_page[j].can_change_or_has_action)
2982 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2984 /* only add event page for the first page found with this event */
2985 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2987 ei->has_change_event[k] = TRUE;
2989 ei->event_page_nr[k] = j;
2990 ei->event_page[k] = &ei->change_page[j];
2996 /* ---------- initialize reference elements in change conditions --------- */
2998 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3000 int element = EL_CUSTOM_START + i;
3001 struct ElementInfo *ei = &element_info[element];
3003 for (j = 0; j < ei->num_change_pages; j++)
3005 int trigger_element = ei->change_page[j].initial_trigger_element;
3007 if (trigger_element >= EL_PREV_CE_8 &&
3008 trigger_element <= EL_NEXT_CE_8)
3009 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3011 ei->change_page[j].trigger_element = trigger_element;
3015 /* ---------- initialize run-time trigger player and element ------------- */
3017 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3019 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3021 for (j = 0; j < ei->num_change_pages; j++)
3023 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3024 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3025 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3026 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3027 ei->change_page[j].actual_trigger_ce_value = 0;
3028 ei->change_page[j].actual_trigger_ce_score = 0;
3032 /* ---------- initialize trigger events ---------------------------------- */
3034 /* initialize trigger events information */
3035 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3036 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3037 trigger_events[i][j] = FALSE;
3039 /* add trigger events from element change event properties */
3040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3042 struct ElementInfo *ei = &element_info[i];
3044 for (j = 0; j < ei->num_change_pages; j++)
3046 if (!ei->change_page[j].can_change_or_has_action)
3049 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3051 int trigger_element = ei->change_page[j].trigger_element;
3053 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3055 if (ei->change_page[j].has_event[k])
3057 if (IS_GROUP_ELEMENT(trigger_element))
3059 struct ElementGroupInfo *group =
3060 element_info[trigger_element].group;
3062 for (l = 0; l < group->num_elements_resolved; l++)
3063 trigger_events[group->element_resolved[l]][k] = TRUE;
3065 else if (trigger_element == EL_ANY_ELEMENT)
3066 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3067 trigger_events[l][k] = TRUE;
3069 trigger_events[trigger_element][k] = TRUE;
3076 /* ---------- initialize push delay -------------------------------------- */
3078 /* initialize push delay values to default */
3079 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3081 if (!IS_CUSTOM_ELEMENT(i))
3083 /* set default push delay values (corrected since version 3.0.7-1) */
3084 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3086 element_info[i].push_delay_fixed = 2;
3087 element_info[i].push_delay_random = 8;
3091 element_info[i].push_delay_fixed = 8;
3092 element_info[i].push_delay_random = 8;
3097 /* set push delay value for certain elements from pre-defined list */
3098 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3100 int e = push_delay_list[i].element;
3102 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3103 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3106 /* set push delay value for Supaplex elements for newer engine versions */
3107 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3109 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3111 if (IS_SP_ELEMENT(i))
3113 /* set SP push delay to just enough to push under a falling zonk */
3114 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3116 element_info[i].push_delay_fixed = delay;
3117 element_info[i].push_delay_random = 0;
3122 /* ---------- initialize move stepsize ----------------------------------- */
3124 /* initialize move stepsize values to default */
3125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3126 if (!IS_CUSTOM_ELEMENT(i))
3127 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3129 /* set move stepsize value for certain elements from pre-defined list */
3130 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3132 int e = move_stepsize_list[i].element;
3134 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3137 /* ---------- initialize collect score ----------------------------------- */
3139 /* initialize collect score values for custom elements from initial value */
3140 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141 if (IS_CUSTOM_ELEMENT(i))
3142 element_info[i].collect_score = element_info[i].collect_score_initial;
3144 /* ---------- initialize collect count ----------------------------------- */
3146 /* initialize collect count values for non-custom elements */
3147 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3148 if (!IS_CUSTOM_ELEMENT(i))
3149 element_info[i].collect_count_initial = 0;
3151 /* add collect count values for all elements from pre-defined list */
3152 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3153 element_info[collect_count_list[i].element].collect_count_initial =
3154 collect_count_list[i].count;
3156 /* ---------- initialize access direction -------------------------------- */
3158 /* initialize access direction values to default (access from every side) */
3159 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160 if (!IS_CUSTOM_ELEMENT(i))
3161 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3163 /* set access direction value for certain elements from pre-defined list */
3164 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3165 element_info[access_direction_list[i].element].access_direction =
3166 access_direction_list[i].direction;
3168 /* ---------- initialize explosion content ------------------------------- */
3169 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (IS_CUSTOM_ELEMENT(i))
3174 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3176 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3178 element_info[i].content.e[x][y] =
3179 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3180 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3181 i == EL_PLAYER_3 ? EL_EMERALD :
3182 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3183 i == EL_MOLE ? EL_EMERALD_RED :
3184 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3185 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3186 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3187 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3188 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3189 i == EL_WALL_EMERALD ? EL_EMERALD :
3190 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3191 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3192 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3193 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3194 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3195 i == EL_WALL_PEARL ? EL_PEARL :
3196 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3201 /* ---------- initialize recursion detection ------------------------------ */
3202 recursion_loop_depth = 0;
3203 recursion_loop_detected = FALSE;
3204 recursion_loop_element = EL_UNDEFINED;
3206 /* ---------- initialize graphics engine ---------------------------------- */
3207 game.scroll_delay_value =
3208 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3209 setup.scroll_delay ? setup.scroll_delay_value : 0);
3210 game.scroll_delay_value =
3211 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3213 /* ---------- initialize game engine snapshots ---------------------------- */
3214 for (i = 0; i < MAX_PLAYERS; i++)
3215 game.snapshot.last_action[i] = 0;
3216 game.snapshot.changed_action = FALSE;
3217 game.snapshot.collected_item = FALSE;
3218 game.snapshot.mode =
3219 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3220 SNAPSHOT_MODE_EVERY_STEP :
3221 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3222 SNAPSHOT_MODE_EVERY_MOVE :
3223 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3224 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3225 game.snapshot.save_snapshot = FALSE;
3227 /* ---------- initialize level time for Supaplex engine ------------------- */
3228 /* Supaplex levels with time limit currently unsupported -- should be added */
3229 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3233 int get_num_special_action(int element, int action_first, int action_last)
3235 int num_special_action = 0;
3238 for (i = action_first; i <= action_last; i++)
3240 boolean found = FALSE;
3242 for (j = 0; j < NUM_DIRECTIONS; j++)
3243 if (el_act_dir2img(element, i, j) !=
3244 el_act_dir2img(element, ACTION_DEFAULT, j))
3248 num_special_action++;
3253 return num_special_action;
3258 =============================================================================
3260 -----------------------------------------------------------------------------
3261 initialize and start new game
3262 =============================================================================
3267 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3268 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3269 int fade_mask = REDRAW_FIELD;
3271 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3272 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3273 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3274 int initial_move_dir = MV_DOWN;
3277 // required here to update video display before fading (FIX THIS)
3278 DrawMaskedBorder(REDRAW_DOOR_2);
3280 if (!game.restart_level)
3281 CloseDoor(DOOR_CLOSE_1);
3283 SetGameStatus(GAME_MODE_PLAYING);
3285 if (level_editor_test_game)
3286 FadeSkipNextFadeIn();
3288 FadeSetEnterScreen();
3290 if (CheckIfGlobalBorderHasChanged())
3291 fade_mask = REDRAW_ALL;
3293 FadeLevelSoundsAndMusic();
3295 ExpireSoundLoops(TRUE);
3299 /* needed if different viewport properties defined for playing */
3300 ChangeViewportPropertiesIfNeeded();
3304 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3306 DrawCompleteVideoDisplay();
3309 InitGameControlValues();
3311 /* don't play tapes over network */
3312 network_playing = (options.network && !tape.playing);
3314 for (i = 0; i < MAX_PLAYERS; i++)
3316 struct PlayerInfo *player = &stored_player[i];
3318 player->index_nr = i;
3319 player->index_bit = (1 << i);
3320 player->element_nr = EL_PLAYER_1 + i;
3322 player->present = FALSE;
3323 player->active = FALSE;
3324 player->mapped = FALSE;
3326 player->killed = FALSE;
3327 player->reanimated = FALSE;
3330 player->effective_action = 0;
3331 player->programmed_action = 0;
3333 player->mouse_action.lx = 0;
3334 player->mouse_action.ly = 0;
3335 player->mouse_action.button = 0;
3337 player->effective_mouse_action.lx = 0;
3338 player->effective_mouse_action.ly = 0;
3339 player->effective_mouse_action.button = 0;
3342 player->score_final = 0;
3344 player->gems_still_needed = level.gems_needed;
3345 player->sokobanfields_still_needed = 0;
3346 player->lights_still_needed = 0;
3347 player->friends_still_needed = 0;
3349 for (j = 0; j < MAX_NUM_KEYS; j++)
3350 player->key[j] = FALSE;
3352 player->num_white_keys = 0;
3354 player->dynabomb_count = 0;
3355 player->dynabomb_size = 1;
3356 player->dynabombs_left = 0;
3357 player->dynabomb_xl = FALSE;
3359 player->MovDir = initial_move_dir;
3362 player->GfxDir = initial_move_dir;
3363 player->GfxAction = ACTION_DEFAULT;
3365 player->StepFrame = 0;
3367 player->initial_element = player->element_nr;
3368 player->artwork_element =
3369 (level.use_artwork_element[i] ? level.artwork_element[i] :
3370 player->element_nr);
3371 player->use_murphy = FALSE;
3373 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3374 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3376 player->gravity = level.initial_player_gravity[i];
3378 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3380 player->actual_frame_counter = 0;
3382 player->step_counter = 0;
3384 player->last_move_dir = initial_move_dir;
3386 player->is_active = FALSE;
3388 player->is_waiting = FALSE;
3389 player->is_moving = FALSE;
3390 player->is_auto_moving = FALSE;
3391 player->is_digging = FALSE;
3392 player->is_snapping = FALSE;
3393 player->is_collecting = FALSE;
3394 player->is_pushing = FALSE;
3395 player->is_switching = FALSE;
3396 player->is_dropping = FALSE;
3397 player->is_dropping_pressed = FALSE;
3399 player->is_bored = FALSE;
3400 player->is_sleeping = FALSE;
3402 player->was_waiting = TRUE;
3403 player->was_moving = FALSE;
3404 player->was_snapping = FALSE;
3405 player->was_dropping = FALSE;
3407 player->force_dropping = FALSE;
3409 player->frame_counter_bored = -1;
3410 player->frame_counter_sleeping = -1;
3412 player->anim_delay_counter = 0;
3413 player->post_delay_counter = 0;
3415 player->dir_waiting = initial_move_dir;
3416 player->action_waiting = ACTION_DEFAULT;
3417 player->last_action_waiting = ACTION_DEFAULT;
3418 player->special_action_bored = ACTION_DEFAULT;
3419 player->special_action_sleeping = ACTION_DEFAULT;
3421 player->switch_x = -1;
3422 player->switch_y = -1;
3424 player->drop_x = -1;
3425 player->drop_y = -1;
3427 player->show_envelope = 0;
3429 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3431 player->push_delay = -1; /* initialized when pushing starts */
3432 player->push_delay_value = game.initial_push_delay_value;
3434 player->drop_delay = 0;
3435 player->drop_pressed_delay = 0;
3437 player->last_jx = -1;
3438 player->last_jy = -1;
3442 player->shield_normal_time_left = 0;
3443 player->shield_deadly_time_left = 0;
3445 player->inventory_infinite_element = EL_UNDEFINED;
3446 player->inventory_size = 0;
3448 if (level.use_initial_inventory[i])
3450 for (j = 0; j < level.initial_inventory_size[i]; j++)
3452 int element = level.initial_inventory_content[i][j];
3453 int collect_count = element_info[element].collect_count_initial;
3456 if (!IS_CUSTOM_ELEMENT(element))
3459 if (collect_count == 0)
3460 player->inventory_infinite_element = element;
3462 for (k = 0; k < collect_count; k++)
3463 if (player->inventory_size < MAX_INVENTORY_SIZE)
3464 player->inventory_element[player->inventory_size++] = element;
3468 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3469 SnapField(player, 0, 0);
3471 player->LevelSolved = FALSE;
3472 player->GameOver = FALSE;
3474 player->LevelSolved_GameWon = FALSE;
3475 player->LevelSolved_GameEnd = FALSE;
3476 player->LevelSolved_PanelOff = FALSE;
3477 player->LevelSolved_SaveTape = FALSE;
3478 player->LevelSolved_SaveScore = FALSE;
3479 player->LevelSolved_CountingTime = 0;
3480 player->LevelSolved_CountingScore = 0;
3482 map_player_action[i] = i;
3485 network_player_action_received = FALSE;
3487 #if defined(NETWORK_AVALIABLE)
3488 /* initial null action */
3489 if (network_playing)
3490 SendToServer_MovePlayer(MV_NONE);
3499 TimeLeft = level.time;
3502 ScreenMovDir = MV_NONE;
3506 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3508 AllPlayersGone = FALSE;
3510 game.no_time_limit = (level.time == 0);
3512 game.yamyam_content_nr = 0;
3513 game.robot_wheel_active = FALSE;
3514 game.magic_wall_active = FALSE;
3515 game.magic_wall_time_left = 0;
3516 game.light_time_left = 0;
3517 game.timegate_time_left = 0;
3518 game.switchgate_pos = 0;
3519 game.wind_direction = level.wind_direction_initial;
3521 game.lenses_time_left = 0;
3522 game.magnify_time_left = 0;
3524 game.ball_state = level.ball_state_initial;
3525 game.ball_content_nr = 0;
3527 game.envelope_active = FALSE;
3529 /* set focus to local player for network games, else to all players */
3530 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3531 game.centered_player_nr_next = game.centered_player_nr;
3532 game.set_centered_player = FALSE;
3534 if (network_playing && tape.recording)
3536 /* store client dependent player focus when recording network games */
3537 tape.centered_player_nr_next = game.centered_player_nr_next;
3538 tape.set_centered_player = TRUE;
3541 for (i = 0; i < NUM_BELTS; i++)
3543 game.belt_dir[i] = MV_NONE;
3544 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3547 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3548 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3550 #if DEBUG_INIT_PLAYER
3553 printf("Player status at level initialization:\n");
3557 SCAN_PLAYFIELD(x, y)
3559 Feld[x][y] = level.field[x][y];
3560 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3561 ChangeDelay[x][y] = 0;
3562 ChangePage[x][y] = -1;
3563 CustomValue[x][y] = 0; /* initialized in InitField() */
3564 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3566 WasJustMoving[x][y] = 0;
3567 WasJustFalling[x][y] = 0;
3568 CheckCollision[x][y] = 0;
3569 CheckImpact[x][y] = 0;
3571 Pushed[x][y] = FALSE;
3573 ChangeCount[x][y] = 0;
3574 ChangeEvent[x][y] = -1;
3576 ExplodePhase[x][y] = 0;
3577 ExplodeDelay[x][y] = 0;
3578 ExplodeField[x][y] = EX_TYPE_NONE;
3580 RunnerVisit[x][y] = 0;
3581 PlayerVisit[x][y] = 0;
3584 GfxRandom[x][y] = INIT_GFX_RANDOM();
3585 GfxElement[x][y] = EL_UNDEFINED;
3586 GfxAction[x][y] = ACTION_DEFAULT;
3587 GfxDir[x][y] = MV_NONE;
3588 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3591 SCAN_PLAYFIELD(x, y)
3593 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3595 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3597 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3600 InitField(x, y, TRUE);
3602 ResetGfxAnimation(x, y);
3607 for (i = 0; i < MAX_PLAYERS; i++)
3609 struct PlayerInfo *player = &stored_player[i];
3611 /* set number of special actions for bored and sleeping animation */
3612 player->num_special_action_bored =
3613 get_num_special_action(player->artwork_element,
3614 ACTION_BORING_1, ACTION_BORING_LAST);
3615 player->num_special_action_sleeping =
3616 get_num_special_action(player->artwork_element,
3617 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3620 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3621 emulate_sb ? EMU_SOKOBAN :
3622 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3624 /* initialize type of slippery elements */
3625 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627 if (!IS_CUSTOM_ELEMENT(i))
3629 /* default: elements slip down either to the left or right randomly */
3630 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3632 /* SP style elements prefer to slip down on the left side */
3633 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3634 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3636 /* BD style elements prefer to slip down on the left side */
3637 if (game.emulation == EMU_BOULDERDASH)
3638 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3642 /* initialize explosion and ignition delay */
3643 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3645 if (!IS_CUSTOM_ELEMENT(i))
3648 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3649 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3650 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3651 int last_phase = (num_phase + 1) * delay;
3652 int half_phase = (num_phase / 2) * delay;
3654 element_info[i].explosion_delay = last_phase - 1;
3655 element_info[i].ignition_delay = half_phase;
3657 if (i == EL_BLACK_ORB)
3658 element_info[i].ignition_delay = 1;
3662 /* correct non-moving belts to start moving left */
3663 for (i = 0; i < NUM_BELTS; i++)
3664 if (game.belt_dir[i] == MV_NONE)
3665 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3667 #if USE_NEW_PLAYER_ASSIGNMENTS
3668 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3669 /* choose default local player */
3670 local_player = &stored_player[0];
3672 for (i = 0; i < MAX_PLAYERS; i++)
3673 stored_player[i].connected = FALSE;
3675 local_player->connected = TRUE;
3676 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3680 for (i = 0; i < MAX_PLAYERS; i++)
3681 stored_player[i].connected = tape.player_participates[i];
3683 else if (game.team_mode && !options.network)
3685 /* try to guess locally connected team mode players (needed for correct
3686 assignment of player figures from level to locally playing players) */
3688 for (i = 0; i < MAX_PLAYERS; i++)
3689 if (setup.input[i].use_joystick ||
3690 setup.input[i].key.left != KSYM_UNDEFINED)
3691 stored_player[i].connected = TRUE;
3694 #if DEBUG_INIT_PLAYER
3697 printf("Player status after level initialization:\n");
3699 for (i = 0; i < MAX_PLAYERS; i++)
3701 struct PlayerInfo *player = &stored_player[i];
3703 printf("- player %d: present == %d, connected == %d, active == %d",
3709 if (local_player == player)
3710 printf(" (local player)");
3717 #if DEBUG_INIT_PLAYER
3719 printf("Reassigning players ...\n");
3722 /* check if any connected player was not found in playfield */
3723 for (i = 0; i < MAX_PLAYERS; i++)
3725 struct PlayerInfo *player = &stored_player[i];
3727 if (player->connected && !player->present)
3729 struct PlayerInfo *field_player = NULL;
3731 #if DEBUG_INIT_PLAYER
3733 printf("- looking for field player for player %d ...\n", i + 1);
3736 /* assign first free player found that is present in the playfield */
3738 /* first try: look for unmapped playfield player that is not connected */
3739 for (j = 0; j < MAX_PLAYERS; j++)
3740 if (field_player == NULL &&
3741 stored_player[j].present &&
3742 !stored_player[j].mapped &&
3743 !stored_player[j].connected)
3744 field_player = &stored_player[j];
3746 /* second try: look for *any* unmapped playfield player */
3747 for (j = 0; j < MAX_PLAYERS; j++)
3748 if (field_player == NULL &&
3749 stored_player[j].present &&
3750 !stored_player[j].mapped)
3751 field_player = &stored_player[j];
3753 if (field_player != NULL)
3755 int jx = field_player->jx, jy = field_player->jy;
3757 #if DEBUG_INIT_PLAYER
3759 printf("- found player %d\n", field_player->index_nr + 1);
3762 player->present = FALSE;
3763 player->active = FALSE;
3765 field_player->present = TRUE;
3766 field_player->active = TRUE;
3769 player->initial_element = field_player->initial_element;
3770 player->artwork_element = field_player->artwork_element;
3772 player->block_last_field = field_player->block_last_field;
3773 player->block_delay_adjustment = field_player->block_delay_adjustment;
3776 StorePlayer[jx][jy] = field_player->element_nr;
3778 field_player->jx = field_player->last_jx = jx;
3779 field_player->jy = field_player->last_jy = jy;
3781 if (local_player == player)
3782 local_player = field_player;
3784 map_player_action[field_player->index_nr] = i;
3786 field_player->mapped = TRUE;
3788 #if DEBUG_INIT_PLAYER
3790 printf("- map_player_action[%d] == %d\n",
3791 field_player->index_nr + 1, i + 1);
3796 if (player->connected && player->present)
3797 player->mapped = TRUE;
3800 #if DEBUG_INIT_PLAYER
3803 printf("Player status after player assignment (first stage):\n");
3805 for (i = 0; i < MAX_PLAYERS; i++)
3807 struct PlayerInfo *player = &stored_player[i];
3809 printf("- player %d: present == %d, connected == %d, active == %d",
3815 if (local_player == player)
3816 printf(" (local player)");
3825 /* check if any connected player was not found in playfield */
3826 for (i = 0; i < MAX_PLAYERS; i++)
3828 struct PlayerInfo *player = &stored_player[i];
3830 if (player->connected && !player->present)
3832 for (j = 0; j < MAX_PLAYERS; j++)
3834 struct PlayerInfo *field_player = &stored_player[j];
3835 int jx = field_player->jx, jy = field_player->jy;
3837 /* assign first free player found that is present in the playfield */
3838 if (field_player->present && !field_player->connected)
3840 player->present = TRUE;
3841 player->active = TRUE;
3843 field_player->present = FALSE;
3844 field_player->active = FALSE;
3846 player->initial_element = field_player->initial_element;
3847 player->artwork_element = field_player->artwork_element;
3849 player->block_last_field = field_player->block_last_field;
3850 player->block_delay_adjustment = field_player->block_delay_adjustment;
3852 StorePlayer[jx][jy] = player->element_nr;
3854 player->jx = player->last_jx = jx;
3855 player->jy = player->last_jy = jy;
3865 printf("::: local_player->present == %d\n", local_player->present);
3870 /* when playing a tape, eliminate all players who do not participate */
3872 #if USE_NEW_PLAYER_ASSIGNMENTS
3874 if (!game.team_mode)
3876 for (i = 0; i < MAX_PLAYERS; i++)
3878 if (stored_player[i].active &&
3879 !tape.player_participates[map_player_action[i]])
3881 struct PlayerInfo *player = &stored_player[i];
3882 int jx = player->jx, jy = player->jy;
3884 #if DEBUG_INIT_PLAYER
3886 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3889 player->active = FALSE;
3890 StorePlayer[jx][jy] = 0;
3891 Feld[jx][jy] = EL_EMPTY;
3898 for (i = 0; i < MAX_PLAYERS; i++)
3900 if (stored_player[i].active &&
3901 !tape.player_participates[i])
3903 struct PlayerInfo *player = &stored_player[i];
3904 int jx = player->jx, jy = player->jy;
3906 player->active = FALSE;
3907 StorePlayer[jx][jy] = 0;
3908 Feld[jx][jy] = EL_EMPTY;
3913 else if (!options.network && !game.team_mode) /* && !tape.playing */
3915 /* when in single player mode, eliminate all but the first active player */
3917 for (i = 0; i < MAX_PLAYERS; i++)
3919 if (stored_player[i].active)
3921 for (j = i + 1; j < MAX_PLAYERS; j++)
3923 if (stored_player[j].active)
3925 struct PlayerInfo *player = &stored_player[j];
3926 int jx = player->jx, jy = player->jy;
3928 player->active = FALSE;
3929 player->present = FALSE;
3931 StorePlayer[jx][jy] = 0;
3932 Feld[jx][jy] = EL_EMPTY;
3939 /* when recording the game, store which players take part in the game */
3942 #if USE_NEW_PLAYER_ASSIGNMENTS
3943 for (i = 0; i < MAX_PLAYERS; i++)
3944 if (stored_player[i].connected)
3945 tape.player_participates[i] = TRUE;
3947 for (i = 0; i < MAX_PLAYERS; i++)
3948 if (stored_player[i].active)
3949 tape.player_participates[i] = TRUE;
3953 #if DEBUG_INIT_PLAYER
3956 printf("Player status after player assignment (final stage):\n");
3958 for (i = 0; i < MAX_PLAYERS; i++)
3960 struct PlayerInfo *player = &stored_player[i];
3962 printf("- player %d: present == %d, connected == %d, active == %d",
3968 if (local_player == player)
3969 printf(" (local player)");
3976 if (BorderElement == EL_EMPTY)
3979 SBX_Right = lev_fieldx - SCR_FIELDX;
3981 SBY_Lower = lev_fieldy - SCR_FIELDY;
3986 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3988 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3991 if (full_lev_fieldx <= SCR_FIELDX)
3992 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3993 if (full_lev_fieldy <= SCR_FIELDY)
3994 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3996 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3998 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4001 /* if local player not found, look for custom element that might create
4002 the player (make some assumptions about the right custom element) */
4003 if (!local_player->present)
4005 int start_x = 0, start_y = 0;
4006 int found_rating = 0;
4007 int found_element = EL_UNDEFINED;
4008 int player_nr = local_player->index_nr;
4010 SCAN_PLAYFIELD(x, y)
4012 int element = Feld[x][y];
4017 if (level.use_start_element[player_nr] &&
4018 level.start_element[player_nr] == element &&
4025 found_element = element;
4028 if (!IS_CUSTOM_ELEMENT(element))
4031 if (CAN_CHANGE(element))
4033 for (i = 0; i < element_info[element].num_change_pages; i++)
4035 /* check for player created from custom element as single target */
4036 content = element_info[element].change_page[i].target_element;
4037 is_player = ELEM_IS_PLAYER(content);
4039 if (is_player && (found_rating < 3 ||
4040 (found_rating == 3 && element < found_element)))
4046 found_element = element;
4051 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4053 /* check for player created from custom element as explosion content */
4054 content = element_info[element].content.e[xx][yy];
4055 is_player = ELEM_IS_PLAYER(content);
4057 if (is_player && (found_rating < 2 ||
4058 (found_rating == 2 && element < found_element)))
4060 start_x = x + xx - 1;
4061 start_y = y + yy - 1;
4064 found_element = element;
4067 if (!CAN_CHANGE(element))
4070 for (i = 0; i < element_info[element].num_change_pages; i++)
4072 /* check for player created from custom element as extended target */
4074 element_info[element].change_page[i].target_content.e[xx][yy];
4076 is_player = ELEM_IS_PLAYER(content);
4078 if (is_player && (found_rating < 1 ||
4079 (found_rating == 1 && element < found_element)))
4081 start_x = x + xx - 1;
4082 start_y = y + yy - 1;
4085 found_element = element;
4091 scroll_x = SCROLL_POSITION_X(start_x);
4092 scroll_y = SCROLL_POSITION_Y(start_y);
4096 scroll_x = SCROLL_POSITION_X(local_player->jx);
4097 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4100 /* !!! FIX THIS (START) !!! */
4101 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4103 InitGameEngine_EM();
4105 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4107 InitGameEngine_SP();
4109 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4111 InitGameEngine_MM();
4115 DrawLevel(REDRAW_FIELD);
4118 /* after drawing the level, correct some elements */
4119 if (game.timegate_time_left == 0)
4120 CloseAllOpenTimegates();
4123 /* blit playfield from scroll buffer to normal back buffer for fading in */
4124 BlitScreenToBitmap(backbuffer);
4125 /* !!! FIX THIS (END) !!! */
4127 DrawMaskedBorder(fade_mask);
4132 // full screen redraw is required at this point in the following cases:
4133 // - special editor door undrawn when game was started from level editor
4134 // - drawing area (playfield) was changed and has to be removed completely
4135 redraw_mask = REDRAW_ALL;
4139 if (!game.restart_level)
4141 /* copy default game door content to main double buffer */
4143 /* !!! CHECK AGAIN !!! */
4144 SetPanelBackground();
4145 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4146 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4149 SetPanelBackground();
4150 SetDrawBackgroundMask(REDRAW_DOOR_1);
4152 UpdateAndDisplayGameControlValues();
4154 if (!game.restart_level)
4160 CreateGameButtons();
4162 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4163 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4164 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4169 /* copy actual game door content to door double buffer for OpenDoor() */
4170 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4172 OpenDoor(DOOR_OPEN_ALL);
4174 PlaySound(SND_GAME_STARTING);
4176 if (setup.sound_music)
4179 KeyboardAutoRepeatOffUnlessAutoplay();
4181 #if DEBUG_INIT_PLAYER
4184 printf("Player status (final):\n");
4186 for (i = 0; i < MAX_PLAYERS; i++)
4188 struct PlayerInfo *player = &stored_player[i];
4190 printf("- player %d: present == %d, connected == %d, active == %d",
4196 if (local_player == player)
4197 printf(" (local player)");
4210 if (!game.restart_level && !tape.playing)
4212 LevelStats_incPlayed(level_nr);
4214 SaveLevelSetup_SeriesInfo();
4217 game.restart_level = FALSE;
4219 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4220 InitGameActions_MM();
4222 SaveEngineSnapshotToListInitial();
4225 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4226 int actual_player_x, int actual_player_y)
4228 /* this is used for non-R'n'D game engines to update certain engine values */
4230 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4232 actual_player_x = correctLevelPosX_EM(actual_player_x);
4233 actual_player_y = correctLevelPosY_EM(actual_player_y);
4236 /* needed to determine if sounds are played within the visible screen area */
4237 scroll_x = actual_scroll_x;
4238 scroll_y = actual_scroll_y;
4240 /* needed to get player position for "follow finger" playing input method */
4241 local_player->jx = actual_player_x;
4242 local_player->jy = actual_player_y;
4245 void InitMovDir(int x, int y)
4247 int i, element = Feld[x][y];
4248 static int xy[4][2] =
4255 static int direction[3][4] =
4257 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4258 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4259 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4268 Feld[x][y] = EL_BUG;
4269 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4272 case EL_SPACESHIP_RIGHT:
4273 case EL_SPACESHIP_UP:
4274 case EL_SPACESHIP_LEFT:
4275 case EL_SPACESHIP_DOWN:
4276 Feld[x][y] = EL_SPACESHIP;
4277 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4280 case EL_BD_BUTTERFLY_RIGHT:
4281 case EL_BD_BUTTERFLY_UP:
4282 case EL_BD_BUTTERFLY_LEFT:
4283 case EL_BD_BUTTERFLY_DOWN:
4284 Feld[x][y] = EL_BD_BUTTERFLY;
4285 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4288 case EL_BD_FIREFLY_RIGHT:
4289 case EL_BD_FIREFLY_UP:
4290 case EL_BD_FIREFLY_LEFT:
4291 case EL_BD_FIREFLY_DOWN:
4292 Feld[x][y] = EL_BD_FIREFLY;
4293 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4296 case EL_PACMAN_RIGHT:
4298 case EL_PACMAN_LEFT:
4299 case EL_PACMAN_DOWN:
4300 Feld[x][y] = EL_PACMAN;
4301 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4304 case EL_YAMYAM_LEFT:
4305 case EL_YAMYAM_RIGHT:
4307 case EL_YAMYAM_DOWN:
4308 Feld[x][y] = EL_YAMYAM;
4309 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4312 case EL_SP_SNIKSNAK:
4313 MovDir[x][y] = MV_UP;
4316 case EL_SP_ELECTRON:
4317 MovDir[x][y] = MV_LEFT;
4324 Feld[x][y] = EL_MOLE;
4325 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4329 if (IS_CUSTOM_ELEMENT(element))
4331 struct ElementInfo *ei = &element_info[element];
4332 int move_direction_initial = ei->move_direction_initial;
4333 int move_pattern = ei->move_pattern;
4335 if (move_direction_initial == MV_START_PREVIOUS)
4337 if (MovDir[x][y] != MV_NONE)
4340 move_direction_initial = MV_START_AUTOMATIC;
4343 if (move_direction_initial == MV_START_RANDOM)
4344 MovDir[x][y] = 1 << RND(4);
4345 else if (move_direction_initial & MV_ANY_DIRECTION)
4346 MovDir[x][y] = move_direction_initial;
4347 else if (move_pattern == MV_ALL_DIRECTIONS ||
4348 move_pattern == MV_TURNING_LEFT ||
4349 move_pattern == MV_TURNING_RIGHT ||
4350 move_pattern == MV_TURNING_LEFT_RIGHT ||
4351 move_pattern == MV_TURNING_RIGHT_LEFT ||
4352 move_pattern == MV_TURNING_RANDOM)
4353 MovDir[x][y] = 1 << RND(4);
4354 else if (move_pattern == MV_HORIZONTAL)
4355 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4356 else if (move_pattern == MV_VERTICAL)
4357 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4358 else if (move_pattern & MV_ANY_DIRECTION)
4359 MovDir[x][y] = element_info[element].move_pattern;
4360 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4361 move_pattern == MV_ALONG_RIGHT_SIDE)
4363 /* use random direction as default start direction */
4364 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4365 MovDir[x][y] = 1 << RND(4);
4367 for (i = 0; i < NUM_DIRECTIONS; i++)
4369 int x1 = x + xy[i][0];
4370 int y1 = y + xy[i][1];
4372 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4374 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4375 MovDir[x][y] = direction[0][i];
4377 MovDir[x][y] = direction[1][i];
4386 MovDir[x][y] = 1 << RND(4);
4388 if (element != EL_BUG &&
4389 element != EL_SPACESHIP &&
4390 element != EL_BD_BUTTERFLY &&
4391 element != EL_BD_FIREFLY)
4394 for (i = 0; i < NUM_DIRECTIONS; i++)
4396 int x1 = x + xy[i][0];
4397 int y1 = y + xy[i][1];
4399 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4401 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4403 MovDir[x][y] = direction[0][i];
4406 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4407 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4409 MovDir[x][y] = direction[1][i];
4418 GfxDir[x][y] = MovDir[x][y];
4421 void InitAmoebaNr(int x, int y)
4424 int group_nr = AmoebeNachbarNr(x, y);
4428 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4430 if (AmoebaCnt[i] == 0)
4438 AmoebaNr[x][y] = group_nr;
4439 AmoebaCnt[group_nr]++;
4440 AmoebaCnt2[group_nr]++;
4443 static void PlayerWins(struct PlayerInfo *player)
4445 player->LevelSolved = TRUE;
4446 player->GameOver = TRUE;
4448 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4449 level.native_em_level->lev->score :
4450 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4454 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4456 player->LevelSolved_CountingScore = player->score_final;
4461 static int time, time_final;
4462 static int score, score_final;
4463 static int game_over_delay_1 = 0;
4464 static int game_over_delay_2 = 0;
4465 int game_over_delay_value_1 = 50;
4466 int game_over_delay_value_2 = 50;
4468 if (!local_player->LevelSolved_GameWon)
4472 /* do not start end game actions before the player stops moving (to exit) */
4473 if (local_player->MovPos)
4476 local_player->LevelSolved_GameWon = TRUE;
4477 local_player->LevelSolved_SaveTape = tape.recording;
4478 local_player->LevelSolved_SaveScore = !tape.playing;
4482 LevelStats_incSolved(level_nr);
4484 SaveLevelSetup_SeriesInfo();
4487 if (tape.auto_play) /* tape might already be stopped here */
4488 tape.auto_play_level_solved = TRUE;
4492 game_over_delay_1 = game_over_delay_value_1;
4493 game_over_delay_2 = game_over_delay_value_2;
4495 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4496 score = score_final = local_player->score_final;
4501 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4503 else if (game.no_time_limit && TimePlayed < 999)
4506 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4509 local_player->score_final = score_final;
4511 if (level_editor_test_game)
4514 score = score_final;
4516 local_player->LevelSolved_CountingTime = time;
4517 local_player->LevelSolved_CountingScore = score;
4519 game_panel_controls[GAME_PANEL_TIME].value = time;
4520 game_panel_controls[GAME_PANEL_SCORE].value = score;
4522 DisplayGameControlValues();
4525 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4527 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4529 /* close exit door after last player */
4530 if ((AllPlayersGone &&
4531 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4532 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4533 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4534 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4535 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4537 int element = Feld[ExitX][ExitY];
4539 Feld[ExitX][ExitY] =
4540 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4541 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4542 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4543 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4544 EL_EM_STEEL_EXIT_CLOSING);
4546 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4549 /* player disappears */
4550 DrawLevelField(ExitX, ExitY);
4553 for (i = 0; i < MAX_PLAYERS; i++)
4555 struct PlayerInfo *player = &stored_player[i];
4557 if (player->present)
4559 RemovePlayer(player);
4561 /* player disappears */
4562 DrawLevelField(player->jx, player->jy);
4567 PlaySound(SND_GAME_WINNING);
4570 if (game_over_delay_1 > 0)
4572 game_over_delay_1--;
4577 if (time != time_final)
4579 int time_to_go = ABS(time_final - time);
4580 int time_count_dir = (time < time_final ? +1 : -1);
4581 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4583 time += time_count_steps * time_count_dir;
4584 score += time_count_steps * level.score[SC_TIME_BONUS];
4586 local_player->LevelSolved_CountingTime = time;
4587 local_player->LevelSolved_CountingScore = score;
4589 game_panel_controls[GAME_PANEL_TIME].value = time;
4590 game_panel_controls[GAME_PANEL_SCORE].value = score;
4592 DisplayGameControlValues();
4594 if (time == time_final)
4595 StopSound(SND_GAME_LEVELTIME_BONUS);
4596 else if (setup.sound_loops)
4597 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4599 PlaySound(SND_GAME_LEVELTIME_BONUS);
4604 local_player->LevelSolved_PanelOff = TRUE;
4606 if (game_over_delay_2 > 0)
4608 game_over_delay_2--;
4619 boolean raise_level = FALSE;
4621 local_player->LevelSolved_GameEnd = TRUE;
4623 if (!global.use_envelope_request)
4624 CloseDoor(DOOR_CLOSE_1);
4626 if (local_player->LevelSolved_SaveTape)
4628 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4631 CloseDoor(DOOR_CLOSE_ALL);
4633 if (level_editor_test_game)
4635 SetGameStatus(GAME_MODE_MAIN);
4642 if (!local_player->LevelSolved_SaveScore)
4644 SetGameStatus(GAME_MODE_MAIN);
4651 if (level_nr == leveldir_current->handicap_level)
4653 leveldir_current->handicap_level++;
4655 SaveLevelSetup_SeriesInfo();
4658 if (setup.increment_levels &&
4659 level_nr < leveldir_current->last_level)
4660 raise_level = TRUE; /* advance to next level */
4662 if ((hi_pos = NewHiScore()) >= 0)
4664 SetGameStatus(GAME_MODE_SCORES);
4666 DrawHallOfFame(hi_pos);
4676 SetGameStatus(GAME_MODE_MAIN);
4692 boolean one_score_entry_per_name = !program.many_scores_per_name;
4694 LoadScore(level_nr);
4696 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4697 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4700 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4702 if (local_player->score_final > highscore[k].Score)
4704 /* player has made it to the hall of fame */
4706 if (k < MAX_SCORE_ENTRIES - 1)
4708 int m = MAX_SCORE_ENTRIES - 1;
4710 if (one_score_entry_per_name)
4712 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4713 if (strEqual(setup.player_name, highscore[l].Name))
4716 if (m == k) /* player's new highscore overwrites his old one */
4720 for (l = m; l > k; l--)
4722 strcpy(highscore[l].Name, highscore[l - 1].Name);
4723 highscore[l].Score = highscore[l - 1].Score;
4729 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4730 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4731 highscore[k].Score = local_player->score_final;
4736 else if (one_score_entry_per_name &&
4737 !strncmp(setup.player_name, highscore[k].Name,
4738 MAX_PLAYER_NAME_LEN))
4739 break; /* player already there with a higher score */
4743 SaveScore(level_nr);
4748 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4750 int element = Feld[x][y];
4751 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4752 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4753 int horiz_move = (dx != 0);
4754 int sign = (horiz_move ? dx : dy);
4755 int step = sign * element_info[element].move_stepsize;
4757 /* special values for move stepsize for spring and things on conveyor belt */
4760 if (CAN_FALL(element) &&
4761 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4762 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4763 else if (element == EL_SPRING)
4764 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4770 inline static int getElementMoveStepsize(int x, int y)
4772 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4775 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4777 if (player->GfxAction != action || player->GfxDir != dir)
4779 player->GfxAction = action;
4780 player->GfxDir = dir;
4782 player->StepFrame = 0;
4786 static void ResetGfxFrame(int x, int y)
4788 // profiling showed that "autotest" spends 10~20% of its time in this function
4789 if (DrawingDeactivatedField())
4792 int element = Feld[x][y];
4793 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4795 if (graphic_info[graphic].anim_global_sync)
4796 GfxFrame[x][y] = FrameCounter;
4797 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4798 GfxFrame[x][y] = CustomValue[x][y];
4799 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4800 GfxFrame[x][y] = element_info[element].collect_score;
4801 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4802 GfxFrame[x][y] = ChangeDelay[x][y];
4805 static void ResetGfxAnimation(int x, int y)
4807 GfxAction[x][y] = ACTION_DEFAULT;
4808 GfxDir[x][y] = MovDir[x][y];
4811 ResetGfxFrame(x, y);
4814 static void ResetRandomAnimationValue(int x, int y)
4816 GfxRandom[x][y] = INIT_GFX_RANDOM();
4819 void InitMovingField(int x, int y, int direction)
4821 int element = Feld[x][y];
4822 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4823 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4826 boolean is_moving_before, is_moving_after;
4828 /* check if element was/is moving or being moved before/after mode change */
4829 is_moving_before = (WasJustMoving[x][y] != 0);
4830 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4832 /* reset animation only for moving elements which change direction of moving
4833 or which just started or stopped moving
4834 (else CEs with property "can move" / "not moving" are reset each frame) */
4835 if (is_moving_before != is_moving_after ||
4836 direction != MovDir[x][y])
4837 ResetGfxAnimation(x, y);
4839 MovDir[x][y] = direction;
4840 GfxDir[x][y] = direction;
4842 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4843 direction == MV_DOWN && CAN_FALL(element) ?
4844 ACTION_FALLING : ACTION_MOVING);
4846 /* this is needed for CEs with property "can move" / "not moving" */
4848 if (is_moving_after)
4850 if (Feld[newx][newy] == EL_EMPTY)
4851 Feld[newx][newy] = EL_BLOCKED;
4853 MovDir[newx][newy] = MovDir[x][y];
4855 CustomValue[newx][newy] = CustomValue[x][y];
4857 GfxFrame[newx][newy] = GfxFrame[x][y];
4858 GfxRandom[newx][newy] = GfxRandom[x][y];
4859 GfxAction[newx][newy] = GfxAction[x][y];
4860 GfxDir[newx][newy] = GfxDir[x][y];
4864 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4866 int direction = MovDir[x][y];
4867 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4868 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4874 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4876 int oldx = x, oldy = y;
4877 int direction = MovDir[x][y];
4879 if (direction == MV_LEFT)
4881 else if (direction == MV_RIGHT)
4883 else if (direction == MV_UP)
4885 else if (direction == MV_DOWN)
4888 *comes_from_x = oldx;
4889 *comes_from_y = oldy;
4892 int MovingOrBlocked2Element(int x, int y)
4894 int element = Feld[x][y];
4896 if (element == EL_BLOCKED)
4900 Blocked2Moving(x, y, &oldx, &oldy);
4901 return Feld[oldx][oldy];
4907 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4909 /* like MovingOrBlocked2Element(), but if element is moving
4910 and (x,y) is the field the moving element is just leaving,
4911 return EL_BLOCKED instead of the element value */
4912 int element = Feld[x][y];
4914 if (IS_MOVING(x, y))
4916 if (element == EL_BLOCKED)
4920 Blocked2Moving(x, y, &oldx, &oldy);
4921 return Feld[oldx][oldy];
4930 static void RemoveField(int x, int y)
4932 Feld[x][y] = EL_EMPTY;
4938 CustomValue[x][y] = 0;
4941 ChangeDelay[x][y] = 0;
4942 ChangePage[x][y] = -1;
4943 Pushed[x][y] = FALSE;
4945 GfxElement[x][y] = EL_UNDEFINED;
4946 GfxAction[x][y] = ACTION_DEFAULT;
4947 GfxDir[x][y] = MV_NONE;
4950 void RemoveMovingField(int x, int y)
4952 int oldx = x, oldy = y, newx = x, newy = y;
4953 int element = Feld[x][y];
4954 int next_element = EL_UNDEFINED;
4956 if (element != EL_BLOCKED && !IS_MOVING(x, y))
4959 if (IS_MOVING(x, y))
4961 Moving2Blocked(x, y, &newx, &newy);
4963 if (Feld[newx][newy] != EL_BLOCKED)
4965 /* element is moving, but target field is not free (blocked), but
4966 already occupied by something different (example: acid pool);
4967 in this case, only remove the moving field, but not the target */
4969 RemoveField(oldx, oldy);
4971 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4973 TEST_DrawLevelField(oldx, oldy);
4978 else if (element == EL_BLOCKED)
4980 Blocked2Moving(x, y, &oldx, &oldy);
4981 if (!IS_MOVING(oldx, oldy))
4985 if (element == EL_BLOCKED &&
4986 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4987 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4988 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4989 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4990 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4991 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4992 next_element = get_next_element(Feld[oldx][oldy]);
4994 RemoveField(oldx, oldy);
4995 RemoveField(newx, newy);
4997 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4999 if (next_element != EL_UNDEFINED)
5000 Feld[oldx][oldy] = next_element;
5002 TEST_DrawLevelField(oldx, oldy);
5003 TEST_DrawLevelField(newx, newy);
5006 void DrawDynamite(int x, int y)
5008 int sx = SCREENX(x), sy = SCREENY(y);
5009 int graphic = el2img(Feld[x][y]);
5012 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5015 if (IS_WALKABLE_INSIDE(Back[x][y]))
5019 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5020 else if (Store[x][y])
5021 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5023 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5025 if (Back[x][y] || Store[x][y])
5026 DrawGraphicThruMask(sx, sy, graphic, frame);
5028 DrawGraphic(sx, sy, graphic, frame);
5031 void CheckDynamite(int x, int y)
5033 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5037 if (MovDelay[x][y] != 0)
5040 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5046 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5051 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5053 boolean num_checked_players = 0;
5056 for (i = 0; i < MAX_PLAYERS; i++)
5058 if (stored_player[i].active)
5060 int sx = stored_player[i].jx;
5061 int sy = stored_player[i].jy;
5063 if (num_checked_players == 0)
5070 *sx1 = MIN(*sx1, sx);
5071 *sy1 = MIN(*sy1, sy);
5072 *sx2 = MAX(*sx2, sx);
5073 *sy2 = MAX(*sy2, sy);
5076 num_checked_players++;
5081 static boolean checkIfAllPlayersFitToScreen_RND()
5083 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5085 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5087 return (sx2 - sx1 < SCR_FIELDX &&
5088 sy2 - sy1 < SCR_FIELDY);
5091 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5093 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5095 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5097 *sx = (sx1 + sx2) / 2;
5098 *sy = (sy1 + sy2) / 2;
5101 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5102 boolean center_screen, boolean quick_relocation)
5104 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5105 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5106 boolean no_delay = (tape.warp_forward);
5107 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5108 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5109 int new_scroll_x, new_scroll_y;
5111 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5113 /* case 1: quick relocation inside visible screen (without scrolling) */
5120 if (!level.shifted_relocation || center_screen)
5122 /* relocation _with_ centering of screen */
5124 new_scroll_x = SCROLL_POSITION_X(x);
5125 new_scroll_y = SCROLL_POSITION_Y(y);
5129 /* relocation _without_ centering of screen */
5131 int center_scroll_x = SCROLL_POSITION_X(old_x);
5132 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5133 int offset_x = x + (scroll_x - center_scroll_x);
5134 int offset_y = y + (scroll_y - center_scroll_y);
5136 /* for new screen position, apply previous offset to center position */
5137 new_scroll_x = SCROLL_POSITION_X(offset_x);
5138 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5141 if (quick_relocation)
5143 /* case 2: quick relocation (redraw without visible scrolling) */
5145 scroll_x = new_scroll_x;
5146 scroll_y = new_scroll_y;
5153 /* case 3: visible relocation (with scrolling to new position) */
5155 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5157 SetVideoFrameDelay(wait_delay_value);
5159 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5162 int fx = FX, fy = FY;
5164 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5165 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5167 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5173 fx += dx * TILEX / 2;
5174 fy += dy * TILEY / 2;
5176 ScrollLevel(dx, dy);
5179 /* scroll in two steps of half tile size to make things smoother */
5180 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5182 /* scroll second step to align at full tile size */
5183 BlitScreenToBitmap(window);
5189 SetVideoFrameDelay(frame_delay_value_old);
5192 void RelocatePlayer(int jx, int jy, int el_player_raw)
5194 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5195 int player_nr = GET_PLAYER_NR(el_player);
5196 struct PlayerInfo *player = &stored_player[player_nr];
5197 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5198 boolean no_delay = (tape.warp_forward);
5199 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5200 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5201 int old_jx = player->jx;
5202 int old_jy = player->jy;
5203 int old_element = Feld[old_jx][old_jy];
5204 int element = Feld[jx][jy];
5205 boolean player_relocated = (old_jx != jx || old_jy != jy);
5207 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5208 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5209 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5210 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5211 int leave_side_horiz = move_dir_horiz;
5212 int leave_side_vert = move_dir_vert;
5213 int enter_side = enter_side_horiz | enter_side_vert;
5214 int leave_side = leave_side_horiz | leave_side_vert;
5216 if (player->GameOver) /* do not reanimate dead player */
5219 if (!player_relocated) /* no need to relocate the player */
5222 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5224 RemoveField(jx, jy); /* temporarily remove newly placed player */
5225 DrawLevelField(jx, jy);
5228 if (player->present)
5230 while (player->MovPos)
5232 ScrollPlayer(player, SCROLL_GO_ON);
5233 ScrollScreen(NULL, SCROLL_GO_ON);
5235 AdvanceFrameAndPlayerCounters(player->index_nr);
5239 BackToFront_WithFrameDelay(wait_delay_value);
5242 DrawPlayer(player); /* needed here only to cleanup last field */
5243 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5245 player->is_moving = FALSE;
5248 if (IS_CUSTOM_ELEMENT(old_element))
5249 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5251 player->index_bit, leave_side);
5253 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5255 player->index_bit, leave_side);
5257 Feld[jx][jy] = el_player;
5258 InitPlayerField(jx, jy, el_player, TRUE);
5260 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5261 possible that the relocation target field did not contain a player element,
5262 but a walkable element, to which the new player was relocated -- in this
5263 case, restore that (already initialized!) element on the player field */
5264 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5266 Feld[jx][jy] = element; /* restore previously existing element */
5269 /* only visually relocate centered player */
5270 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5271 FALSE, level.instant_relocation);
5273 TestIfPlayerTouchesBadThing(jx, jy);
5274 TestIfPlayerTouchesCustomElement(jx, jy);
5276 if (IS_CUSTOM_ELEMENT(element))
5277 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5278 player->index_bit, enter_side);
5280 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5281 player->index_bit, enter_side);
5283 if (player->is_switching)
5285 /* ensure that relocation while still switching an element does not cause
5286 a new element to be treated as also switched directly after relocation
5287 (this is important for teleporter switches that teleport the player to
5288 a place where another teleporter switch is in the same direction, which
5289 would then incorrectly be treated as immediately switched before the
5290 direction key that caused the switch was released) */
5292 player->switch_x += jx - old_jx;
5293 player->switch_y += jy - old_jy;
5297 void Explode(int ex, int ey, int phase, int mode)
5303 /* !!! eliminate this variable !!! */
5304 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5306 if (game.explosions_delayed)
5308 ExplodeField[ex][ey] = mode;
5312 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5314 int center_element = Feld[ex][ey];
5315 int artwork_element, explosion_element; /* set these values later */
5317 /* remove things displayed in background while burning dynamite */
5318 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5321 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5323 /* put moving element to center field (and let it explode there) */
5324 center_element = MovingOrBlocked2Element(ex, ey);
5325 RemoveMovingField(ex, ey);
5326 Feld[ex][ey] = center_element;
5329 /* now "center_element" is finally determined -- set related values now */
5330 artwork_element = center_element; /* for custom player artwork */
5331 explosion_element = center_element; /* for custom player artwork */
5333 if (IS_PLAYER(ex, ey))
5335 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5337 artwork_element = stored_player[player_nr].artwork_element;
5339 if (level.use_explosion_element[player_nr])
5341 explosion_element = level.explosion_element[player_nr];
5342 artwork_element = explosion_element;
5346 if (mode == EX_TYPE_NORMAL ||
5347 mode == EX_TYPE_CENTER ||
5348 mode == EX_TYPE_CROSS)
5349 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5351 last_phase = element_info[explosion_element].explosion_delay + 1;
5353 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5355 int xx = x - ex + 1;
5356 int yy = y - ey + 1;
5359 if (!IN_LEV_FIELD(x, y) ||
5360 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5361 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5364 element = Feld[x][y];
5366 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5368 element = MovingOrBlocked2Element(x, y);
5370 if (!IS_EXPLOSION_PROOF(element))
5371 RemoveMovingField(x, y);
5374 /* indestructible elements can only explode in center (but not flames) */
5375 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5376 mode == EX_TYPE_BORDER)) ||
5377 element == EL_FLAMES)
5380 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5381 behaviour, for example when touching a yamyam that explodes to rocks
5382 with active deadly shield, a rock is created under the player !!! */
5383 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5385 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5386 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5387 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5389 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5392 if (IS_ACTIVE_BOMB(element))
5394 /* re-activate things under the bomb like gate or penguin */
5395 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5402 /* save walkable background elements while explosion on same tile */
5403 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5404 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5405 Back[x][y] = element;
5407 /* ignite explodable elements reached by other explosion */
5408 if (element == EL_EXPLOSION)
5409 element = Store2[x][y];
5411 if (AmoebaNr[x][y] &&
5412 (element == EL_AMOEBA_FULL ||
5413 element == EL_BD_AMOEBA ||
5414 element == EL_AMOEBA_GROWING))
5416 AmoebaCnt[AmoebaNr[x][y]]--;
5417 AmoebaCnt2[AmoebaNr[x][y]]--;
5422 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5424 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5426 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5428 if (PLAYERINFO(ex, ey)->use_murphy)
5429 Store[x][y] = EL_EMPTY;
5432 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5433 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5434 else if (ELEM_IS_PLAYER(center_element))
5435 Store[x][y] = EL_EMPTY;
5436 else if (center_element == EL_YAMYAM)
5437 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5438 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5439 Store[x][y] = element_info[center_element].content.e[xx][yy];
5441 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5442 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5443 otherwise) -- FIX THIS !!! */
5444 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5445 Store[x][y] = element_info[element].content.e[1][1];
5447 else if (!CAN_EXPLODE(element))
5448 Store[x][y] = element_info[element].content.e[1][1];
5451 Store[x][y] = EL_EMPTY;
5453 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5454 center_element == EL_AMOEBA_TO_DIAMOND)
5455 Store2[x][y] = element;
5457 Feld[x][y] = EL_EXPLOSION;
5458 GfxElement[x][y] = artwork_element;
5460 ExplodePhase[x][y] = 1;
5461 ExplodeDelay[x][y] = last_phase;
5466 if (center_element == EL_YAMYAM)
5467 game.yamyam_content_nr =
5468 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5480 GfxFrame[x][y] = 0; /* restart explosion animation */
5482 last_phase = ExplodeDelay[x][y];
5484 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5486 /* this can happen if the player leaves an explosion just in time */
5487 if (GfxElement[x][y] == EL_UNDEFINED)
5488 GfxElement[x][y] = EL_EMPTY;
5490 border_element = Store2[x][y];
5491 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5492 border_element = StorePlayer[x][y];
5494 if (phase == element_info[border_element].ignition_delay ||
5495 phase == last_phase)
5497 boolean border_explosion = FALSE;
5499 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5500 !PLAYER_EXPLOSION_PROTECTED(x, y))
5502 KillPlayerUnlessExplosionProtected(x, y);
5503 border_explosion = TRUE;
5505 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5507 Feld[x][y] = Store2[x][y];
5510 border_explosion = TRUE;
5512 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5514 AmoebeUmwandeln(x, y);
5516 border_explosion = TRUE;
5519 /* if an element just explodes due to another explosion (chain-reaction),
5520 do not immediately end the new explosion when it was the last frame of
5521 the explosion (as it would be done in the following "if"-statement!) */
5522 if (border_explosion && phase == last_phase)
5526 if (phase == last_phase)
5530 element = Feld[x][y] = Store[x][y];
5531 Store[x][y] = Store2[x][y] = 0;
5532 GfxElement[x][y] = EL_UNDEFINED;
5534 /* player can escape from explosions and might therefore be still alive */
5535 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5536 element <= EL_PLAYER_IS_EXPLODING_4)
5538 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5539 int explosion_element = EL_PLAYER_1 + player_nr;
5540 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5541 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5543 if (level.use_explosion_element[player_nr])
5544 explosion_element = level.explosion_element[player_nr];
5546 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5547 element_info[explosion_element].content.e[xx][yy]);
5550 /* restore probably existing indestructible background element */
5551 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5552 element = Feld[x][y] = Back[x][y];
5555 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5556 GfxDir[x][y] = MV_NONE;
5557 ChangeDelay[x][y] = 0;
5558 ChangePage[x][y] = -1;
5560 CustomValue[x][y] = 0;
5562 InitField_WithBug2(x, y, FALSE);
5564 TEST_DrawLevelField(x, y);
5566 TestIfElementTouchesCustomElement(x, y);
5568 if (GFX_CRUMBLED(element))
5569 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5571 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5572 StorePlayer[x][y] = 0;
5574 if (ELEM_IS_PLAYER(element))
5575 RelocatePlayer(x, y, element);
5577 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5579 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5580 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5583 TEST_DrawLevelFieldCrumbled(x, y);
5585 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5587 DrawLevelElement(x, y, Back[x][y]);
5588 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5590 else if (IS_WALKABLE_UNDER(Back[x][y]))
5592 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5593 DrawLevelElementThruMask(x, y, Back[x][y]);
5595 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5596 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5600 void DynaExplode(int ex, int ey)
5603 int dynabomb_element = Feld[ex][ey];
5604 int dynabomb_size = 1;
5605 boolean dynabomb_xl = FALSE;
5606 struct PlayerInfo *player;
5607 static int xy[4][2] =
5615 if (IS_ACTIVE_BOMB(dynabomb_element))
5617 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5618 dynabomb_size = player->dynabomb_size;
5619 dynabomb_xl = player->dynabomb_xl;
5620 player->dynabombs_left++;
5623 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5625 for (i = 0; i < NUM_DIRECTIONS; i++)
5627 for (j = 1; j <= dynabomb_size; j++)
5629 int x = ex + j * xy[i][0];
5630 int y = ey + j * xy[i][1];
5633 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5636 element = Feld[x][y];
5638 /* do not restart explosions of fields with active bombs */
5639 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5642 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5644 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5645 !IS_DIGGABLE(element) && !dynabomb_xl)
5651 void Bang(int x, int y)
5653 int element = MovingOrBlocked2Element(x, y);
5654 int explosion_type = EX_TYPE_NORMAL;
5656 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5658 struct PlayerInfo *player = PLAYERINFO(x, y);
5660 element = Feld[x][y] = player->initial_element;
5662 if (level.use_explosion_element[player->index_nr])
5664 int explosion_element = level.explosion_element[player->index_nr];
5666 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5667 explosion_type = EX_TYPE_CROSS;
5668 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5669 explosion_type = EX_TYPE_CENTER;
5677 case EL_BD_BUTTERFLY:
5680 case EL_DARK_YAMYAM:
5684 RaiseScoreElement(element);
5687 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5688 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5689 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5690 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5691 case EL_DYNABOMB_INCREASE_NUMBER:
5692 case EL_DYNABOMB_INCREASE_SIZE:
5693 case EL_DYNABOMB_INCREASE_POWER:
5694 explosion_type = EX_TYPE_DYNA;
5697 case EL_DC_LANDMINE:
5698 explosion_type = EX_TYPE_CENTER;
5703 case EL_LAMP_ACTIVE:
5704 case EL_AMOEBA_TO_DIAMOND:
5705 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5706 explosion_type = EX_TYPE_CENTER;
5710 if (element_info[element].explosion_type == EXPLODES_CROSS)
5711 explosion_type = EX_TYPE_CROSS;
5712 else if (element_info[element].explosion_type == EXPLODES_1X1)
5713 explosion_type = EX_TYPE_CENTER;
5717 if (explosion_type == EX_TYPE_DYNA)
5720 Explode(x, y, EX_PHASE_START, explosion_type);
5722 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5725 void SplashAcid(int x, int y)
5727 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5728 (!IN_LEV_FIELD(x - 1, y - 2) ||
5729 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5730 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5732 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5733 (!IN_LEV_FIELD(x + 1, y - 2) ||
5734 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5735 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5737 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5740 static void InitBeltMovement()
5742 static int belt_base_element[4] =
5744 EL_CONVEYOR_BELT_1_LEFT,
5745 EL_CONVEYOR_BELT_2_LEFT,
5746 EL_CONVEYOR_BELT_3_LEFT,
5747 EL_CONVEYOR_BELT_4_LEFT
5749 static int belt_base_active_element[4] =
5751 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5752 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5753 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5754 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5759 /* set frame order for belt animation graphic according to belt direction */
5760 for (i = 0; i < NUM_BELTS; i++)
5764 for (j = 0; j < NUM_BELT_PARTS; j++)
5766 int element = belt_base_active_element[belt_nr] + j;
5767 int graphic_1 = el2img(element);
5768 int graphic_2 = el2panelimg(element);
5770 if (game.belt_dir[i] == MV_LEFT)
5772 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5773 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5777 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5778 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5783 SCAN_PLAYFIELD(x, y)
5785 int element = Feld[x][y];
5787 for (i = 0; i < NUM_BELTS; i++)
5789 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5791 int e_belt_nr = getBeltNrFromBeltElement(element);
5794 if (e_belt_nr == belt_nr)
5796 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5798 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5805 static void ToggleBeltSwitch(int x, int y)
5807 static int belt_base_element[4] =
5809 EL_CONVEYOR_BELT_1_LEFT,
5810 EL_CONVEYOR_BELT_2_LEFT,
5811 EL_CONVEYOR_BELT_3_LEFT,
5812 EL_CONVEYOR_BELT_4_LEFT
5814 static int belt_base_active_element[4] =
5816 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5817 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5818 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5819 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5821 static int belt_base_switch_element[4] =
5823 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5824 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5825 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5826 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5828 static int belt_move_dir[4] =
5836 int element = Feld[x][y];
5837 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5838 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5839 int belt_dir = belt_move_dir[belt_dir_nr];
5842 if (!IS_BELT_SWITCH(element))
5845 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5846 game.belt_dir[belt_nr] = belt_dir;
5848 if (belt_dir_nr == 3)
5851 /* set frame order for belt animation graphic according to belt direction */
5852 for (i = 0; i < NUM_BELT_PARTS; i++)
5854 int element = belt_base_active_element[belt_nr] + i;
5855 int graphic_1 = el2img(element);
5856 int graphic_2 = el2panelimg(element);
5858 if (belt_dir == MV_LEFT)
5860 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5861 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5865 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5866 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5870 SCAN_PLAYFIELD(xx, yy)
5872 int element = Feld[xx][yy];
5874 if (IS_BELT_SWITCH(element))
5876 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5878 if (e_belt_nr == belt_nr)
5880 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5881 TEST_DrawLevelField(xx, yy);
5884 else if (IS_BELT(element) && belt_dir != MV_NONE)
5886 int e_belt_nr = getBeltNrFromBeltElement(element);
5888 if (e_belt_nr == belt_nr)
5890 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5892 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5893 TEST_DrawLevelField(xx, yy);
5896 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5898 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5900 if (e_belt_nr == belt_nr)
5902 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5904 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5905 TEST_DrawLevelField(xx, yy);
5911 static void ToggleSwitchgateSwitch(int x, int y)
5915 game.switchgate_pos = !game.switchgate_pos;
5917 SCAN_PLAYFIELD(xx, yy)
5919 int element = Feld[xx][yy];
5921 if (element == EL_SWITCHGATE_SWITCH_UP)
5923 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5924 TEST_DrawLevelField(xx, yy);
5926 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5928 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5929 TEST_DrawLevelField(xx, yy);
5931 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5933 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5934 TEST_DrawLevelField(xx, yy);
5936 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5938 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5939 TEST_DrawLevelField(xx, yy);
5941 else if (element == EL_SWITCHGATE_OPEN ||
5942 element == EL_SWITCHGATE_OPENING)
5944 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5946 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5948 else if (element == EL_SWITCHGATE_CLOSED ||
5949 element == EL_SWITCHGATE_CLOSING)
5951 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5953 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5958 static int getInvisibleActiveFromInvisibleElement(int element)
5960 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5961 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
5962 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
5966 static int getInvisibleFromInvisibleActiveElement(int element)
5968 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5969 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
5970 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
5974 static void RedrawAllLightSwitchesAndInvisibleElements()
5978 SCAN_PLAYFIELD(x, y)
5980 int element = Feld[x][y];
5982 if (element == EL_LIGHT_SWITCH &&
5983 game.light_time_left > 0)
5985 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5986 TEST_DrawLevelField(x, y);
5988 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5989 game.light_time_left == 0)
5991 Feld[x][y] = EL_LIGHT_SWITCH;
5992 TEST_DrawLevelField(x, y);
5994 else if (element == EL_EMC_DRIPPER &&
5995 game.light_time_left > 0)
5997 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5998 TEST_DrawLevelField(x, y);
6000 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6001 game.light_time_left == 0)
6003 Feld[x][y] = EL_EMC_DRIPPER;
6004 TEST_DrawLevelField(x, y);
6006 else if (element == EL_INVISIBLE_STEELWALL ||
6007 element == EL_INVISIBLE_WALL ||
6008 element == EL_INVISIBLE_SAND)
6010 if (game.light_time_left > 0)
6011 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6013 TEST_DrawLevelField(x, y);
6015 /* uncrumble neighbour fields, if needed */
6016 if (element == EL_INVISIBLE_SAND)
6017 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6019 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6020 element == EL_INVISIBLE_WALL_ACTIVE ||
6021 element == EL_INVISIBLE_SAND_ACTIVE)
6023 if (game.light_time_left == 0)
6024 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6026 TEST_DrawLevelField(x, y);
6028 /* re-crumble neighbour fields, if needed */
6029 if (element == EL_INVISIBLE_SAND)
6030 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6035 static void RedrawAllInvisibleElementsForLenses()
6039 SCAN_PLAYFIELD(x, y)
6041 int element = Feld[x][y];
6043 if (element == EL_EMC_DRIPPER &&
6044 game.lenses_time_left > 0)
6046 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6047 TEST_DrawLevelField(x, y);
6049 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6050 game.lenses_time_left == 0)
6052 Feld[x][y] = EL_EMC_DRIPPER;
6053 TEST_DrawLevelField(x, y);
6055 else if (element == EL_INVISIBLE_STEELWALL ||
6056 element == EL_INVISIBLE_WALL ||
6057 element == EL_INVISIBLE_SAND)
6059 if (game.lenses_time_left > 0)
6060 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6062 TEST_DrawLevelField(x, y);
6064 /* uncrumble neighbour fields, if needed */
6065 if (element == EL_INVISIBLE_SAND)
6066 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6068 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6069 element == EL_INVISIBLE_WALL_ACTIVE ||
6070 element == EL_INVISIBLE_SAND_ACTIVE)
6072 if (game.lenses_time_left == 0)
6073 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6075 TEST_DrawLevelField(x, y);
6077 /* re-crumble neighbour fields, if needed */
6078 if (element == EL_INVISIBLE_SAND)
6079 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6084 static void RedrawAllInvisibleElementsForMagnifier()
6088 SCAN_PLAYFIELD(x, y)
6090 int element = Feld[x][y];
6092 if (element == EL_EMC_FAKE_GRASS &&
6093 game.magnify_time_left > 0)
6095 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6096 TEST_DrawLevelField(x, y);
6098 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6099 game.magnify_time_left == 0)
6101 Feld[x][y] = EL_EMC_FAKE_GRASS;
6102 TEST_DrawLevelField(x, y);
6104 else if (IS_GATE_GRAY(element) &&
6105 game.magnify_time_left > 0)
6107 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6108 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6109 IS_EM_GATE_GRAY(element) ?
6110 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6111 IS_EMC_GATE_GRAY(element) ?
6112 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6113 IS_DC_GATE_GRAY(element) ?
6114 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6116 TEST_DrawLevelField(x, y);
6118 else if (IS_GATE_GRAY_ACTIVE(element) &&
6119 game.magnify_time_left == 0)
6121 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6122 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6123 IS_EM_GATE_GRAY_ACTIVE(element) ?
6124 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6125 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6126 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6127 IS_DC_GATE_GRAY_ACTIVE(element) ?
6128 EL_DC_GATE_WHITE_GRAY :
6130 TEST_DrawLevelField(x, y);
6135 static void ToggleLightSwitch(int x, int y)
6137 int element = Feld[x][y];
6139 game.light_time_left =
6140 (element == EL_LIGHT_SWITCH ?
6141 level.time_light * FRAMES_PER_SECOND : 0);
6143 RedrawAllLightSwitchesAndInvisibleElements();
6146 static void ActivateTimegateSwitch(int x, int y)
6150 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6152 SCAN_PLAYFIELD(xx, yy)
6154 int element = Feld[xx][yy];
6156 if (element == EL_TIMEGATE_CLOSED ||
6157 element == EL_TIMEGATE_CLOSING)
6159 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6160 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6164 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6166 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6167 TEST_DrawLevelField(xx, yy);
6173 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6174 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6177 void Impact(int x, int y)
6179 boolean last_line = (y == lev_fieldy - 1);
6180 boolean object_hit = FALSE;
6181 boolean impact = (last_line || object_hit);
6182 int element = Feld[x][y];
6183 int smashed = EL_STEELWALL;
6185 if (!last_line) /* check if element below was hit */
6187 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6190 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6191 MovDir[x][y + 1] != MV_DOWN ||
6192 MovPos[x][y + 1] <= TILEY / 2));
6194 /* do not smash moving elements that left the smashed field in time */
6195 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6196 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6199 #if USE_QUICKSAND_IMPACT_BUGFIX
6200 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6202 RemoveMovingField(x, y + 1);
6203 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6204 Feld[x][y + 2] = EL_ROCK;
6205 TEST_DrawLevelField(x, y + 2);
6210 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6212 RemoveMovingField(x, y + 1);
6213 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6214 Feld[x][y + 2] = EL_ROCK;
6215 TEST_DrawLevelField(x, y + 2);
6222 smashed = MovingOrBlocked2Element(x, y + 1);
6224 impact = (last_line || object_hit);
6227 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6229 SplashAcid(x, y + 1);
6233 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6234 /* only reset graphic animation if graphic really changes after impact */
6236 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6238 ResetGfxAnimation(x, y);
6239 TEST_DrawLevelField(x, y);
6242 if (impact && CAN_EXPLODE_IMPACT(element))
6247 else if (impact && element == EL_PEARL &&
6248 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6250 ResetGfxAnimation(x, y);
6252 Feld[x][y] = EL_PEARL_BREAKING;
6253 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6256 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6258 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6263 if (impact && element == EL_AMOEBA_DROP)
6265 if (object_hit && IS_PLAYER(x, y + 1))
6266 KillPlayerUnlessEnemyProtected(x, y + 1);
6267 else if (object_hit && smashed == EL_PENGUIN)
6271 Feld[x][y] = EL_AMOEBA_GROWING;
6272 Store[x][y] = EL_AMOEBA_WET;
6274 ResetRandomAnimationValue(x, y);
6279 if (object_hit) /* check which object was hit */
6281 if ((CAN_PASS_MAGIC_WALL(element) &&
6282 (smashed == EL_MAGIC_WALL ||
6283 smashed == EL_BD_MAGIC_WALL)) ||
6284 (CAN_PASS_DC_MAGIC_WALL(element) &&
6285 smashed == EL_DC_MAGIC_WALL))
6288 int activated_magic_wall =
6289 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6290 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6291 EL_DC_MAGIC_WALL_ACTIVE);
6293 /* activate magic wall / mill */
6294 SCAN_PLAYFIELD(xx, yy)
6296 if (Feld[xx][yy] == smashed)
6297 Feld[xx][yy] = activated_magic_wall;
6300 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6301 game.magic_wall_active = TRUE;
6303 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6304 SND_MAGIC_WALL_ACTIVATING :
6305 smashed == EL_BD_MAGIC_WALL ?
6306 SND_BD_MAGIC_WALL_ACTIVATING :
6307 SND_DC_MAGIC_WALL_ACTIVATING));
6310 if (IS_PLAYER(x, y + 1))
6312 if (CAN_SMASH_PLAYER(element))
6314 KillPlayerUnlessEnemyProtected(x, y + 1);
6318 else if (smashed == EL_PENGUIN)
6320 if (CAN_SMASH_PLAYER(element))
6326 else if (element == EL_BD_DIAMOND)
6328 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6334 else if (((element == EL_SP_INFOTRON ||
6335 element == EL_SP_ZONK) &&
6336 (smashed == EL_SP_SNIKSNAK ||
6337 smashed == EL_SP_ELECTRON ||
6338 smashed == EL_SP_DISK_ORANGE)) ||
6339 (element == EL_SP_INFOTRON &&
6340 smashed == EL_SP_DISK_YELLOW))
6345 else if (CAN_SMASH_EVERYTHING(element))
6347 if (IS_CLASSIC_ENEMY(smashed) ||
6348 CAN_EXPLODE_SMASHED(smashed))
6353 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6355 if (smashed == EL_LAMP ||
6356 smashed == EL_LAMP_ACTIVE)
6361 else if (smashed == EL_NUT)
6363 Feld[x][y + 1] = EL_NUT_BREAKING;
6364 PlayLevelSound(x, y, SND_NUT_BREAKING);
6365 RaiseScoreElement(EL_NUT);
6368 else if (smashed == EL_PEARL)
6370 ResetGfxAnimation(x, y);
6372 Feld[x][y + 1] = EL_PEARL_BREAKING;
6373 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6376 else if (smashed == EL_DIAMOND)
6378 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6379 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6382 else if (IS_BELT_SWITCH(smashed))
6384 ToggleBeltSwitch(x, y + 1);
6386 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6387 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6388 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6389 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6391 ToggleSwitchgateSwitch(x, y + 1);
6393 else if (smashed == EL_LIGHT_SWITCH ||
6394 smashed == EL_LIGHT_SWITCH_ACTIVE)
6396 ToggleLightSwitch(x, y + 1);
6400 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6402 CheckElementChangeBySide(x, y + 1, smashed, element,
6403 CE_SWITCHED, CH_SIDE_TOP);
6404 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6410 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6415 /* play sound of magic wall / mill */
6417 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6418 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6419 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6421 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6422 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6423 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6424 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6425 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6426 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6431 /* play sound of object that hits the ground */
6432 if (last_line || object_hit)
6433 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6436 inline static void TurnRoundExt(int x, int y)
6448 { 0, 0 }, { 0, 0 }, { 0, 0 },
6453 int left, right, back;
6457 { MV_DOWN, MV_UP, MV_RIGHT },
6458 { MV_UP, MV_DOWN, MV_LEFT },
6460 { MV_LEFT, MV_RIGHT, MV_DOWN },
6464 { MV_RIGHT, MV_LEFT, MV_UP }
6467 int element = Feld[x][y];
6468 int move_pattern = element_info[element].move_pattern;
6470 int old_move_dir = MovDir[x][y];
6471 int left_dir = turn[old_move_dir].left;
6472 int right_dir = turn[old_move_dir].right;
6473 int back_dir = turn[old_move_dir].back;
6475 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6476 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6477 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6478 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6480 int left_x = x + left_dx, left_y = y + left_dy;
6481 int right_x = x + right_dx, right_y = y + right_dy;
6482 int move_x = x + move_dx, move_y = y + move_dy;
6486 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6488 TestIfBadThingTouchesOtherBadThing(x, y);
6490 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6491 MovDir[x][y] = right_dir;
6492 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6493 MovDir[x][y] = left_dir;
6495 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6497 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6500 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6502 TestIfBadThingTouchesOtherBadThing(x, y);
6504 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6505 MovDir[x][y] = left_dir;
6506 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6507 MovDir[x][y] = right_dir;
6509 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6511 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6514 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6516 TestIfBadThingTouchesOtherBadThing(x, y);
6518 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6519 MovDir[x][y] = left_dir;
6520 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6521 MovDir[x][y] = right_dir;
6523 if (MovDir[x][y] != old_move_dir)
6526 else if (element == EL_YAMYAM)
6528 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6529 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6531 if (can_turn_left && can_turn_right)
6532 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6533 else if (can_turn_left)
6534 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6535 else if (can_turn_right)
6536 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6538 MovDir[x][y] = back_dir;
6540 MovDelay[x][y] = 16 + 16 * RND(3);
6542 else if (element == EL_DARK_YAMYAM)
6544 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6546 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6549 if (can_turn_left && can_turn_right)
6550 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6551 else if (can_turn_left)
6552 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6553 else if (can_turn_right)
6554 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6556 MovDir[x][y] = back_dir;
6558 MovDelay[x][y] = 16 + 16 * RND(3);
6560 else if (element == EL_PACMAN)
6562 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6563 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6565 if (can_turn_left && can_turn_right)
6566 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6567 else if (can_turn_left)
6568 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6569 else if (can_turn_right)
6570 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6572 MovDir[x][y] = back_dir;
6574 MovDelay[x][y] = 6 + RND(40);
6576 else if (element == EL_PIG)
6578 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6579 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6580 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6581 boolean should_turn_left, should_turn_right, should_move_on;
6583 int rnd = RND(rnd_value);
6585 should_turn_left = (can_turn_left &&
6587 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6588 y + back_dy + left_dy)));
6589 should_turn_right = (can_turn_right &&
6591 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6592 y + back_dy + right_dy)));
6593 should_move_on = (can_move_on &&
6596 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6597 y + move_dy + left_dy) ||
6598 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6599 y + move_dy + right_dy)));
6601 if (should_turn_left || should_turn_right || should_move_on)
6603 if (should_turn_left && should_turn_right && should_move_on)
6604 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6605 rnd < 2 * rnd_value / 3 ? right_dir :
6607 else if (should_turn_left && should_turn_right)
6608 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6609 else if (should_turn_left && should_move_on)
6610 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6611 else if (should_turn_right && should_move_on)
6612 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6613 else if (should_turn_left)
6614 MovDir[x][y] = left_dir;
6615 else if (should_turn_right)
6616 MovDir[x][y] = right_dir;
6617 else if (should_move_on)
6618 MovDir[x][y] = old_move_dir;
6620 else if (can_move_on && rnd > rnd_value / 8)
6621 MovDir[x][y] = old_move_dir;
6622 else if (can_turn_left && can_turn_right)
6623 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6624 else if (can_turn_left && rnd > rnd_value / 8)
6625 MovDir[x][y] = left_dir;
6626 else if (can_turn_right && rnd > rnd_value/8)
6627 MovDir[x][y] = right_dir;
6629 MovDir[x][y] = back_dir;
6631 xx = x + move_xy[MovDir[x][y]].dx;
6632 yy = y + move_xy[MovDir[x][y]].dy;
6634 if (!IN_LEV_FIELD(xx, yy) ||
6635 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6636 MovDir[x][y] = old_move_dir;
6640 else if (element == EL_DRAGON)
6642 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6643 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6644 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6646 int rnd = RND(rnd_value);
6648 if (can_move_on && rnd > rnd_value / 8)
6649 MovDir[x][y] = old_move_dir;
6650 else if (can_turn_left && can_turn_right)
6651 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6652 else if (can_turn_left && rnd > rnd_value / 8)
6653 MovDir[x][y] = left_dir;
6654 else if (can_turn_right && rnd > rnd_value / 8)
6655 MovDir[x][y] = right_dir;
6657 MovDir[x][y] = back_dir;
6659 xx = x + move_xy[MovDir[x][y]].dx;
6660 yy = y + move_xy[MovDir[x][y]].dy;
6662 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6663 MovDir[x][y] = old_move_dir;
6667 else if (element == EL_MOLE)
6669 boolean can_move_on =
6670 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6671 IS_AMOEBOID(Feld[move_x][move_y]) ||
6672 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6675 boolean can_turn_left =
6676 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6677 IS_AMOEBOID(Feld[left_x][left_y])));
6679 boolean can_turn_right =
6680 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6681 IS_AMOEBOID(Feld[right_x][right_y])));
6683 if (can_turn_left && can_turn_right)
6684 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6685 else if (can_turn_left)
6686 MovDir[x][y] = left_dir;
6688 MovDir[x][y] = right_dir;
6691 if (MovDir[x][y] != old_move_dir)
6694 else if (element == EL_BALLOON)
6696 MovDir[x][y] = game.wind_direction;
6699 else if (element == EL_SPRING)
6701 if (MovDir[x][y] & MV_HORIZONTAL)
6703 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6704 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6706 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6707 ResetGfxAnimation(move_x, move_y);
6708 TEST_DrawLevelField(move_x, move_y);
6710 MovDir[x][y] = back_dir;
6712 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6713 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6714 MovDir[x][y] = MV_NONE;
6719 else if (element == EL_ROBOT ||
6720 element == EL_SATELLITE ||
6721 element == EL_PENGUIN ||
6722 element == EL_EMC_ANDROID)
6724 int attr_x = -1, attr_y = -1;
6735 for (i = 0; i < MAX_PLAYERS; i++)
6737 struct PlayerInfo *player = &stored_player[i];
6738 int jx = player->jx, jy = player->jy;
6740 if (!player->active)
6744 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6752 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6753 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6754 game.engine_version < VERSION_IDENT(3,1,0,0)))
6760 if (element == EL_PENGUIN)
6763 static int xy[4][2] =
6771 for (i = 0; i < NUM_DIRECTIONS; i++)
6773 int ex = x + xy[i][0];
6774 int ey = y + xy[i][1];
6776 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6777 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6778 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6779 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6788 MovDir[x][y] = MV_NONE;
6790 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6791 else if (attr_x > x)
6792 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6794 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6795 else if (attr_y > y)
6796 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6798 if (element == EL_ROBOT)
6802 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6803 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6804 Moving2Blocked(x, y, &newx, &newy);
6806 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6807 MovDelay[x][y] = 8 + 8 * !RND(3);
6809 MovDelay[x][y] = 16;
6811 else if (element == EL_PENGUIN)
6817 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6819 boolean first_horiz = RND(2);
6820 int new_move_dir = MovDir[x][y];
6823 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6824 Moving2Blocked(x, y, &newx, &newy);
6826 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6830 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6831 Moving2Blocked(x, y, &newx, &newy);
6833 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6836 MovDir[x][y] = old_move_dir;
6840 else if (element == EL_SATELLITE)
6846 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6848 boolean first_horiz = RND(2);
6849 int new_move_dir = MovDir[x][y];
6852 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6853 Moving2Blocked(x, y, &newx, &newy);
6855 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6859 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6860 Moving2Blocked(x, y, &newx, &newy);
6862 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6865 MovDir[x][y] = old_move_dir;
6869 else if (element == EL_EMC_ANDROID)
6871 static int check_pos[16] =
6873 -1, /* 0 => (invalid) */
6874 7, /* 1 => MV_LEFT */
6875 3, /* 2 => MV_RIGHT */
6876 -1, /* 3 => (invalid) */
6878 0, /* 5 => MV_LEFT | MV_UP */
6879 2, /* 6 => MV_RIGHT | MV_UP */
6880 -1, /* 7 => (invalid) */
6881 5, /* 8 => MV_DOWN */
6882 6, /* 9 => MV_LEFT | MV_DOWN */
6883 4, /* 10 => MV_RIGHT | MV_DOWN */
6884 -1, /* 11 => (invalid) */
6885 -1, /* 12 => (invalid) */
6886 -1, /* 13 => (invalid) */
6887 -1, /* 14 => (invalid) */
6888 -1, /* 15 => (invalid) */
6896 { -1, -1, MV_LEFT | MV_UP },
6898 { +1, -1, MV_RIGHT | MV_UP },
6899 { +1, 0, MV_RIGHT },
6900 { +1, +1, MV_RIGHT | MV_DOWN },
6902 { -1, +1, MV_LEFT | MV_DOWN },
6905 int start_pos, check_order;
6906 boolean can_clone = FALSE;
6909 /* check if there is any free field around current position */
6910 for (i = 0; i < 8; i++)
6912 int newx = x + check_xy[i].dx;
6913 int newy = y + check_xy[i].dy;
6915 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6923 if (can_clone) /* randomly find an element to clone */
6927 start_pos = check_pos[RND(8)];
6928 check_order = (RND(2) ? -1 : +1);
6930 for (i = 0; i < 8; i++)
6932 int pos_raw = start_pos + i * check_order;
6933 int pos = (pos_raw + 8) % 8;
6934 int newx = x + check_xy[pos].dx;
6935 int newy = y + check_xy[pos].dy;
6937 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6939 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6940 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6942 Store[x][y] = Feld[newx][newy];
6951 if (can_clone) /* randomly find a direction to move */
6955 start_pos = check_pos[RND(8)];
6956 check_order = (RND(2) ? -1 : +1);
6958 for (i = 0; i < 8; i++)
6960 int pos_raw = start_pos + i * check_order;
6961 int pos = (pos_raw + 8) % 8;
6962 int newx = x + check_xy[pos].dx;
6963 int newy = y + check_xy[pos].dy;
6964 int new_move_dir = check_xy[pos].dir;
6966 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6968 MovDir[x][y] = new_move_dir;
6969 MovDelay[x][y] = level.android_clone_time * 8 + 1;
6978 if (can_clone) /* cloning and moving successful */
6981 /* cannot clone -- try to move towards player */
6983 start_pos = check_pos[MovDir[x][y] & 0x0f];
6984 check_order = (RND(2) ? -1 : +1);
6986 for (i = 0; i < 3; i++)
6988 /* first check start_pos, then previous/next or (next/previous) pos */
6989 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6990 int pos = (pos_raw + 8) % 8;
6991 int newx = x + check_xy[pos].dx;
6992 int newy = y + check_xy[pos].dy;
6993 int new_move_dir = check_xy[pos].dir;
6995 if (IS_PLAYER(newx, newy))
6998 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7000 MovDir[x][y] = new_move_dir;
7001 MovDelay[x][y] = level.android_move_time * 8 + 1;
7008 else if (move_pattern == MV_TURNING_LEFT ||
7009 move_pattern == MV_TURNING_RIGHT ||
7010 move_pattern == MV_TURNING_LEFT_RIGHT ||
7011 move_pattern == MV_TURNING_RIGHT_LEFT ||
7012 move_pattern == MV_TURNING_RANDOM ||
7013 move_pattern == MV_ALL_DIRECTIONS)
7015 boolean can_turn_left =
7016 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7017 boolean can_turn_right =
7018 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7020 if (element_info[element].move_stepsize == 0) /* "not moving" */
7023 if (move_pattern == MV_TURNING_LEFT)
7024 MovDir[x][y] = left_dir;
7025 else if (move_pattern == MV_TURNING_RIGHT)
7026 MovDir[x][y] = right_dir;
7027 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7028 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7029 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7030 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7031 else if (move_pattern == MV_TURNING_RANDOM)
7032 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7033 can_turn_right && !can_turn_left ? right_dir :
7034 RND(2) ? left_dir : right_dir);
7035 else if (can_turn_left && can_turn_right)
7036 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7037 else if (can_turn_left)
7038 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7039 else if (can_turn_right)
7040 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7042 MovDir[x][y] = back_dir;
7044 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7046 else if (move_pattern == MV_HORIZONTAL ||
7047 move_pattern == MV_VERTICAL)
7049 if (move_pattern & old_move_dir)
7050 MovDir[x][y] = back_dir;
7051 else if (move_pattern == MV_HORIZONTAL)
7052 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7053 else if (move_pattern == MV_VERTICAL)
7054 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7056 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7058 else if (move_pattern & MV_ANY_DIRECTION)
7060 MovDir[x][y] = move_pattern;
7061 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7063 else if (move_pattern & MV_WIND_DIRECTION)
7065 MovDir[x][y] = game.wind_direction;
7066 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7068 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7070 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7071 MovDir[x][y] = left_dir;
7072 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7073 MovDir[x][y] = right_dir;
7075 if (MovDir[x][y] != old_move_dir)
7076 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7078 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7080 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7081 MovDir[x][y] = right_dir;
7082 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7083 MovDir[x][y] = left_dir;
7085 if (MovDir[x][y] != old_move_dir)
7086 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7088 else if (move_pattern == MV_TOWARDS_PLAYER ||
7089 move_pattern == MV_AWAY_FROM_PLAYER)
7091 int attr_x = -1, attr_y = -1;
7093 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7104 for (i = 0; i < MAX_PLAYERS; i++)
7106 struct PlayerInfo *player = &stored_player[i];
7107 int jx = player->jx, jy = player->jy;
7109 if (!player->active)
7113 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7121 MovDir[x][y] = MV_NONE;
7123 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7124 else if (attr_x > x)
7125 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7127 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7128 else if (attr_y > y)
7129 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7131 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7133 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7135 boolean first_horiz = RND(2);
7136 int new_move_dir = MovDir[x][y];
7138 if (element_info[element].move_stepsize == 0) /* "not moving" */
7140 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7141 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7147 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7148 Moving2Blocked(x, y, &newx, &newy);
7150 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7154 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7155 Moving2Blocked(x, y, &newx, &newy);
7157 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7160 MovDir[x][y] = old_move_dir;
7163 else if (move_pattern == MV_WHEN_PUSHED ||
7164 move_pattern == MV_WHEN_DROPPED)
7166 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7167 MovDir[x][y] = MV_NONE;
7171 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7173 static int test_xy[7][2] =
7183 static int test_dir[7] =
7193 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7194 int move_preference = -1000000; /* start with very low preference */
7195 int new_move_dir = MV_NONE;
7196 int start_test = RND(4);
7199 for (i = 0; i < NUM_DIRECTIONS; i++)
7201 int move_dir = test_dir[start_test + i];
7202 int move_dir_preference;
7204 xx = x + test_xy[start_test + i][0];
7205 yy = y + test_xy[start_test + i][1];
7207 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7208 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7210 new_move_dir = move_dir;
7215 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7218 move_dir_preference = -1 * RunnerVisit[xx][yy];
7219 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7220 move_dir_preference = PlayerVisit[xx][yy];
7222 if (move_dir_preference > move_preference)
7224 /* prefer field that has not been visited for the longest time */
7225 move_preference = move_dir_preference;
7226 new_move_dir = move_dir;
7228 else if (move_dir_preference == move_preference &&
7229 move_dir == old_move_dir)
7231 /* prefer last direction when all directions are preferred equally */
7232 move_preference = move_dir_preference;
7233 new_move_dir = move_dir;
7237 MovDir[x][y] = new_move_dir;
7238 if (old_move_dir != new_move_dir)
7239 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7243 static void TurnRound(int x, int y)
7245 int direction = MovDir[x][y];
7249 GfxDir[x][y] = MovDir[x][y];
7251 if (direction != MovDir[x][y])
7255 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7257 ResetGfxFrame(x, y);
7260 static boolean JustBeingPushed(int x, int y)
7264 for (i = 0; i < MAX_PLAYERS; i++)
7266 struct PlayerInfo *player = &stored_player[i];
7268 if (player->active && player->is_pushing && player->MovPos)
7270 int next_jx = player->jx + (player->jx - player->last_jx);
7271 int next_jy = player->jy + (player->jy - player->last_jy);
7273 if (x == next_jx && y == next_jy)
7281 void StartMoving(int x, int y)
7283 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7284 int element = Feld[x][y];
7289 if (MovDelay[x][y] == 0)
7290 GfxAction[x][y] = ACTION_DEFAULT;
7292 if (CAN_FALL(element) && y < lev_fieldy - 1)
7294 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7295 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7296 if (JustBeingPushed(x, y))
7299 if (element == EL_QUICKSAND_FULL)
7301 if (IS_FREE(x, y + 1))
7303 InitMovingField(x, y, MV_DOWN);
7304 started_moving = TRUE;
7306 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7307 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7308 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7309 Store[x][y] = EL_ROCK;
7311 Store[x][y] = EL_ROCK;
7314 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7316 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7318 if (!MovDelay[x][y])
7320 MovDelay[x][y] = TILEY + 1;
7322 ResetGfxAnimation(x, y);
7323 ResetGfxAnimation(x, y + 1);
7328 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7329 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7336 Feld[x][y] = EL_QUICKSAND_EMPTY;
7337 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7338 Store[x][y + 1] = Store[x][y];
7341 PlayLevelSoundAction(x, y, ACTION_FILLING);
7343 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7345 if (!MovDelay[x][y])
7347 MovDelay[x][y] = TILEY + 1;
7349 ResetGfxAnimation(x, y);
7350 ResetGfxAnimation(x, y + 1);
7355 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7356 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7363 Feld[x][y] = EL_QUICKSAND_EMPTY;
7364 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7365 Store[x][y + 1] = Store[x][y];
7368 PlayLevelSoundAction(x, y, ACTION_FILLING);
7371 else if (element == EL_QUICKSAND_FAST_FULL)
7373 if (IS_FREE(x, y + 1))
7375 InitMovingField(x, y, MV_DOWN);
7376 started_moving = TRUE;
7378 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7379 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7380 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7381 Store[x][y] = EL_ROCK;
7383 Store[x][y] = EL_ROCK;
7386 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7388 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7390 if (!MovDelay[x][y])
7392 MovDelay[x][y] = TILEY + 1;
7394 ResetGfxAnimation(x, y);
7395 ResetGfxAnimation(x, y + 1);
7400 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7401 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7408 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7409 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7410 Store[x][y + 1] = Store[x][y];
7413 PlayLevelSoundAction(x, y, ACTION_FILLING);
7415 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7417 if (!MovDelay[x][y])
7419 MovDelay[x][y] = TILEY + 1;
7421 ResetGfxAnimation(x, y);
7422 ResetGfxAnimation(x, y + 1);
7427 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7428 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7435 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7436 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7437 Store[x][y + 1] = Store[x][y];
7440 PlayLevelSoundAction(x, y, ACTION_FILLING);
7443 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7444 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7446 InitMovingField(x, y, MV_DOWN);
7447 started_moving = TRUE;
7449 Feld[x][y] = EL_QUICKSAND_FILLING;
7450 Store[x][y] = element;
7452 PlayLevelSoundAction(x, y, ACTION_FILLING);
7454 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7455 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7457 InitMovingField(x, y, MV_DOWN);
7458 started_moving = TRUE;
7460 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7461 Store[x][y] = element;
7463 PlayLevelSoundAction(x, y, ACTION_FILLING);
7465 else if (element == EL_MAGIC_WALL_FULL)
7467 if (IS_FREE(x, y + 1))
7469 InitMovingField(x, y, MV_DOWN);
7470 started_moving = TRUE;
7472 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7473 Store[x][y] = EL_CHANGED(Store[x][y]);
7475 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7477 if (!MovDelay[x][y])
7478 MovDelay[x][y] = TILEY / 4 + 1;
7487 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7488 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7489 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7493 else if (element == EL_BD_MAGIC_WALL_FULL)
7495 if (IS_FREE(x, y + 1))
7497 InitMovingField(x, y, MV_DOWN);
7498 started_moving = TRUE;
7500 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7501 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7503 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7505 if (!MovDelay[x][y])
7506 MovDelay[x][y] = TILEY / 4 + 1;
7515 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7516 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7517 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7521 else if (element == EL_DC_MAGIC_WALL_FULL)
7523 if (IS_FREE(x, y + 1))
7525 InitMovingField(x, y, MV_DOWN);
7526 started_moving = TRUE;
7528 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7529 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7531 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7533 if (!MovDelay[x][y])
7534 MovDelay[x][y] = TILEY / 4 + 1;
7543 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7544 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7545 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7549 else if ((CAN_PASS_MAGIC_WALL(element) &&
7550 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7551 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7552 (CAN_PASS_DC_MAGIC_WALL(element) &&
7553 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7556 InitMovingField(x, y, MV_DOWN);
7557 started_moving = TRUE;
7560 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7561 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7562 EL_DC_MAGIC_WALL_FILLING);
7563 Store[x][y] = element;
7565 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7567 SplashAcid(x, y + 1);
7569 InitMovingField(x, y, MV_DOWN);
7570 started_moving = TRUE;
7572 Store[x][y] = EL_ACID;
7575 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7576 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7577 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7578 CAN_FALL(element) && WasJustFalling[x][y] &&
7579 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7581 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7582 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7583 (Feld[x][y + 1] == EL_BLOCKED)))
7585 /* this is needed for a special case not covered by calling "Impact()"
7586 from "ContinueMoving()": if an element moves to a tile directly below
7587 another element which was just falling on that tile (which was empty
7588 in the previous frame), the falling element above would just stop
7589 instead of smashing the element below (in previous version, the above
7590 element was just checked for "moving" instead of "falling", resulting
7591 in incorrect smashes caused by horizontal movement of the above
7592 element; also, the case of the player being the element to smash was
7593 simply not covered here... :-/ ) */
7595 CheckCollision[x][y] = 0;
7596 CheckImpact[x][y] = 0;
7600 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7602 if (MovDir[x][y] == MV_NONE)
7604 InitMovingField(x, y, MV_DOWN);
7605 started_moving = TRUE;
7608 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7610 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7611 MovDir[x][y] = MV_DOWN;
7613 InitMovingField(x, y, MV_DOWN);
7614 started_moving = TRUE;
7616 else if (element == EL_AMOEBA_DROP)
7618 Feld[x][y] = EL_AMOEBA_GROWING;
7619 Store[x][y] = EL_AMOEBA_WET;
7621 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7622 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7623 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7624 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7626 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7627 (IS_FREE(x - 1, y + 1) ||
7628 Feld[x - 1][y + 1] == EL_ACID));
7629 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7630 (IS_FREE(x + 1, y + 1) ||
7631 Feld[x + 1][y + 1] == EL_ACID));
7632 boolean can_fall_any = (can_fall_left || can_fall_right);
7633 boolean can_fall_both = (can_fall_left && can_fall_right);
7634 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7636 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7638 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7639 can_fall_right = FALSE;
7640 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7641 can_fall_left = FALSE;
7642 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7643 can_fall_right = FALSE;
7644 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7645 can_fall_left = FALSE;
7647 can_fall_any = (can_fall_left || can_fall_right);
7648 can_fall_both = FALSE;
7653 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7654 can_fall_right = FALSE; /* slip down on left side */
7656 can_fall_left = !(can_fall_right = RND(2));
7658 can_fall_both = FALSE;
7663 /* if not determined otherwise, prefer left side for slipping down */
7664 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7665 started_moving = TRUE;
7668 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7670 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7671 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7672 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7673 int belt_dir = game.belt_dir[belt_nr];
7675 if ((belt_dir == MV_LEFT && left_is_free) ||
7676 (belt_dir == MV_RIGHT && right_is_free))
7678 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7680 InitMovingField(x, y, belt_dir);
7681 started_moving = TRUE;
7683 Pushed[x][y] = TRUE;
7684 Pushed[nextx][y] = TRUE;
7686 GfxAction[x][y] = ACTION_DEFAULT;
7690 MovDir[x][y] = 0; /* if element was moving, stop it */
7695 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7696 if (CAN_MOVE(element) && !started_moving)
7698 int move_pattern = element_info[element].move_pattern;
7701 Moving2Blocked(x, y, &newx, &newy);
7703 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7706 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7707 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7709 WasJustMoving[x][y] = 0;
7710 CheckCollision[x][y] = 0;
7712 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7714 if (Feld[x][y] != element) /* element has changed */
7718 if (!MovDelay[x][y]) /* start new movement phase */
7720 /* all objects that can change their move direction after each step
7721 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7723 if (element != EL_YAMYAM &&
7724 element != EL_DARK_YAMYAM &&
7725 element != EL_PACMAN &&
7726 !(move_pattern & MV_ANY_DIRECTION) &&
7727 move_pattern != MV_TURNING_LEFT &&
7728 move_pattern != MV_TURNING_RIGHT &&
7729 move_pattern != MV_TURNING_LEFT_RIGHT &&
7730 move_pattern != MV_TURNING_RIGHT_LEFT &&
7731 move_pattern != MV_TURNING_RANDOM)
7735 if (MovDelay[x][y] && (element == EL_BUG ||
7736 element == EL_SPACESHIP ||
7737 element == EL_SP_SNIKSNAK ||
7738 element == EL_SP_ELECTRON ||
7739 element == EL_MOLE))
7740 TEST_DrawLevelField(x, y);
7744 if (MovDelay[x][y]) /* wait some time before next movement */
7748 if (element == EL_ROBOT ||
7749 element == EL_YAMYAM ||
7750 element == EL_DARK_YAMYAM)
7752 DrawLevelElementAnimationIfNeeded(x, y, element);
7753 PlayLevelSoundAction(x, y, ACTION_WAITING);
7755 else if (element == EL_SP_ELECTRON)
7756 DrawLevelElementAnimationIfNeeded(x, y, element);
7757 else if (element == EL_DRAGON)
7760 int dir = MovDir[x][y];
7761 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7762 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7763 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7764 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7765 dir == MV_UP ? IMG_FLAMES_1_UP :
7766 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7767 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7769 GfxAction[x][y] = ACTION_ATTACKING;
7771 if (IS_PLAYER(x, y))
7772 DrawPlayerField(x, y);
7774 TEST_DrawLevelField(x, y);
7776 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7778 for (i = 1; i <= 3; i++)
7780 int xx = x + i * dx;
7781 int yy = y + i * dy;
7782 int sx = SCREENX(xx);
7783 int sy = SCREENY(yy);
7784 int flame_graphic = graphic + (i - 1);
7786 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7791 int flamed = MovingOrBlocked2Element(xx, yy);
7793 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7796 RemoveMovingField(xx, yy);
7798 ChangeDelay[xx][yy] = 0;
7800 Feld[xx][yy] = EL_FLAMES;
7802 if (IN_SCR_FIELD(sx, sy))
7804 TEST_DrawLevelFieldCrumbled(xx, yy);
7805 DrawGraphic(sx, sy, flame_graphic, frame);
7810 if (Feld[xx][yy] == EL_FLAMES)
7811 Feld[xx][yy] = EL_EMPTY;
7812 TEST_DrawLevelField(xx, yy);
7817 if (MovDelay[x][y]) /* element still has to wait some time */
7819 PlayLevelSoundAction(x, y, ACTION_WAITING);
7825 /* now make next step */
7827 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7829 if (DONT_COLLIDE_WITH(element) &&
7830 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7831 !PLAYER_ENEMY_PROTECTED(newx, newy))
7833 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7838 else if (CAN_MOVE_INTO_ACID(element) &&
7839 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7840 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7841 (MovDir[x][y] == MV_DOWN ||
7842 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7844 SplashAcid(newx, newy);
7845 Store[x][y] = EL_ACID;
7847 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7849 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7850 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7851 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7852 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7855 TEST_DrawLevelField(x, y);
7857 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7858 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7859 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7861 local_player->friends_still_needed--;
7862 if (!local_player->friends_still_needed &&
7863 !local_player->GameOver && AllPlayersGone)
7864 PlayerWins(local_player);
7868 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7870 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7871 TEST_DrawLevelField(newx, newy);
7873 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7875 else if (!IS_FREE(newx, newy))
7877 GfxAction[x][y] = ACTION_WAITING;
7879 if (IS_PLAYER(x, y))
7880 DrawPlayerField(x, y);
7882 TEST_DrawLevelField(x, y);
7887 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7889 if (IS_FOOD_PIG(Feld[newx][newy]))
7891 if (IS_MOVING(newx, newy))
7892 RemoveMovingField(newx, newy);
7895 Feld[newx][newy] = EL_EMPTY;
7896 TEST_DrawLevelField(newx, newy);
7899 PlayLevelSound(x, y, SND_PIG_DIGGING);
7901 else if (!IS_FREE(newx, newy))
7903 if (IS_PLAYER(x, y))
7904 DrawPlayerField(x, y);
7906 TEST_DrawLevelField(x, y);
7911 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7913 if (Store[x][y] != EL_EMPTY)
7915 boolean can_clone = FALSE;
7918 /* check if element to clone is still there */
7919 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7921 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7929 /* cannot clone or target field not free anymore -- do not clone */
7930 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7931 Store[x][y] = EL_EMPTY;
7934 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7936 if (IS_MV_DIAGONAL(MovDir[x][y]))
7938 int diagonal_move_dir = MovDir[x][y];
7939 int stored = Store[x][y];
7940 int change_delay = 8;
7943 /* android is moving diagonally */
7945 CreateField(x, y, EL_DIAGONAL_SHRINKING);
7947 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7948 GfxElement[x][y] = EL_EMC_ANDROID;
7949 GfxAction[x][y] = ACTION_SHRINKING;
7950 GfxDir[x][y] = diagonal_move_dir;
7951 ChangeDelay[x][y] = change_delay;
7953 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7956 DrawLevelGraphicAnimation(x, y, graphic);
7957 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7959 if (Feld[newx][newy] == EL_ACID)
7961 SplashAcid(newx, newy);
7966 CreateField(newx, newy, EL_DIAGONAL_GROWING);
7968 Store[newx][newy] = EL_EMC_ANDROID;
7969 GfxElement[newx][newy] = EL_EMC_ANDROID;
7970 GfxAction[newx][newy] = ACTION_GROWING;
7971 GfxDir[newx][newy] = diagonal_move_dir;
7972 ChangeDelay[newx][newy] = change_delay;
7974 graphic = el_act_dir2img(GfxElement[newx][newy],
7975 GfxAction[newx][newy], GfxDir[newx][newy]);
7977 DrawLevelGraphicAnimation(newx, newy, graphic);
7978 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7984 Feld[newx][newy] = EL_EMPTY;
7985 TEST_DrawLevelField(newx, newy);
7987 PlayLevelSoundAction(x, y, ACTION_DIGGING);
7990 else if (!IS_FREE(newx, newy))
7995 else if (IS_CUSTOM_ELEMENT(element) &&
7996 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7998 if (!DigFieldByCE(newx, newy, element))
8001 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8003 RunnerVisit[x][y] = FrameCounter;
8004 PlayerVisit[x][y] /= 8; /* expire player visit path */
8007 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8009 if (!IS_FREE(newx, newy))
8011 if (IS_PLAYER(x, y))
8012 DrawPlayerField(x, y);
8014 TEST_DrawLevelField(x, y);
8020 boolean wanna_flame = !RND(10);
8021 int dx = newx - x, dy = newy - y;
8022 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8023 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8024 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8025 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8026 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8027 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8030 IS_CLASSIC_ENEMY(element1) ||
8031 IS_CLASSIC_ENEMY(element2)) &&
8032 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8033 element1 != EL_FLAMES && element2 != EL_FLAMES)
8035 ResetGfxAnimation(x, y);
8036 GfxAction[x][y] = ACTION_ATTACKING;
8038 if (IS_PLAYER(x, y))
8039 DrawPlayerField(x, y);
8041 TEST_DrawLevelField(x, y);
8043 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8045 MovDelay[x][y] = 50;
8047 Feld[newx][newy] = EL_FLAMES;
8048 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8049 Feld[newx1][newy1] = EL_FLAMES;
8050 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8051 Feld[newx2][newy2] = EL_FLAMES;
8057 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8058 Feld[newx][newy] == EL_DIAMOND)
8060 if (IS_MOVING(newx, newy))
8061 RemoveMovingField(newx, newy);
8064 Feld[newx][newy] = EL_EMPTY;
8065 TEST_DrawLevelField(newx, newy);
8068 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8070 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8071 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8073 if (AmoebaNr[newx][newy])
8075 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8076 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8077 Feld[newx][newy] == EL_BD_AMOEBA)
8078 AmoebaCnt[AmoebaNr[newx][newy]]--;
8081 if (IS_MOVING(newx, newy))
8083 RemoveMovingField(newx, newy);
8087 Feld[newx][newy] = EL_EMPTY;
8088 TEST_DrawLevelField(newx, newy);
8091 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8093 else if ((element == EL_PACMAN || element == EL_MOLE)
8094 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8096 if (AmoebaNr[newx][newy])
8098 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8099 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8100 Feld[newx][newy] == EL_BD_AMOEBA)
8101 AmoebaCnt[AmoebaNr[newx][newy]]--;
8104 if (element == EL_MOLE)
8106 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8107 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8109 ResetGfxAnimation(x, y);
8110 GfxAction[x][y] = ACTION_DIGGING;
8111 TEST_DrawLevelField(x, y);
8113 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8115 return; /* wait for shrinking amoeba */
8117 else /* element == EL_PACMAN */
8119 Feld[newx][newy] = EL_EMPTY;
8120 TEST_DrawLevelField(newx, newy);
8121 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8124 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8125 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8126 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8128 /* wait for shrinking amoeba to completely disappear */
8131 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8133 /* object was running against a wall */
8137 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8138 DrawLevelElementAnimation(x, y, element);
8140 if (DONT_TOUCH(element))
8141 TestIfBadThingTouchesPlayer(x, y);
8146 InitMovingField(x, y, MovDir[x][y]);
8148 PlayLevelSoundAction(x, y, ACTION_MOVING);
8152 ContinueMoving(x, y);
8155 void ContinueMoving(int x, int y)
8157 int element = Feld[x][y];
8158 struct ElementInfo *ei = &element_info[element];
8159 int direction = MovDir[x][y];
8160 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8161 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8162 int newx = x + dx, newy = y + dy;
8163 int stored = Store[x][y];
8164 int stored_new = Store[newx][newy];
8165 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8166 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8167 boolean last_line = (newy == lev_fieldy - 1);
8169 MovPos[x][y] += getElementMoveStepsize(x, y);
8171 if (pushed_by_player) /* special case: moving object pushed by player */
8172 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8174 if (ABS(MovPos[x][y]) < TILEX)
8176 TEST_DrawLevelField(x, y);
8178 return; /* element is still moving */
8181 /* element reached destination field */
8183 Feld[x][y] = EL_EMPTY;
8184 Feld[newx][newy] = element;
8185 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8187 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8189 element = Feld[newx][newy] = EL_ACID;
8191 else if (element == EL_MOLE)
8193 Feld[x][y] = EL_SAND;
8195 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8197 else if (element == EL_QUICKSAND_FILLING)
8199 element = Feld[newx][newy] = get_next_element(element);
8200 Store[newx][newy] = Store[x][y];
8202 else if (element == EL_QUICKSAND_EMPTYING)
8204 Feld[x][y] = get_next_element(element);
8205 element = Feld[newx][newy] = Store[x][y];
8207 else if (element == EL_QUICKSAND_FAST_FILLING)
8209 element = Feld[newx][newy] = get_next_element(element);
8210 Store[newx][newy] = Store[x][y];
8212 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8214 Feld[x][y] = get_next_element(element);
8215 element = Feld[newx][newy] = Store[x][y];
8217 else if (element == EL_MAGIC_WALL_FILLING)
8219 element = Feld[newx][newy] = get_next_element(element);
8220 if (!game.magic_wall_active)
8221 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8222 Store[newx][newy] = Store[x][y];
8224 else if (element == EL_MAGIC_WALL_EMPTYING)
8226 Feld[x][y] = get_next_element(element);
8227 if (!game.magic_wall_active)
8228 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8229 element = Feld[newx][newy] = Store[x][y];
8231 InitField(newx, newy, FALSE);
8233 else if (element == EL_BD_MAGIC_WALL_FILLING)
8235 element = Feld[newx][newy] = get_next_element(element);
8236 if (!game.magic_wall_active)
8237 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8238 Store[newx][newy] = Store[x][y];
8240 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8242 Feld[x][y] = get_next_element(element);
8243 if (!game.magic_wall_active)
8244 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8245 element = Feld[newx][newy] = Store[x][y];
8247 InitField(newx, newy, FALSE);
8249 else if (element == EL_DC_MAGIC_WALL_FILLING)
8251 element = Feld[newx][newy] = get_next_element(element);
8252 if (!game.magic_wall_active)
8253 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8254 Store[newx][newy] = Store[x][y];
8256 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8258 Feld[x][y] = get_next_element(element);
8259 if (!game.magic_wall_active)
8260 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8261 element = Feld[newx][newy] = Store[x][y];
8263 InitField(newx, newy, FALSE);
8265 else if (element == EL_AMOEBA_DROPPING)
8267 Feld[x][y] = get_next_element(element);
8268 element = Feld[newx][newy] = Store[x][y];
8270 else if (element == EL_SOKOBAN_OBJECT)
8273 Feld[x][y] = Back[x][y];
8275 if (Back[newx][newy])
8276 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8278 Back[x][y] = Back[newx][newy] = 0;
8281 Store[x][y] = EL_EMPTY;
8286 MovDelay[newx][newy] = 0;
8288 if (CAN_CHANGE_OR_HAS_ACTION(element))
8290 /* copy element change control values to new field */
8291 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8292 ChangePage[newx][newy] = ChangePage[x][y];
8293 ChangeCount[newx][newy] = ChangeCount[x][y];
8294 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8297 CustomValue[newx][newy] = CustomValue[x][y];
8299 ChangeDelay[x][y] = 0;
8300 ChangePage[x][y] = -1;
8301 ChangeCount[x][y] = 0;
8302 ChangeEvent[x][y] = -1;
8304 CustomValue[x][y] = 0;
8306 /* copy animation control values to new field */
8307 GfxFrame[newx][newy] = GfxFrame[x][y];
8308 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8309 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8310 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8312 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8314 /* some elements can leave other elements behind after moving */
8315 if (ei->move_leave_element != EL_EMPTY &&
8316 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8317 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8319 int move_leave_element = ei->move_leave_element;
8321 /* this makes it possible to leave the removed element again */
8322 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8323 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8325 Feld[x][y] = move_leave_element;
8327 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8328 MovDir[x][y] = direction;
8330 InitField(x, y, FALSE);
8332 if (GFX_CRUMBLED(Feld[x][y]))
8333 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8335 if (ELEM_IS_PLAYER(move_leave_element))
8336 RelocatePlayer(x, y, move_leave_element);
8339 /* do this after checking for left-behind element */
8340 ResetGfxAnimation(x, y); /* reset animation values for old field */
8342 if (!CAN_MOVE(element) ||
8343 (CAN_FALL(element) && direction == MV_DOWN &&
8344 (element == EL_SPRING ||
8345 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8346 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8347 GfxDir[x][y] = MovDir[newx][newy] = 0;
8349 TEST_DrawLevelField(x, y);
8350 TEST_DrawLevelField(newx, newy);
8352 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8354 /* prevent pushed element from moving on in pushed direction */
8355 if (pushed_by_player && CAN_MOVE(element) &&
8356 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8357 !(element_info[element].move_pattern & direction))
8358 TurnRound(newx, newy);
8360 /* prevent elements on conveyor belt from moving on in last direction */
8361 if (pushed_by_conveyor && CAN_FALL(element) &&
8362 direction & MV_HORIZONTAL)
8363 MovDir[newx][newy] = 0;
8365 if (!pushed_by_player)
8367 int nextx = newx + dx, nexty = newy + dy;
8368 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8370 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8372 if (CAN_FALL(element) && direction == MV_DOWN)
8373 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8375 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8376 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8378 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8379 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8382 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8384 TestIfBadThingTouchesPlayer(newx, newy);
8385 TestIfBadThingTouchesFriend(newx, newy);
8387 if (!IS_CUSTOM_ELEMENT(element))
8388 TestIfBadThingTouchesOtherBadThing(newx, newy);
8390 else if (element == EL_PENGUIN)
8391 TestIfFriendTouchesBadThing(newx, newy);
8393 if (DONT_GET_HIT_BY(element))
8395 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8398 /* give the player one last chance (one more frame) to move away */
8399 if (CAN_FALL(element) && direction == MV_DOWN &&
8400 (last_line || (!IS_FREE(x, newy + 1) &&
8401 (!IS_PLAYER(x, newy + 1) ||
8402 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8405 if (pushed_by_player && !game.use_change_when_pushing_bug)
8407 int push_side = MV_DIR_OPPOSITE(direction);
8408 struct PlayerInfo *player = PLAYERINFO(x, y);
8410 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8411 player->index_bit, push_side);
8412 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8413 player->index_bit, push_side);
8416 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8417 MovDelay[newx][newy] = 1;
8419 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8421 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8422 TestIfElementHitsCustomElement(newx, newy, direction);
8423 TestIfPlayerTouchesCustomElement(newx, newy);
8424 TestIfElementTouchesCustomElement(newx, newy);
8426 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8427 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8428 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8429 MV_DIR_OPPOSITE(direction));
8432 int AmoebeNachbarNr(int ax, int ay)
8435 int element = Feld[ax][ay];
8437 static int xy[4][2] =
8445 for (i = 0; i < NUM_DIRECTIONS; i++)
8447 int x = ax + xy[i][0];
8448 int y = ay + xy[i][1];
8450 if (!IN_LEV_FIELD(x, y))
8453 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8454 group_nr = AmoebaNr[x][y];
8460 void AmoebenVereinigen(int ax, int ay)
8462 int i, x, y, xx, yy;
8463 int new_group_nr = AmoebaNr[ax][ay];
8464 static int xy[4][2] =
8472 if (new_group_nr == 0)
8475 for (i = 0; i < NUM_DIRECTIONS; i++)
8480 if (!IN_LEV_FIELD(x, y))
8483 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8484 Feld[x][y] == EL_BD_AMOEBA ||
8485 Feld[x][y] == EL_AMOEBA_DEAD) &&
8486 AmoebaNr[x][y] != new_group_nr)
8488 int old_group_nr = AmoebaNr[x][y];
8490 if (old_group_nr == 0)
8493 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8494 AmoebaCnt[old_group_nr] = 0;
8495 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8496 AmoebaCnt2[old_group_nr] = 0;
8498 SCAN_PLAYFIELD(xx, yy)
8500 if (AmoebaNr[xx][yy] == old_group_nr)
8501 AmoebaNr[xx][yy] = new_group_nr;
8507 void AmoebeUmwandeln(int ax, int ay)
8511 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8513 int group_nr = AmoebaNr[ax][ay];
8518 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8519 printf("AmoebeUmwandeln(): This should never happen!\n");
8524 SCAN_PLAYFIELD(x, y)
8526 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8529 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8533 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8534 SND_AMOEBA_TURNING_TO_GEM :
8535 SND_AMOEBA_TURNING_TO_ROCK));
8540 static int xy[4][2] =
8548 for (i = 0; i < NUM_DIRECTIONS; i++)
8553 if (!IN_LEV_FIELD(x, y))
8556 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8558 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8559 SND_AMOEBA_TURNING_TO_GEM :
8560 SND_AMOEBA_TURNING_TO_ROCK));
8567 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8570 int group_nr = AmoebaNr[ax][ay];
8571 boolean done = FALSE;
8576 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8577 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8582 SCAN_PLAYFIELD(x, y)
8584 if (AmoebaNr[x][y] == group_nr &&
8585 (Feld[x][y] == EL_AMOEBA_DEAD ||
8586 Feld[x][y] == EL_BD_AMOEBA ||
8587 Feld[x][y] == EL_AMOEBA_GROWING))
8590 Feld[x][y] = new_element;
8591 InitField(x, y, FALSE);
8592 TEST_DrawLevelField(x, y);
8598 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8599 SND_BD_AMOEBA_TURNING_TO_ROCK :
8600 SND_BD_AMOEBA_TURNING_TO_GEM));
8603 void AmoebeWaechst(int x, int y)
8605 static unsigned int sound_delay = 0;
8606 static unsigned int sound_delay_value = 0;
8608 if (!MovDelay[x][y]) /* start new growing cycle */
8612 if (DelayReached(&sound_delay, sound_delay_value))
8614 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8615 sound_delay_value = 30;
8619 if (MovDelay[x][y]) /* wait some time before growing bigger */
8622 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8624 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8625 6 - MovDelay[x][y]);
8627 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8630 if (!MovDelay[x][y])
8632 Feld[x][y] = Store[x][y];
8634 TEST_DrawLevelField(x, y);
8639 void AmoebaDisappearing(int x, int y)
8641 static unsigned int sound_delay = 0;
8642 static unsigned int sound_delay_value = 0;
8644 if (!MovDelay[x][y]) /* start new shrinking cycle */
8648 if (DelayReached(&sound_delay, sound_delay_value))
8649 sound_delay_value = 30;
8652 if (MovDelay[x][y]) /* wait some time before shrinking */
8655 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8657 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8658 6 - MovDelay[x][y]);
8660 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8663 if (!MovDelay[x][y])
8665 Feld[x][y] = EL_EMPTY;
8666 TEST_DrawLevelField(x, y);
8668 /* don't let mole enter this field in this cycle;
8669 (give priority to objects falling to this field from above) */
8675 void AmoebeAbleger(int ax, int ay)
8678 int element = Feld[ax][ay];
8679 int graphic = el2img(element);
8680 int newax = ax, neway = ay;
8681 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8682 static int xy[4][2] =
8690 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8692 Feld[ax][ay] = EL_AMOEBA_DEAD;
8693 TEST_DrawLevelField(ax, ay);
8697 if (IS_ANIMATED(graphic))
8698 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8700 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8701 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8703 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8706 if (MovDelay[ax][ay])
8710 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8713 int x = ax + xy[start][0];
8714 int y = ay + xy[start][1];
8716 if (!IN_LEV_FIELD(x, y))
8719 if (IS_FREE(x, y) ||
8720 CAN_GROW_INTO(Feld[x][y]) ||
8721 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8722 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8728 if (newax == ax && neway == ay)
8731 else /* normal or "filled" (BD style) amoeba */
8734 boolean waiting_for_player = FALSE;
8736 for (i = 0; i < NUM_DIRECTIONS; i++)
8738 int j = (start + i) % 4;
8739 int x = ax + xy[j][0];
8740 int y = ay + xy[j][1];
8742 if (!IN_LEV_FIELD(x, y))
8745 if (IS_FREE(x, y) ||
8746 CAN_GROW_INTO(Feld[x][y]) ||
8747 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8748 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8754 else if (IS_PLAYER(x, y))
8755 waiting_for_player = TRUE;
8758 if (newax == ax && neway == ay) /* amoeba cannot grow */
8760 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8762 Feld[ax][ay] = EL_AMOEBA_DEAD;
8763 TEST_DrawLevelField(ax, ay);
8764 AmoebaCnt[AmoebaNr[ax][ay]]--;
8766 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8768 if (element == EL_AMOEBA_FULL)
8769 AmoebeUmwandeln(ax, ay);
8770 else if (element == EL_BD_AMOEBA)
8771 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8776 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8778 /* amoeba gets larger by growing in some direction */
8780 int new_group_nr = AmoebaNr[ax][ay];
8783 if (new_group_nr == 0)
8785 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8786 printf("AmoebeAbleger(): This should never happen!\n");
8791 AmoebaNr[newax][neway] = new_group_nr;
8792 AmoebaCnt[new_group_nr]++;
8793 AmoebaCnt2[new_group_nr]++;
8795 /* if amoeba touches other amoeba(s) after growing, unify them */
8796 AmoebenVereinigen(newax, neway);
8798 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8800 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8806 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8807 (neway == lev_fieldy - 1 && newax != ax))
8809 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8810 Store[newax][neway] = element;
8812 else if (neway == ay || element == EL_EMC_DRIPPER)
8814 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8816 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8820 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8821 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8822 Store[ax][ay] = EL_AMOEBA_DROP;
8823 ContinueMoving(ax, ay);
8827 TEST_DrawLevelField(newax, neway);
8830 void Life(int ax, int ay)
8834 int element = Feld[ax][ay];
8835 int graphic = el2img(element);
8836 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8838 boolean changed = FALSE;
8840 if (IS_ANIMATED(graphic))
8841 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8846 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8847 MovDelay[ax][ay] = life_time;
8849 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8852 if (MovDelay[ax][ay])
8856 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8858 int xx = ax+x1, yy = ay+y1;
8861 if (!IN_LEV_FIELD(xx, yy))
8864 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8866 int x = xx+x2, y = yy+y2;
8868 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8871 if (((Feld[x][y] == element ||
8872 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8874 (IS_FREE(x, y) && Stop[x][y]))
8878 if (xx == ax && yy == ay) /* field in the middle */
8880 if (nachbarn < life_parameter[0] ||
8881 nachbarn > life_parameter[1])
8883 Feld[xx][yy] = EL_EMPTY;
8885 TEST_DrawLevelField(xx, yy);
8886 Stop[xx][yy] = TRUE;
8890 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8891 { /* free border field */
8892 if (nachbarn >= life_parameter[2] &&
8893 nachbarn <= life_parameter[3])
8895 Feld[xx][yy] = element;
8896 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8898 TEST_DrawLevelField(xx, yy);
8899 Stop[xx][yy] = TRUE;
8906 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8907 SND_GAME_OF_LIFE_GROWING);
8910 static void InitRobotWheel(int x, int y)
8912 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8915 static void RunRobotWheel(int x, int y)
8917 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8920 static void StopRobotWheel(int x, int y)
8922 if (ZX == x && ZY == y)
8926 game.robot_wheel_active = FALSE;
8930 static void InitTimegateWheel(int x, int y)
8932 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8935 static void RunTimegateWheel(int x, int y)
8937 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8940 static void InitMagicBallDelay(int x, int y)
8942 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8945 static void ActivateMagicBall(int bx, int by)
8949 if (level.ball_random)
8951 int pos_border = RND(8); /* select one of the eight border elements */
8952 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8953 int xx = pos_content % 3;
8954 int yy = pos_content / 3;
8959 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8960 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8964 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8966 int xx = x - bx + 1;
8967 int yy = y - by + 1;
8969 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8970 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8974 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8977 void CheckExit(int x, int y)
8979 if (local_player->gems_still_needed > 0 ||
8980 local_player->sokobanfields_still_needed > 0 ||
8981 local_player->lights_still_needed > 0)
8983 int element = Feld[x][y];
8984 int graphic = el2img(element);
8986 if (IS_ANIMATED(graphic))
8987 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8992 if (AllPlayersGone) /* do not re-open exit door closed after last player */
8995 Feld[x][y] = EL_EXIT_OPENING;
8997 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9000 void CheckExitEM(int x, int y)
9002 if (local_player->gems_still_needed > 0 ||
9003 local_player->sokobanfields_still_needed > 0 ||
9004 local_player->lights_still_needed > 0)
9006 int element = Feld[x][y];
9007 int graphic = el2img(element);
9009 if (IS_ANIMATED(graphic))
9010 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9015 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9018 Feld[x][y] = EL_EM_EXIT_OPENING;
9020 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9023 void CheckExitSteel(int x, int y)
9025 if (local_player->gems_still_needed > 0 ||
9026 local_player->sokobanfields_still_needed > 0 ||
9027 local_player->lights_still_needed > 0)
9029 int element = Feld[x][y];
9030 int graphic = el2img(element);
9032 if (IS_ANIMATED(graphic))
9033 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9038 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9041 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9043 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9046 void CheckExitSteelEM(int x, int y)
9048 if (local_player->gems_still_needed > 0 ||
9049 local_player->sokobanfields_still_needed > 0 ||
9050 local_player->lights_still_needed > 0)
9052 int element = Feld[x][y];
9053 int graphic = el2img(element);
9055 if (IS_ANIMATED(graphic))
9056 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9061 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9064 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9066 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9069 void CheckExitSP(int x, int y)
9071 if (local_player->gems_still_needed > 0)
9073 int element = Feld[x][y];
9074 int graphic = el2img(element);
9076 if (IS_ANIMATED(graphic))
9077 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9082 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9085 Feld[x][y] = EL_SP_EXIT_OPENING;
9087 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9090 static void CloseAllOpenTimegates()
9094 SCAN_PLAYFIELD(x, y)
9096 int element = Feld[x][y];
9098 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9100 Feld[x][y] = EL_TIMEGATE_CLOSING;
9102 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9107 void DrawTwinkleOnField(int x, int y)
9109 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9112 if (Feld[x][y] == EL_BD_DIAMOND)
9115 if (MovDelay[x][y] == 0) /* next animation frame */
9116 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9118 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9122 DrawLevelElementAnimation(x, y, Feld[x][y]);
9124 if (MovDelay[x][y] != 0)
9126 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9127 10 - MovDelay[x][y]);
9129 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9134 void MauerWaechst(int x, int y)
9138 if (!MovDelay[x][y]) /* next animation frame */
9139 MovDelay[x][y] = 3 * delay;
9141 if (MovDelay[x][y]) /* wait some time before next frame */
9145 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9147 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9148 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9150 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9153 if (!MovDelay[x][y])
9155 if (MovDir[x][y] == MV_LEFT)
9157 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9158 TEST_DrawLevelField(x - 1, y);
9160 else if (MovDir[x][y] == MV_RIGHT)
9162 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9163 TEST_DrawLevelField(x + 1, y);
9165 else if (MovDir[x][y] == MV_UP)
9167 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9168 TEST_DrawLevelField(x, y - 1);
9172 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9173 TEST_DrawLevelField(x, y + 1);
9176 Feld[x][y] = Store[x][y];
9178 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9179 TEST_DrawLevelField(x, y);
9184 void MauerAbleger(int ax, int ay)
9186 int element = Feld[ax][ay];
9187 int graphic = el2img(element);
9188 boolean oben_frei = FALSE, unten_frei = FALSE;
9189 boolean links_frei = FALSE, rechts_frei = FALSE;
9190 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9191 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9192 boolean new_wall = FALSE;
9194 if (IS_ANIMATED(graphic))
9195 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9197 if (!MovDelay[ax][ay]) /* start building new wall */
9198 MovDelay[ax][ay] = 6;
9200 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9203 if (MovDelay[ax][ay])
9207 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9209 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9211 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9213 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9216 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9217 element == EL_EXPANDABLE_WALL_ANY)
9221 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9222 Store[ax][ay-1] = element;
9223 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9224 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9225 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9226 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9231 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9232 Store[ax][ay+1] = element;
9233 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9234 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9235 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9236 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9241 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9242 element == EL_EXPANDABLE_WALL_ANY ||
9243 element == EL_EXPANDABLE_WALL ||
9244 element == EL_BD_EXPANDABLE_WALL)
9248 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9249 Store[ax-1][ay] = element;
9250 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9251 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9252 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9253 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9259 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9260 Store[ax+1][ay] = element;
9261 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9262 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9263 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9264 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9269 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9270 TEST_DrawLevelField(ax, ay);
9272 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9274 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9275 unten_massiv = TRUE;
9276 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9277 links_massiv = TRUE;
9278 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9279 rechts_massiv = TRUE;
9281 if (((oben_massiv && unten_massiv) ||
9282 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9283 element == EL_EXPANDABLE_WALL) &&
9284 ((links_massiv && rechts_massiv) ||
9285 element == EL_EXPANDABLE_WALL_VERTICAL))
9286 Feld[ax][ay] = EL_WALL;
9289 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9292 void MauerAblegerStahl(int ax, int ay)
9294 int element = Feld[ax][ay];
9295 int graphic = el2img(element);
9296 boolean oben_frei = FALSE, unten_frei = FALSE;
9297 boolean links_frei = FALSE, rechts_frei = FALSE;
9298 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9299 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9300 boolean new_wall = FALSE;
9302 if (IS_ANIMATED(graphic))
9303 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9305 if (!MovDelay[ax][ay]) /* start building new wall */
9306 MovDelay[ax][ay] = 6;
9308 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9311 if (MovDelay[ax][ay])
9315 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9317 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9319 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9321 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9324 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9325 element == EL_EXPANDABLE_STEELWALL_ANY)
9329 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9330 Store[ax][ay-1] = element;
9331 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9332 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9333 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9334 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9339 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9340 Store[ax][ay+1] = element;
9341 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9342 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9343 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9344 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9349 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9350 element == EL_EXPANDABLE_STEELWALL_ANY)
9354 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9355 Store[ax-1][ay] = element;
9356 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9357 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9358 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9359 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9365 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9366 Store[ax+1][ay] = element;
9367 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9368 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9369 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9370 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9375 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9377 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9378 unten_massiv = TRUE;
9379 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9380 links_massiv = TRUE;
9381 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9382 rechts_massiv = TRUE;
9384 if (((oben_massiv && unten_massiv) ||
9385 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9386 ((links_massiv && rechts_massiv) ||
9387 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9388 Feld[ax][ay] = EL_STEELWALL;
9391 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9394 void CheckForDragon(int x, int y)
9397 boolean dragon_found = FALSE;
9398 static int xy[4][2] =
9406 for (i = 0; i < NUM_DIRECTIONS; i++)
9408 for (j = 0; j < 4; j++)
9410 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9412 if (IN_LEV_FIELD(xx, yy) &&
9413 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9415 if (Feld[xx][yy] == EL_DRAGON)
9416 dragon_found = TRUE;
9425 for (i = 0; i < NUM_DIRECTIONS; i++)
9427 for (j = 0; j < 3; j++)
9429 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9431 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9433 Feld[xx][yy] = EL_EMPTY;
9434 TEST_DrawLevelField(xx, yy);
9443 static void InitBuggyBase(int x, int y)
9445 int element = Feld[x][y];
9446 int activating_delay = FRAMES_PER_SECOND / 4;
9449 (element == EL_SP_BUGGY_BASE ?
9450 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9451 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9453 element == EL_SP_BUGGY_BASE_ACTIVE ?
9454 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9457 static void WarnBuggyBase(int x, int y)
9460 static int xy[4][2] =
9468 for (i = 0; i < NUM_DIRECTIONS; i++)
9470 int xx = x + xy[i][0];
9471 int yy = y + xy[i][1];
9473 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9475 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9482 static void InitTrap(int x, int y)
9484 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9487 static void ActivateTrap(int x, int y)
9489 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9492 static void ChangeActiveTrap(int x, int y)
9494 int graphic = IMG_TRAP_ACTIVE;
9496 /* if new animation frame was drawn, correct crumbled sand border */
9497 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9498 TEST_DrawLevelFieldCrumbled(x, y);
9501 static int getSpecialActionElement(int element, int number, int base_element)
9503 return (element != EL_EMPTY ? element :
9504 number != -1 ? base_element + number - 1 :
9508 static int getModifiedActionNumber(int value_old, int operator, int operand,
9509 int value_min, int value_max)
9511 int value_new = (operator == CA_MODE_SET ? operand :
9512 operator == CA_MODE_ADD ? value_old + operand :
9513 operator == CA_MODE_SUBTRACT ? value_old - operand :
9514 operator == CA_MODE_MULTIPLY ? value_old * operand :
9515 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9516 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9519 return (value_new < value_min ? value_min :
9520 value_new > value_max ? value_max :
9524 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9526 struct ElementInfo *ei = &element_info[element];
9527 struct ElementChangeInfo *change = &ei->change_page[page];
9528 int target_element = change->target_element;
9529 int action_type = change->action_type;
9530 int action_mode = change->action_mode;
9531 int action_arg = change->action_arg;
9532 int action_element = change->action_element;
9535 if (!change->has_action)
9538 /* ---------- determine action paramater values -------------------------- */
9540 int level_time_value =
9541 (level.time > 0 ? TimeLeft :
9544 int action_arg_element_raw =
9545 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9546 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9547 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9548 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9549 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9550 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9551 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9553 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9555 int action_arg_direction =
9556 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9557 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9558 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9559 change->actual_trigger_side :
9560 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9561 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9564 int action_arg_number_min =
9565 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9568 int action_arg_number_max =
9569 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9570 action_type == CA_SET_LEVEL_GEMS ? 999 :
9571 action_type == CA_SET_LEVEL_TIME ? 9999 :
9572 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9573 action_type == CA_SET_CE_VALUE ? 9999 :
9574 action_type == CA_SET_CE_SCORE ? 9999 :
9577 int action_arg_number_reset =
9578 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9579 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9580 action_type == CA_SET_LEVEL_TIME ? level.time :
9581 action_type == CA_SET_LEVEL_SCORE ? 0 :
9582 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9583 action_type == CA_SET_CE_SCORE ? 0 :
9586 int action_arg_number =
9587 (action_arg <= CA_ARG_MAX ? action_arg :
9588 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9589 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9590 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9591 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9592 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9593 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9594 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9595 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9596 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9597 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9598 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9599 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9600 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9601 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9602 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9603 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9604 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9605 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9606 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9607 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9608 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9611 int action_arg_number_old =
9612 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9613 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9614 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9615 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9616 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9619 int action_arg_number_new =
9620 getModifiedActionNumber(action_arg_number_old,
9621 action_mode, action_arg_number,
9622 action_arg_number_min, action_arg_number_max);
9624 int trigger_player_bits =
9625 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9626 change->actual_trigger_player_bits : change->trigger_player);
9628 int action_arg_player_bits =
9629 (action_arg >= CA_ARG_PLAYER_1 &&
9630 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9631 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9632 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9635 /* ---------- execute action -------------------------------------------- */
9637 switch (action_type)
9644 /* ---------- level actions ------------------------------------------- */
9646 case CA_RESTART_LEVEL:
9648 game.restart_level = TRUE;
9653 case CA_SHOW_ENVELOPE:
9655 int element = getSpecialActionElement(action_arg_element,
9656 action_arg_number, EL_ENVELOPE_1);
9658 if (IS_ENVELOPE(element))
9659 local_player->show_envelope = element;
9664 case CA_SET_LEVEL_TIME:
9666 if (level.time > 0) /* only modify limited time value */
9668 TimeLeft = action_arg_number_new;
9670 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9672 DisplayGameControlValues();
9674 if (!TimeLeft && setup.time_limit)
9675 for (i = 0; i < MAX_PLAYERS; i++)
9676 KillPlayer(&stored_player[i]);
9682 case CA_SET_LEVEL_SCORE:
9684 local_player->score = action_arg_number_new;
9686 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9688 DisplayGameControlValues();
9693 case CA_SET_LEVEL_GEMS:
9695 local_player->gems_still_needed = action_arg_number_new;
9697 game.snapshot.collected_item = TRUE;
9699 game_panel_controls[GAME_PANEL_GEMS].value =
9700 local_player->gems_still_needed;
9702 DisplayGameControlValues();
9707 case CA_SET_LEVEL_WIND:
9709 game.wind_direction = action_arg_direction;
9714 case CA_SET_LEVEL_RANDOM_SEED:
9716 /* ensure that setting a new random seed while playing is predictable */
9717 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9722 /* ---------- player actions ------------------------------------------ */
9724 case CA_MOVE_PLAYER:
9726 /* automatically move to the next field in specified direction */
9727 for (i = 0; i < MAX_PLAYERS; i++)
9728 if (trigger_player_bits & (1 << i))
9729 stored_player[i].programmed_action = action_arg_direction;
9734 case CA_EXIT_PLAYER:
9736 for (i = 0; i < MAX_PLAYERS; i++)
9737 if (action_arg_player_bits & (1 << i))
9738 PlayerWins(&stored_player[i]);
9743 case CA_KILL_PLAYER:
9745 for (i = 0; i < MAX_PLAYERS; i++)
9746 if (action_arg_player_bits & (1 << i))
9747 KillPlayer(&stored_player[i]);
9752 case CA_SET_PLAYER_KEYS:
9754 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9755 int element = getSpecialActionElement(action_arg_element,
9756 action_arg_number, EL_KEY_1);
9758 if (IS_KEY(element))
9760 for (i = 0; i < MAX_PLAYERS; i++)
9762 if (trigger_player_bits & (1 << i))
9764 stored_player[i].key[KEY_NR(element)] = key_state;
9766 DrawGameDoorValues();
9774 case CA_SET_PLAYER_SPEED:
9776 for (i = 0; i < MAX_PLAYERS; i++)
9778 if (trigger_player_bits & (1 << i))
9780 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9782 if (action_arg == CA_ARG_SPEED_FASTER &&
9783 stored_player[i].cannot_move)
9785 action_arg_number = STEPSIZE_VERY_SLOW;
9787 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9788 action_arg == CA_ARG_SPEED_FASTER)
9790 action_arg_number = 2;
9791 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9794 else if (action_arg == CA_ARG_NUMBER_RESET)
9796 action_arg_number = level.initial_player_stepsize[i];
9800 getModifiedActionNumber(move_stepsize,
9803 action_arg_number_min,
9804 action_arg_number_max);
9806 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9813 case CA_SET_PLAYER_SHIELD:
9815 for (i = 0; i < MAX_PLAYERS; i++)
9817 if (trigger_player_bits & (1 << i))
9819 if (action_arg == CA_ARG_SHIELD_OFF)
9821 stored_player[i].shield_normal_time_left = 0;
9822 stored_player[i].shield_deadly_time_left = 0;
9824 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9826 stored_player[i].shield_normal_time_left = 999999;
9828 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9830 stored_player[i].shield_normal_time_left = 999999;
9831 stored_player[i].shield_deadly_time_left = 999999;
9839 case CA_SET_PLAYER_GRAVITY:
9841 for (i = 0; i < MAX_PLAYERS; i++)
9843 if (trigger_player_bits & (1 << i))
9845 stored_player[i].gravity =
9846 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9847 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9848 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9849 stored_player[i].gravity);
9856 case CA_SET_PLAYER_ARTWORK:
9858 for (i = 0; i < MAX_PLAYERS; i++)
9860 if (trigger_player_bits & (1 << i))
9862 int artwork_element = action_arg_element;
9864 if (action_arg == CA_ARG_ELEMENT_RESET)
9866 (level.use_artwork_element[i] ? level.artwork_element[i] :
9867 stored_player[i].element_nr);
9869 if (stored_player[i].artwork_element != artwork_element)
9870 stored_player[i].Frame = 0;
9872 stored_player[i].artwork_element = artwork_element;
9874 SetPlayerWaiting(&stored_player[i], FALSE);
9876 /* set number of special actions for bored and sleeping animation */
9877 stored_player[i].num_special_action_bored =
9878 get_num_special_action(artwork_element,
9879 ACTION_BORING_1, ACTION_BORING_LAST);
9880 stored_player[i].num_special_action_sleeping =
9881 get_num_special_action(artwork_element,
9882 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9889 case CA_SET_PLAYER_INVENTORY:
9891 for (i = 0; i < MAX_PLAYERS; i++)
9893 struct PlayerInfo *player = &stored_player[i];
9896 if (trigger_player_bits & (1 << i))
9898 int inventory_element = action_arg_element;
9900 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9901 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9902 action_arg == CA_ARG_ELEMENT_ACTION)
9904 int element = inventory_element;
9905 int collect_count = element_info[element].collect_count_initial;
9907 if (!IS_CUSTOM_ELEMENT(element))
9910 if (collect_count == 0)
9911 player->inventory_infinite_element = element;
9913 for (k = 0; k < collect_count; k++)
9914 if (player->inventory_size < MAX_INVENTORY_SIZE)
9915 player->inventory_element[player->inventory_size++] =
9918 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9919 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9920 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9922 if (player->inventory_infinite_element != EL_UNDEFINED &&
9923 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9924 action_arg_element_raw))
9925 player->inventory_infinite_element = EL_UNDEFINED;
9927 for (k = 0, j = 0; j < player->inventory_size; j++)
9929 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9930 action_arg_element_raw))
9931 player->inventory_element[k++] = player->inventory_element[j];
9934 player->inventory_size = k;
9936 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9938 if (player->inventory_size > 0)
9940 for (j = 0; j < player->inventory_size - 1; j++)
9941 player->inventory_element[j] = player->inventory_element[j + 1];
9943 player->inventory_size--;
9946 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9948 if (player->inventory_size > 0)
9949 player->inventory_size--;
9951 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9953 player->inventory_infinite_element = EL_UNDEFINED;
9954 player->inventory_size = 0;
9956 else if (action_arg == CA_ARG_INVENTORY_RESET)
9958 player->inventory_infinite_element = EL_UNDEFINED;
9959 player->inventory_size = 0;
9961 if (level.use_initial_inventory[i])
9963 for (j = 0; j < level.initial_inventory_size[i]; j++)
9965 int element = level.initial_inventory_content[i][j];
9966 int collect_count = element_info[element].collect_count_initial;
9968 if (!IS_CUSTOM_ELEMENT(element))
9971 if (collect_count == 0)
9972 player->inventory_infinite_element = element;
9974 for (k = 0; k < collect_count; k++)
9975 if (player->inventory_size < MAX_INVENTORY_SIZE)
9976 player->inventory_element[player->inventory_size++] =
9987 /* ---------- CE actions ---------------------------------------------- */
9989 case CA_SET_CE_VALUE:
9991 int last_ce_value = CustomValue[x][y];
9993 CustomValue[x][y] = action_arg_number_new;
9995 if (CustomValue[x][y] != last_ce_value)
9997 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9998 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10000 if (CustomValue[x][y] == 0)
10002 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10003 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10010 case CA_SET_CE_SCORE:
10012 int last_ce_score = ei->collect_score;
10014 ei->collect_score = action_arg_number_new;
10016 if (ei->collect_score != last_ce_score)
10018 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10019 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10021 if (ei->collect_score == 0)
10025 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10026 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10029 This is a very special case that seems to be a mixture between
10030 CheckElementChange() and CheckTriggeredElementChange(): while
10031 the first one only affects single elements that are triggered
10032 directly, the second one affects multiple elements in the playfield
10033 that are triggered indirectly by another element. This is a third
10034 case: Changing the CE score always affects multiple identical CEs,
10035 so every affected CE must be checked, not only the single CE for
10036 which the CE score was changed in the first place (as every instance
10037 of that CE shares the same CE score, and therefore also can change)!
10039 SCAN_PLAYFIELD(xx, yy)
10041 if (Feld[xx][yy] == element)
10042 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10043 CE_SCORE_GETS_ZERO);
10051 case CA_SET_CE_ARTWORK:
10053 int artwork_element = action_arg_element;
10054 boolean reset_frame = FALSE;
10057 if (action_arg == CA_ARG_ELEMENT_RESET)
10058 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10061 if (ei->gfx_element != artwork_element)
10062 reset_frame = TRUE;
10064 ei->gfx_element = artwork_element;
10066 SCAN_PLAYFIELD(xx, yy)
10068 if (Feld[xx][yy] == element)
10072 ResetGfxAnimation(xx, yy);
10073 ResetRandomAnimationValue(xx, yy);
10076 TEST_DrawLevelField(xx, yy);
10083 /* ---------- engine actions ------------------------------------------ */
10085 case CA_SET_ENGINE_SCAN_MODE:
10087 InitPlayfieldScanMode(action_arg);
10097 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10099 int old_element = Feld[x][y];
10100 int new_element = GetElementFromGroupElement(element);
10101 int previous_move_direction = MovDir[x][y];
10102 int last_ce_value = CustomValue[x][y];
10103 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10104 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10105 boolean add_player_onto_element = (new_element_is_player &&
10106 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10107 IS_WALKABLE(old_element));
10109 if (!add_player_onto_element)
10111 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10112 RemoveMovingField(x, y);
10116 Feld[x][y] = new_element;
10118 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10119 MovDir[x][y] = previous_move_direction;
10121 if (element_info[new_element].use_last_ce_value)
10122 CustomValue[x][y] = last_ce_value;
10124 InitField_WithBug1(x, y, FALSE);
10126 new_element = Feld[x][y]; /* element may have changed */
10128 ResetGfxAnimation(x, y);
10129 ResetRandomAnimationValue(x, y);
10131 TEST_DrawLevelField(x, y);
10133 if (GFX_CRUMBLED(new_element))
10134 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10137 /* check if element under the player changes from accessible to unaccessible
10138 (needed for special case of dropping element which then changes) */
10139 /* (must be checked after creating new element for walkable group elements) */
10140 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10141 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10148 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10149 if (new_element_is_player)
10150 RelocatePlayer(x, y, new_element);
10153 ChangeCount[x][y]++; /* count number of changes in the same frame */
10155 TestIfBadThingTouchesPlayer(x, y);
10156 TestIfPlayerTouchesCustomElement(x, y);
10157 TestIfElementTouchesCustomElement(x, y);
10160 static void CreateField(int x, int y, int element)
10162 CreateFieldExt(x, y, element, FALSE);
10165 static void CreateElementFromChange(int x, int y, int element)
10167 element = GET_VALID_RUNTIME_ELEMENT(element);
10169 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10171 int old_element = Feld[x][y];
10173 /* prevent changed element from moving in same engine frame
10174 unless both old and new element can either fall or move */
10175 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10176 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10180 CreateFieldExt(x, y, element, TRUE);
10183 static boolean ChangeElement(int x, int y, int element, int page)
10185 struct ElementInfo *ei = &element_info[element];
10186 struct ElementChangeInfo *change = &ei->change_page[page];
10187 int ce_value = CustomValue[x][y];
10188 int ce_score = ei->collect_score;
10189 int target_element;
10190 int old_element = Feld[x][y];
10192 /* always use default change event to prevent running into a loop */
10193 if (ChangeEvent[x][y] == -1)
10194 ChangeEvent[x][y] = CE_DELAY;
10196 if (ChangeEvent[x][y] == CE_DELAY)
10198 /* reset actual trigger element, trigger player and action element */
10199 change->actual_trigger_element = EL_EMPTY;
10200 change->actual_trigger_player = EL_EMPTY;
10201 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10202 change->actual_trigger_side = CH_SIDE_NONE;
10203 change->actual_trigger_ce_value = 0;
10204 change->actual_trigger_ce_score = 0;
10207 /* do not change elements more than a specified maximum number of changes */
10208 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10211 ChangeCount[x][y]++; /* count number of changes in the same frame */
10213 if (change->explode)
10220 if (change->use_target_content)
10222 boolean complete_replace = TRUE;
10223 boolean can_replace[3][3];
10226 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10229 boolean is_walkable;
10230 boolean is_diggable;
10231 boolean is_collectible;
10232 boolean is_removable;
10233 boolean is_destructible;
10234 int ex = x + xx - 1;
10235 int ey = y + yy - 1;
10236 int content_element = change->target_content.e[xx][yy];
10239 can_replace[xx][yy] = TRUE;
10241 if (ex == x && ey == y) /* do not check changing element itself */
10244 if (content_element == EL_EMPTY_SPACE)
10246 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10251 if (!IN_LEV_FIELD(ex, ey))
10253 can_replace[xx][yy] = FALSE;
10254 complete_replace = FALSE;
10261 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10262 e = MovingOrBlocked2Element(ex, ey);
10264 is_empty = (IS_FREE(ex, ey) ||
10265 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10267 is_walkable = (is_empty || IS_WALKABLE(e));
10268 is_diggable = (is_empty || IS_DIGGABLE(e));
10269 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10270 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10271 is_removable = (is_diggable || is_collectible);
10273 can_replace[xx][yy] =
10274 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10275 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10276 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10277 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10278 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10279 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10280 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10282 if (!can_replace[xx][yy])
10283 complete_replace = FALSE;
10286 if (!change->only_if_complete || complete_replace)
10288 boolean something_has_changed = FALSE;
10290 if (change->only_if_complete && change->use_random_replace &&
10291 RND(100) < change->random_percentage)
10294 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10296 int ex = x + xx - 1;
10297 int ey = y + yy - 1;
10298 int content_element;
10300 if (can_replace[xx][yy] && (!change->use_random_replace ||
10301 RND(100) < change->random_percentage))
10303 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10304 RemoveMovingField(ex, ey);
10306 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10308 content_element = change->target_content.e[xx][yy];
10309 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10310 ce_value, ce_score);
10312 CreateElementFromChange(ex, ey, target_element);
10314 something_has_changed = TRUE;
10316 /* for symmetry reasons, freeze newly created border elements */
10317 if (ex != x || ey != y)
10318 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10322 if (something_has_changed)
10324 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10325 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10331 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10332 ce_value, ce_score);
10334 if (element == EL_DIAGONAL_GROWING ||
10335 element == EL_DIAGONAL_SHRINKING)
10337 target_element = Store[x][y];
10339 Store[x][y] = EL_EMPTY;
10342 CreateElementFromChange(x, y, target_element);
10344 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10345 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10348 /* this uses direct change before indirect change */
10349 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10354 static void HandleElementChange(int x, int y, int page)
10356 int element = MovingOrBlocked2Element(x, y);
10357 struct ElementInfo *ei = &element_info[element];
10358 struct ElementChangeInfo *change = &ei->change_page[page];
10359 boolean handle_action_before_change = FALSE;
10362 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10363 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10366 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10367 x, y, element, element_info[element].token_name);
10368 printf("HandleElementChange(): This should never happen!\n");
10373 /* this can happen with classic bombs on walkable, changing elements */
10374 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10379 if (ChangeDelay[x][y] == 0) /* initialize element change */
10381 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10383 if (change->can_change)
10385 /* !!! not clear why graphic animation should be reset at all here !!! */
10386 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10387 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10390 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10392 When using an animation frame delay of 1 (this only happens with
10393 "sp_zonk.moving.left/right" in the classic graphics), the default
10394 (non-moving) animation shows wrong animation frames (while the
10395 moving animation, like "sp_zonk.moving.left/right", is correct,
10396 so this graphical bug never shows up with the classic graphics).
10397 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10398 be drawn instead of the correct frames 0,1,2,3. This is caused by
10399 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10400 an element change: First when the change delay ("ChangeDelay[][]")
10401 counter has reached zero after decrementing, then a second time in
10402 the next frame (after "GfxFrame[][]" was already incremented) when
10403 "ChangeDelay[][]" is reset to the initial delay value again.
10405 This causes frame 0 to be drawn twice, while the last frame won't
10406 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10408 As some animations may already be cleverly designed around this bug
10409 (at least the "Snake Bite" snake tail animation does this), it cannot
10410 simply be fixed here without breaking such existing animations.
10411 Unfortunately, it cannot easily be detected if a graphics set was
10412 designed "before" or "after" the bug was fixed. As a workaround,
10413 a new graphics set option "game.graphics_engine_version" was added
10414 to be able to specify the game's major release version for which the
10415 graphics set was designed, which can then be used to decide if the
10416 bugfix should be used (version 4 and above) or not (version 3 or
10417 below, or if no version was specified at all, as with old sets).
10419 (The wrong/fixed animation frames can be tested with the test level set
10420 "test_gfxframe" and level "000", which contains a specially prepared
10421 custom element at level position (x/y) == (11/9) which uses the zonk
10422 animation mentioned above. Using "game.graphics_engine_version: 4"
10423 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10424 This can also be seen from the debug output for this test element.)
10427 /* when a custom element is about to change (for example by change delay),
10428 do not reset graphic animation when the custom element is moving */
10429 if (game.graphics_engine_version < 4 &&
10432 ResetGfxAnimation(x, y);
10433 ResetRandomAnimationValue(x, y);
10436 if (change->pre_change_function)
10437 change->pre_change_function(x, y);
10441 ChangeDelay[x][y]--;
10443 if (ChangeDelay[x][y] != 0) /* continue element change */
10445 if (change->can_change)
10447 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10449 if (IS_ANIMATED(graphic))
10450 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10452 if (change->change_function)
10453 change->change_function(x, y);
10456 else /* finish element change */
10458 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10460 page = ChangePage[x][y];
10461 ChangePage[x][y] = -1;
10463 change = &ei->change_page[page];
10466 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10468 ChangeDelay[x][y] = 1; /* try change after next move step */
10469 ChangePage[x][y] = page; /* remember page to use for change */
10474 /* special case: set new level random seed before changing element */
10475 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10476 handle_action_before_change = TRUE;
10478 if (change->has_action && handle_action_before_change)
10479 ExecuteCustomElementAction(x, y, element, page);
10481 if (change->can_change)
10483 if (ChangeElement(x, y, element, page))
10485 if (change->post_change_function)
10486 change->post_change_function(x, y);
10490 if (change->has_action && !handle_action_before_change)
10491 ExecuteCustomElementAction(x, y, element, page);
10495 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10496 int trigger_element,
10498 int trigger_player,
10502 boolean change_done_any = FALSE;
10503 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10506 if (!(trigger_events[trigger_element][trigger_event]))
10509 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10511 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10513 int element = EL_CUSTOM_START + i;
10514 boolean change_done = FALSE;
10517 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10518 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10521 for (p = 0; p < element_info[element].num_change_pages; p++)
10523 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10525 if (change->can_change_or_has_action &&
10526 change->has_event[trigger_event] &&
10527 change->trigger_side & trigger_side &&
10528 change->trigger_player & trigger_player &&
10529 change->trigger_page & trigger_page_bits &&
10530 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10532 change->actual_trigger_element = trigger_element;
10533 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10534 change->actual_trigger_player_bits = trigger_player;
10535 change->actual_trigger_side = trigger_side;
10536 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10537 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10539 if ((change->can_change && !change_done) || change->has_action)
10543 SCAN_PLAYFIELD(x, y)
10545 if (Feld[x][y] == element)
10547 if (change->can_change && !change_done)
10549 /* if element already changed in this frame, not only prevent
10550 another element change (checked in ChangeElement()), but
10551 also prevent additional element actions for this element */
10553 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10554 !level.use_action_after_change_bug)
10557 ChangeDelay[x][y] = 1;
10558 ChangeEvent[x][y] = trigger_event;
10560 HandleElementChange(x, y, p);
10562 else if (change->has_action)
10564 /* if element already changed in this frame, not only prevent
10565 another element change (checked in ChangeElement()), but
10566 also prevent additional element actions for this element */
10568 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10569 !level.use_action_after_change_bug)
10572 ExecuteCustomElementAction(x, y, element, p);
10573 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10578 if (change->can_change)
10580 change_done = TRUE;
10581 change_done_any = TRUE;
10588 RECURSION_LOOP_DETECTION_END();
10590 return change_done_any;
10593 static boolean CheckElementChangeExt(int x, int y,
10595 int trigger_element,
10597 int trigger_player,
10600 boolean change_done = FALSE;
10603 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10604 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10607 if (Feld[x][y] == EL_BLOCKED)
10609 Blocked2Moving(x, y, &x, &y);
10610 element = Feld[x][y];
10613 /* check if element has already changed or is about to change after moving */
10614 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10615 Feld[x][y] != element) ||
10617 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10618 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10619 ChangePage[x][y] != -1)))
10622 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10624 for (p = 0; p < element_info[element].num_change_pages; p++)
10626 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10628 /* check trigger element for all events where the element that is checked
10629 for changing interacts with a directly adjacent element -- this is
10630 different to element changes that affect other elements to change on the
10631 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10632 boolean check_trigger_element =
10633 (trigger_event == CE_TOUCHING_X ||
10634 trigger_event == CE_HITTING_X ||
10635 trigger_event == CE_HIT_BY_X ||
10636 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10638 if (change->can_change_or_has_action &&
10639 change->has_event[trigger_event] &&
10640 change->trigger_side & trigger_side &&
10641 change->trigger_player & trigger_player &&
10642 (!check_trigger_element ||
10643 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10645 change->actual_trigger_element = trigger_element;
10646 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10647 change->actual_trigger_player_bits = trigger_player;
10648 change->actual_trigger_side = trigger_side;
10649 change->actual_trigger_ce_value = CustomValue[x][y];
10650 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10652 /* special case: trigger element not at (x,y) position for some events */
10653 if (check_trigger_element)
10665 { 0, 0 }, { 0, 0 }, { 0, 0 },
10669 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10670 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10672 change->actual_trigger_ce_value = CustomValue[xx][yy];
10673 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10676 if (change->can_change && !change_done)
10678 ChangeDelay[x][y] = 1;
10679 ChangeEvent[x][y] = trigger_event;
10681 HandleElementChange(x, y, p);
10683 change_done = TRUE;
10685 else if (change->has_action)
10687 ExecuteCustomElementAction(x, y, element, p);
10688 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10693 RECURSION_LOOP_DETECTION_END();
10695 return change_done;
10698 static void PlayPlayerSound(struct PlayerInfo *player)
10700 int jx = player->jx, jy = player->jy;
10701 int sound_element = player->artwork_element;
10702 int last_action = player->last_action_waiting;
10703 int action = player->action_waiting;
10705 if (player->is_waiting)
10707 if (action != last_action)
10708 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10710 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10714 if (action != last_action)
10715 StopSound(element_info[sound_element].sound[last_action]);
10717 if (last_action == ACTION_SLEEPING)
10718 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10722 static void PlayAllPlayersSound()
10726 for (i = 0; i < MAX_PLAYERS; i++)
10727 if (stored_player[i].active)
10728 PlayPlayerSound(&stored_player[i]);
10731 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10733 boolean last_waiting = player->is_waiting;
10734 int move_dir = player->MovDir;
10736 player->dir_waiting = move_dir;
10737 player->last_action_waiting = player->action_waiting;
10741 if (!last_waiting) /* not waiting -> waiting */
10743 player->is_waiting = TRUE;
10745 player->frame_counter_bored =
10747 game.player_boring_delay_fixed +
10748 GetSimpleRandom(game.player_boring_delay_random);
10749 player->frame_counter_sleeping =
10751 game.player_sleeping_delay_fixed +
10752 GetSimpleRandom(game.player_sleeping_delay_random);
10754 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10757 if (game.player_sleeping_delay_fixed +
10758 game.player_sleeping_delay_random > 0 &&
10759 player->anim_delay_counter == 0 &&
10760 player->post_delay_counter == 0 &&
10761 FrameCounter >= player->frame_counter_sleeping)
10762 player->is_sleeping = TRUE;
10763 else if (game.player_boring_delay_fixed +
10764 game.player_boring_delay_random > 0 &&
10765 FrameCounter >= player->frame_counter_bored)
10766 player->is_bored = TRUE;
10768 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10769 player->is_bored ? ACTION_BORING :
10772 if (player->is_sleeping && player->use_murphy)
10774 /* special case for sleeping Murphy when leaning against non-free tile */
10776 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10777 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10778 !IS_MOVING(player->jx - 1, player->jy)))
10779 move_dir = MV_LEFT;
10780 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10781 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10782 !IS_MOVING(player->jx + 1, player->jy)))
10783 move_dir = MV_RIGHT;
10785 player->is_sleeping = FALSE;
10787 player->dir_waiting = move_dir;
10790 if (player->is_sleeping)
10792 if (player->num_special_action_sleeping > 0)
10794 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10796 int last_special_action = player->special_action_sleeping;
10797 int num_special_action = player->num_special_action_sleeping;
10798 int special_action =
10799 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10800 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10801 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10802 last_special_action + 1 : ACTION_SLEEPING);
10803 int special_graphic =
10804 el_act_dir2img(player->artwork_element, special_action, move_dir);
10806 player->anim_delay_counter =
10807 graphic_info[special_graphic].anim_delay_fixed +
10808 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10809 player->post_delay_counter =
10810 graphic_info[special_graphic].post_delay_fixed +
10811 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10813 player->special_action_sleeping = special_action;
10816 if (player->anim_delay_counter > 0)
10818 player->action_waiting = player->special_action_sleeping;
10819 player->anim_delay_counter--;
10821 else if (player->post_delay_counter > 0)
10823 player->post_delay_counter--;
10827 else if (player->is_bored)
10829 if (player->num_special_action_bored > 0)
10831 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10833 int special_action =
10834 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10835 int special_graphic =
10836 el_act_dir2img(player->artwork_element, special_action, move_dir);
10838 player->anim_delay_counter =
10839 graphic_info[special_graphic].anim_delay_fixed +
10840 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10841 player->post_delay_counter =
10842 graphic_info[special_graphic].post_delay_fixed +
10843 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10845 player->special_action_bored = special_action;
10848 if (player->anim_delay_counter > 0)
10850 player->action_waiting = player->special_action_bored;
10851 player->anim_delay_counter--;
10853 else if (player->post_delay_counter > 0)
10855 player->post_delay_counter--;
10860 else if (last_waiting) /* waiting -> not waiting */
10862 player->is_waiting = FALSE;
10863 player->is_bored = FALSE;
10864 player->is_sleeping = FALSE;
10866 player->frame_counter_bored = -1;
10867 player->frame_counter_sleeping = -1;
10869 player->anim_delay_counter = 0;
10870 player->post_delay_counter = 0;
10872 player->dir_waiting = player->MovDir;
10873 player->action_waiting = ACTION_DEFAULT;
10875 player->special_action_bored = ACTION_DEFAULT;
10876 player->special_action_sleeping = ACTION_DEFAULT;
10880 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10882 if ((!player->is_moving && player->was_moving) ||
10883 (player->MovPos == 0 && player->was_moving) ||
10884 (player->is_snapping && !player->was_snapping) ||
10885 (player->is_dropping && !player->was_dropping))
10887 if (!CheckSaveEngineSnapshotToList())
10890 player->was_moving = FALSE;
10891 player->was_snapping = TRUE;
10892 player->was_dropping = TRUE;
10896 if (player->is_moving)
10897 player->was_moving = TRUE;
10899 if (!player->is_snapping)
10900 player->was_snapping = FALSE;
10902 if (!player->is_dropping)
10903 player->was_dropping = FALSE;
10907 static void CheckSingleStepMode(struct PlayerInfo *player)
10909 if (tape.single_step && tape.recording && !tape.pausing)
10911 /* as it is called "single step mode", just return to pause mode when the
10912 player stopped moving after one tile (or never starts moving at all) */
10913 if (!player->is_moving &&
10914 !player->is_pushing &&
10915 !player->is_dropping_pressed)
10917 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10918 SnapField(player, 0, 0); /* stop snapping */
10922 CheckSaveEngineSnapshot(player);
10925 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10927 int left = player_action & JOY_LEFT;
10928 int right = player_action & JOY_RIGHT;
10929 int up = player_action & JOY_UP;
10930 int down = player_action & JOY_DOWN;
10931 int button1 = player_action & JOY_BUTTON_1;
10932 int button2 = player_action & JOY_BUTTON_2;
10933 int dx = (left ? -1 : right ? 1 : 0);
10934 int dy = (up ? -1 : down ? 1 : 0);
10936 if (!player->active || tape.pausing)
10942 SnapField(player, dx, dy);
10946 DropElement(player);
10948 MovePlayer(player, dx, dy);
10951 CheckSingleStepMode(player);
10953 SetPlayerWaiting(player, FALSE);
10955 return player_action;
10959 /* no actions for this player (no input at player's configured device) */
10961 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10962 SnapField(player, 0, 0);
10963 CheckGravityMovementWhenNotMoving(player);
10965 if (player->MovPos == 0)
10966 SetPlayerWaiting(player, TRUE);
10968 if (player->MovPos == 0) /* needed for tape.playing */
10969 player->is_moving = FALSE;
10971 player->is_dropping = FALSE;
10972 player->is_dropping_pressed = FALSE;
10973 player->drop_pressed_delay = 0;
10975 CheckSingleStepMode(player);
10981 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
10984 if (!tape.use_mouse)
10987 mouse_action->lx = tape_action[TAPE_ACTION_LX];
10988 mouse_action->ly = tape_action[TAPE_ACTION_LY];
10989 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
10992 static void SetTapeActionFromMouseAction(byte *tape_action,
10993 struct MouseActionInfo *mouse_action)
10995 if (!tape.use_mouse)
10998 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
10999 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11000 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11003 static void CheckLevelTime()
11007 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11008 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11010 if (level.native_em_level->lev->home == 0) /* all players at home */
11012 PlayerWins(local_player);
11014 AllPlayersGone = TRUE;
11016 level.native_em_level->lev->home = -1;
11019 if (level.native_em_level->ply[0]->alive == 0 &&
11020 level.native_em_level->ply[1]->alive == 0 &&
11021 level.native_em_level->ply[2]->alive == 0 &&
11022 level.native_em_level->ply[3]->alive == 0) /* all dead */
11023 AllPlayersGone = TRUE;
11025 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11027 if (game_sp.LevelSolved &&
11028 !game_sp.GameOver) /* game won */
11030 PlayerWins(local_player);
11032 game_sp.GameOver = TRUE;
11034 AllPlayersGone = TRUE;
11037 if (game_sp.GameOver) /* game lost */
11038 AllPlayersGone = TRUE;
11040 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11042 if (game_mm.level_solved &&
11043 !game_mm.game_over) /* game won */
11045 PlayerWins(local_player);
11047 game_mm.game_over = TRUE;
11049 AllPlayersGone = TRUE;
11052 if (game_mm.game_over) /* game lost */
11053 AllPlayersGone = TRUE;
11056 if (TimeFrames >= FRAMES_PER_SECOND)
11061 for (i = 0; i < MAX_PLAYERS; i++)
11063 struct PlayerInfo *player = &stored_player[i];
11065 if (SHIELD_ON(player))
11067 player->shield_normal_time_left--;
11069 if (player->shield_deadly_time_left > 0)
11070 player->shield_deadly_time_left--;
11074 if (!local_player->LevelSolved && !level.use_step_counter)
11082 if (TimeLeft <= 10 && setup.time_limit)
11083 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11085 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11086 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11088 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11090 if (!TimeLeft && setup.time_limit)
11092 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11093 level.native_em_level->lev->killed_out_of_time = TRUE;
11095 for (i = 0; i < MAX_PLAYERS; i++)
11096 KillPlayer(&stored_player[i]);
11099 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11101 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11104 level.native_em_level->lev->time =
11105 (game.no_time_limit ? TimePlayed : TimeLeft);
11108 if (tape.recording || tape.playing)
11109 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11112 if (tape.recording || tape.playing)
11113 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11115 UpdateAndDisplayGameControlValues();
11118 void AdvanceFrameAndPlayerCounters(int player_nr)
11122 /* advance frame counters (global frame counter and time frame counter) */
11126 /* advance player counters (counters for move delay, move animation etc.) */
11127 for (i = 0; i < MAX_PLAYERS; i++)
11129 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11130 int move_delay_value = stored_player[i].move_delay_value;
11131 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11133 if (!advance_player_counters) /* not all players may be affected */
11136 if (move_frames == 0) /* less than one move per game frame */
11138 int stepsize = TILEX / move_delay_value;
11139 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11140 int count = (stored_player[i].is_moving ?
11141 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11143 if (count % delay == 0)
11147 stored_player[i].Frame += move_frames;
11149 if (stored_player[i].MovPos != 0)
11150 stored_player[i].StepFrame += move_frames;
11152 if (stored_player[i].move_delay > 0)
11153 stored_player[i].move_delay--;
11155 /* due to bugs in previous versions, counter must count up, not down */
11156 if (stored_player[i].push_delay != -1)
11157 stored_player[i].push_delay++;
11159 if (stored_player[i].drop_delay > 0)
11160 stored_player[i].drop_delay--;
11162 if (stored_player[i].is_dropping_pressed)
11163 stored_player[i].drop_pressed_delay++;
11167 void StartGameActions(boolean init_network_game, boolean record_tape,
11170 unsigned int new_random_seed = InitRND(random_seed);
11173 TapeStartRecording(new_random_seed);
11175 #if defined(NETWORK_AVALIABLE)
11176 if (init_network_game)
11178 SendToServer_StartPlaying();
11187 void GameActionsExt()
11190 static unsigned int game_frame_delay = 0;
11192 unsigned int game_frame_delay_value;
11193 byte *recorded_player_action;
11194 byte summarized_player_action = 0;
11195 byte tape_action[MAX_PLAYERS];
11198 /* detect endless loops, caused by custom element programming */
11199 if (recursion_loop_detected && recursion_loop_depth == 0)
11201 char *message = getStringCat3("Internal Error! Element ",
11202 EL_NAME(recursion_loop_element),
11203 " caused endless loop! Quit the game?");
11205 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11206 EL_NAME(recursion_loop_element));
11208 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11210 recursion_loop_detected = FALSE; /* if game should be continued */
11217 if (game.restart_level)
11218 StartGameActions(options.network, setup.autorecord, level.random_seed);
11220 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11221 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11223 if (level.native_em_level->lev->home == 0) /* all players at home */
11225 PlayerWins(local_player);
11227 AllPlayersGone = TRUE;
11229 level.native_em_level->lev->home = -1;
11232 if (level.native_em_level->ply[0]->alive == 0 &&
11233 level.native_em_level->ply[1]->alive == 0 &&
11234 level.native_em_level->ply[2]->alive == 0 &&
11235 level.native_em_level->ply[3]->alive == 0) /* all dead */
11236 AllPlayersGone = TRUE;
11238 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11240 if (game_sp.LevelSolved &&
11241 !game_sp.GameOver) /* game won */
11243 PlayerWins(local_player);
11245 game_sp.GameOver = TRUE;
11247 AllPlayersGone = TRUE;
11250 if (game_sp.GameOver) /* game lost */
11251 AllPlayersGone = TRUE;
11253 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11255 if (game_mm.level_solved &&
11256 !game_mm.game_over) /* game won */
11258 PlayerWins(local_player);
11260 game_mm.game_over = TRUE;
11262 AllPlayersGone = TRUE;
11265 if (game_mm.game_over) /* game lost */
11266 AllPlayersGone = TRUE;
11269 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11272 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11275 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11278 game_frame_delay_value =
11279 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11281 if (tape.playing && tape.warp_forward && !tape.pausing)
11282 game_frame_delay_value = 0;
11284 SetVideoFrameDelay(game_frame_delay_value);
11288 /* ---------- main game synchronization point ---------- */
11290 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11292 printf("::: skip == %d\n", skip);
11295 /* ---------- main game synchronization point ---------- */
11297 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11301 if (network_playing && !network_player_action_received)
11303 /* try to get network player actions in time */
11305 #if defined(NETWORK_AVALIABLE)
11306 /* last chance to get network player actions without main loop delay */
11307 HandleNetworking();
11310 /* game was quit by network peer */
11311 if (game_status != GAME_MODE_PLAYING)
11314 if (!network_player_action_received)
11315 return; /* failed to get network player actions in time */
11317 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11323 /* at this point we know that we really continue executing the game */
11325 network_player_action_received = FALSE;
11327 /* when playing tape, read previously recorded player input from tape data */
11328 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11330 local_player->effective_mouse_action = local_player->mouse_action;
11332 if (recorded_player_action != NULL)
11333 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11334 recorded_player_action);
11336 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11340 if (tape.set_centered_player)
11342 game.centered_player_nr_next = tape.centered_player_nr_next;
11343 game.set_centered_player = TRUE;
11346 for (i = 0; i < MAX_PLAYERS; i++)
11348 summarized_player_action |= stored_player[i].action;
11350 if (!network_playing && (game.team_mode || tape.playing))
11351 stored_player[i].effective_action = stored_player[i].action;
11354 #if defined(NETWORK_AVALIABLE)
11355 if (network_playing)
11356 SendToServer_MovePlayer(summarized_player_action);
11359 // summarize all actions at local players mapped input device position
11360 // (this allows using different input devices in single player mode)
11361 if (!options.network && !game.team_mode)
11362 stored_player[map_player_action[local_player->index_nr]].effective_action =
11363 summarized_player_action;
11365 if (tape.recording &&
11367 setup.input_on_focus &&
11368 game.centered_player_nr != -1)
11370 for (i = 0; i < MAX_PLAYERS; i++)
11371 stored_player[i].effective_action =
11372 (i == game.centered_player_nr ? summarized_player_action : 0);
11375 if (recorded_player_action != NULL)
11376 for (i = 0; i < MAX_PLAYERS; i++)
11377 stored_player[i].effective_action = recorded_player_action[i];
11379 for (i = 0; i < MAX_PLAYERS; i++)
11381 tape_action[i] = stored_player[i].effective_action;
11383 /* (this may happen in the RND game engine if a player was not present on
11384 the playfield on level start, but appeared later from a custom element */
11385 if (setup.team_mode &&
11388 !tape.player_participates[i])
11389 tape.player_participates[i] = TRUE;
11392 SetTapeActionFromMouseAction(tape_action,
11393 &local_player->effective_mouse_action);
11395 /* only record actions from input devices, but not programmed actions */
11396 if (tape.recording)
11397 TapeRecordAction(tape_action);
11399 #if USE_NEW_PLAYER_ASSIGNMENTS
11400 // !!! also map player actions in single player mode !!!
11401 // if (game.team_mode)
11404 byte mapped_action[MAX_PLAYERS];
11406 #if DEBUG_PLAYER_ACTIONS
11408 for (i = 0; i < MAX_PLAYERS; i++)
11409 printf(" %d, ", stored_player[i].effective_action);
11412 for (i = 0; i < MAX_PLAYERS; i++)
11413 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11415 for (i = 0; i < MAX_PLAYERS; i++)
11416 stored_player[i].effective_action = mapped_action[i];
11418 #if DEBUG_PLAYER_ACTIONS
11420 for (i = 0; i < MAX_PLAYERS; i++)
11421 printf(" %d, ", stored_player[i].effective_action);
11425 #if DEBUG_PLAYER_ACTIONS
11429 for (i = 0; i < MAX_PLAYERS; i++)
11430 printf(" %d, ", stored_player[i].effective_action);
11436 for (i = 0; i < MAX_PLAYERS; i++)
11438 // allow engine snapshot in case of changed movement attempt
11439 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11440 (stored_player[i].effective_action & KEY_MOTION))
11441 game.snapshot.changed_action = TRUE;
11443 // allow engine snapshot in case of snapping/dropping attempt
11444 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11445 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11446 game.snapshot.changed_action = TRUE;
11448 game.snapshot.last_action[i] = stored_player[i].effective_action;
11451 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11453 GameActions_EM_Main();
11455 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11457 GameActions_SP_Main();
11459 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11461 GameActions_MM_Main();
11465 GameActions_RND_Main();
11468 BlitScreenToBitmap(backbuffer);
11472 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11474 if (global.show_frames_per_second)
11476 static unsigned int fps_counter = 0;
11477 static int fps_frames = 0;
11478 unsigned int fps_delay_ms = Counter() - fps_counter;
11482 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11484 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11487 fps_counter = Counter();
11489 /* always draw FPS to screen after FPS value was updated */
11490 redraw_mask |= REDRAW_FPS;
11493 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11494 if (GetDrawDeactivationMask() == REDRAW_NONE)
11495 redraw_mask |= REDRAW_FPS;
11499 static void GameActions_CheckSaveEngineSnapshot()
11501 if (!game.snapshot.save_snapshot)
11504 // clear flag for saving snapshot _before_ saving snapshot
11505 game.snapshot.save_snapshot = FALSE;
11507 SaveEngineSnapshotToList();
11514 GameActions_CheckSaveEngineSnapshot();
11517 void GameActions_EM_Main()
11519 byte effective_action[MAX_PLAYERS];
11520 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11523 for (i = 0; i < MAX_PLAYERS; i++)
11524 effective_action[i] = stored_player[i].effective_action;
11526 GameActions_EM(effective_action, warp_mode);
11529 void GameActions_SP_Main()
11531 byte effective_action[MAX_PLAYERS];
11532 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11535 for (i = 0; i < MAX_PLAYERS; i++)
11536 effective_action[i] = stored_player[i].effective_action;
11538 GameActions_SP(effective_action, warp_mode);
11540 for (i = 0; i < MAX_PLAYERS; i++)
11542 if (stored_player[i].force_dropping)
11543 stored_player[i].action |= KEY_BUTTON_DROP;
11545 stored_player[i].force_dropping = FALSE;
11549 void GameActions_MM_Main()
11551 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11553 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11556 void GameActions_RND_Main()
11561 void GameActions_RND()
11563 int magic_wall_x = 0, magic_wall_y = 0;
11564 int i, x, y, element, graphic, last_gfx_frame;
11566 InitPlayfieldScanModeVars();
11568 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11570 SCAN_PLAYFIELD(x, y)
11572 ChangeCount[x][y] = 0;
11573 ChangeEvent[x][y] = -1;
11577 if (game.set_centered_player)
11579 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11581 /* switching to "all players" only possible if all players fit to screen */
11582 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11584 game.centered_player_nr_next = game.centered_player_nr;
11585 game.set_centered_player = FALSE;
11588 /* do not switch focus to non-existing (or non-active) player */
11589 if (game.centered_player_nr_next >= 0 &&
11590 !stored_player[game.centered_player_nr_next].active)
11592 game.centered_player_nr_next = game.centered_player_nr;
11593 game.set_centered_player = FALSE;
11597 if (game.set_centered_player &&
11598 ScreenMovPos == 0) /* screen currently aligned at tile position */
11602 if (game.centered_player_nr_next == -1)
11604 setScreenCenteredToAllPlayers(&sx, &sy);
11608 sx = stored_player[game.centered_player_nr_next].jx;
11609 sy = stored_player[game.centered_player_nr_next].jy;
11612 game.centered_player_nr = game.centered_player_nr_next;
11613 game.set_centered_player = FALSE;
11615 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11616 DrawGameDoorValues();
11619 for (i = 0; i < MAX_PLAYERS; i++)
11621 int actual_player_action = stored_player[i].effective_action;
11624 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11625 - rnd_equinox_tetrachloride 048
11626 - rnd_equinox_tetrachloride_ii 096
11627 - rnd_emanuel_schmieg 002
11628 - doctor_sloan_ww 001, 020
11630 if (stored_player[i].MovPos == 0)
11631 CheckGravityMovement(&stored_player[i]);
11634 /* overwrite programmed action with tape action */
11635 if (stored_player[i].programmed_action)
11636 actual_player_action = stored_player[i].programmed_action;
11638 PlayerActions(&stored_player[i], actual_player_action);
11640 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11643 ScrollScreen(NULL, SCROLL_GO_ON);
11645 /* for backwards compatibility, the following code emulates a fixed bug that
11646 occured when pushing elements (causing elements that just made their last
11647 pushing step to already (if possible) make their first falling step in the
11648 same game frame, which is bad); this code is also needed to use the famous
11649 "spring push bug" which is used in older levels and might be wanted to be
11650 used also in newer levels, but in this case the buggy pushing code is only
11651 affecting the "spring" element and no other elements */
11653 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11655 for (i = 0; i < MAX_PLAYERS; i++)
11657 struct PlayerInfo *player = &stored_player[i];
11658 int x = player->jx;
11659 int y = player->jy;
11661 if (player->active && player->is_pushing && player->is_moving &&
11663 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11664 Feld[x][y] == EL_SPRING))
11666 ContinueMoving(x, y);
11668 /* continue moving after pushing (this is actually a bug) */
11669 if (!IS_MOVING(x, y))
11670 Stop[x][y] = FALSE;
11675 SCAN_PLAYFIELD(x, y)
11677 ChangeCount[x][y] = 0;
11678 ChangeEvent[x][y] = -1;
11680 /* this must be handled before main playfield loop */
11681 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11684 if (MovDelay[x][y] <= 0)
11688 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11691 if (MovDelay[x][y] <= 0)
11694 TEST_DrawLevelField(x, y);
11696 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11701 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11703 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11704 printf("GameActions(): This should never happen!\n");
11706 ChangePage[x][y] = -1;
11710 Stop[x][y] = FALSE;
11711 if (WasJustMoving[x][y] > 0)
11712 WasJustMoving[x][y]--;
11713 if (WasJustFalling[x][y] > 0)
11714 WasJustFalling[x][y]--;
11715 if (CheckCollision[x][y] > 0)
11716 CheckCollision[x][y]--;
11717 if (CheckImpact[x][y] > 0)
11718 CheckImpact[x][y]--;
11722 /* reset finished pushing action (not done in ContinueMoving() to allow
11723 continuous pushing animation for elements with zero push delay) */
11724 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11726 ResetGfxAnimation(x, y);
11727 TEST_DrawLevelField(x, y);
11731 if (IS_BLOCKED(x, y))
11735 Blocked2Moving(x, y, &oldx, &oldy);
11736 if (!IS_MOVING(oldx, oldy))
11738 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11739 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11740 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11741 printf("GameActions(): This should never happen!\n");
11747 SCAN_PLAYFIELD(x, y)
11749 element = Feld[x][y];
11750 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11751 last_gfx_frame = GfxFrame[x][y];
11753 ResetGfxFrame(x, y);
11755 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11756 DrawLevelGraphicAnimation(x, y, graphic);
11758 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11759 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11760 ResetRandomAnimationValue(x, y);
11762 SetRandomAnimationValue(x, y);
11764 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11766 if (IS_INACTIVE(element))
11768 if (IS_ANIMATED(graphic))
11769 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11774 /* this may take place after moving, so 'element' may have changed */
11775 if (IS_CHANGING(x, y) &&
11776 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11778 int page = element_info[element].event_page_nr[CE_DELAY];
11780 HandleElementChange(x, y, page);
11782 element = Feld[x][y];
11783 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11786 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11790 element = Feld[x][y];
11791 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11793 if (IS_ANIMATED(graphic) &&
11794 !IS_MOVING(x, y) &&
11796 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11798 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11799 TEST_DrawTwinkleOnField(x, y);
11801 else if (element == EL_ACID)
11804 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11806 else if ((element == EL_EXIT_OPEN ||
11807 element == EL_EM_EXIT_OPEN ||
11808 element == EL_SP_EXIT_OPEN ||
11809 element == EL_STEEL_EXIT_OPEN ||
11810 element == EL_EM_STEEL_EXIT_OPEN ||
11811 element == EL_SP_TERMINAL ||
11812 element == EL_SP_TERMINAL_ACTIVE ||
11813 element == EL_EXTRA_TIME ||
11814 element == EL_SHIELD_NORMAL ||
11815 element == EL_SHIELD_DEADLY) &&
11816 IS_ANIMATED(graphic))
11817 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11818 else if (IS_MOVING(x, y))
11819 ContinueMoving(x, y);
11820 else if (IS_ACTIVE_BOMB(element))
11821 CheckDynamite(x, y);
11822 else if (element == EL_AMOEBA_GROWING)
11823 AmoebeWaechst(x, y);
11824 else if (element == EL_AMOEBA_SHRINKING)
11825 AmoebaDisappearing(x, y);
11827 #if !USE_NEW_AMOEBA_CODE
11828 else if (IS_AMOEBALIVE(element))
11829 AmoebeAbleger(x, y);
11832 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11834 else if (element == EL_EXIT_CLOSED)
11836 else if (element == EL_EM_EXIT_CLOSED)
11838 else if (element == EL_STEEL_EXIT_CLOSED)
11839 CheckExitSteel(x, y);
11840 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11841 CheckExitSteelEM(x, y);
11842 else if (element == EL_SP_EXIT_CLOSED)
11844 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11845 element == EL_EXPANDABLE_STEELWALL_GROWING)
11846 MauerWaechst(x, y);
11847 else if (element == EL_EXPANDABLE_WALL ||
11848 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11849 element == EL_EXPANDABLE_WALL_VERTICAL ||
11850 element == EL_EXPANDABLE_WALL_ANY ||
11851 element == EL_BD_EXPANDABLE_WALL)
11852 MauerAbleger(x, y);
11853 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11854 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11855 element == EL_EXPANDABLE_STEELWALL_ANY)
11856 MauerAblegerStahl(x, y);
11857 else if (element == EL_FLAMES)
11858 CheckForDragon(x, y);
11859 else if (element == EL_EXPLOSION)
11860 ; /* drawing of correct explosion animation is handled separately */
11861 else if (element == EL_ELEMENT_SNAPPING ||
11862 element == EL_DIAGONAL_SHRINKING ||
11863 element == EL_DIAGONAL_GROWING)
11865 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11867 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11869 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11870 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11872 if (IS_BELT_ACTIVE(element))
11873 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11875 if (game.magic_wall_active)
11877 int jx = local_player->jx, jy = local_player->jy;
11879 /* play the element sound at the position nearest to the player */
11880 if ((element == EL_MAGIC_WALL_FULL ||
11881 element == EL_MAGIC_WALL_ACTIVE ||
11882 element == EL_MAGIC_WALL_EMPTYING ||
11883 element == EL_BD_MAGIC_WALL_FULL ||
11884 element == EL_BD_MAGIC_WALL_ACTIVE ||
11885 element == EL_BD_MAGIC_WALL_EMPTYING ||
11886 element == EL_DC_MAGIC_WALL_FULL ||
11887 element == EL_DC_MAGIC_WALL_ACTIVE ||
11888 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11889 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11897 #if USE_NEW_AMOEBA_CODE
11898 /* new experimental amoeba growth stuff */
11899 if (!(FrameCounter % 8))
11901 static unsigned int random = 1684108901;
11903 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11905 x = RND(lev_fieldx);
11906 y = RND(lev_fieldy);
11907 element = Feld[x][y];
11909 if (!IS_PLAYER(x,y) &&
11910 (element == EL_EMPTY ||
11911 CAN_GROW_INTO(element) ||
11912 element == EL_QUICKSAND_EMPTY ||
11913 element == EL_QUICKSAND_FAST_EMPTY ||
11914 element == EL_ACID_SPLASH_LEFT ||
11915 element == EL_ACID_SPLASH_RIGHT))
11917 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11918 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11919 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11920 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11921 Feld[x][y] = EL_AMOEBA_DROP;
11924 random = random * 129 + 1;
11929 game.explosions_delayed = FALSE;
11931 SCAN_PLAYFIELD(x, y)
11933 element = Feld[x][y];
11935 if (ExplodeField[x][y])
11936 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11937 else if (element == EL_EXPLOSION)
11938 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11940 ExplodeField[x][y] = EX_TYPE_NONE;
11943 game.explosions_delayed = TRUE;
11945 if (game.magic_wall_active)
11947 if (!(game.magic_wall_time_left % 4))
11949 int element = Feld[magic_wall_x][magic_wall_y];
11951 if (element == EL_BD_MAGIC_WALL_FULL ||
11952 element == EL_BD_MAGIC_WALL_ACTIVE ||
11953 element == EL_BD_MAGIC_WALL_EMPTYING)
11954 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11955 else if (element == EL_DC_MAGIC_WALL_FULL ||
11956 element == EL_DC_MAGIC_WALL_ACTIVE ||
11957 element == EL_DC_MAGIC_WALL_EMPTYING)
11958 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11960 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11963 if (game.magic_wall_time_left > 0)
11965 game.magic_wall_time_left--;
11967 if (!game.magic_wall_time_left)
11969 SCAN_PLAYFIELD(x, y)
11971 element = Feld[x][y];
11973 if (element == EL_MAGIC_WALL_ACTIVE ||
11974 element == EL_MAGIC_WALL_FULL)
11976 Feld[x][y] = EL_MAGIC_WALL_DEAD;
11977 TEST_DrawLevelField(x, y);
11979 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11980 element == EL_BD_MAGIC_WALL_FULL)
11982 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11983 TEST_DrawLevelField(x, y);
11985 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11986 element == EL_DC_MAGIC_WALL_FULL)
11988 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11989 TEST_DrawLevelField(x, y);
11993 game.magic_wall_active = FALSE;
11998 if (game.light_time_left > 0)
12000 game.light_time_left--;
12002 if (game.light_time_left == 0)
12003 RedrawAllLightSwitchesAndInvisibleElements();
12006 if (game.timegate_time_left > 0)
12008 game.timegate_time_left--;
12010 if (game.timegate_time_left == 0)
12011 CloseAllOpenTimegates();
12014 if (game.lenses_time_left > 0)
12016 game.lenses_time_left--;
12018 if (game.lenses_time_left == 0)
12019 RedrawAllInvisibleElementsForLenses();
12022 if (game.magnify_time_left > 0)
12024 game.magnify_time_left--;
12026 if (game.magnify_time_left == 0)
12027 RedrawAllInvisibleElementsForMagnifier();
12030 for (i = 0; i < MAX_PLAYERS; i++)
12032 struct PlayerInfo *player = &stored_player[i];
12034 if (SHIELD_ON(player))
12036 if (player->shield_deadly_time_left)
12037 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12038 else if (player->shield_normal_time_left)
12039 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12043 #if USE_DELAYED_GFX_REDRAW
12044 SCAN_PLAYFIELD(x, y)
12046 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12048 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12049 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12051 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12052 DrawLevelField(x, y);
12054 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12055 DrawLevelFieldCrumbled(x, y);
12057 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12058 DrawLevelFieldCrumbledNeighbours(x, y);
12060 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12061 DrawTwinkleOnField(x, y);
12064 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12069 PlayAllPlayersSound();
12071 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12073 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12075 local_player->show_envelope = 0;
12078 /* use random number generator in every frame to make it less predictable */
12079 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12083 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12085 int min_x = x, min_y = y, max_x = x, max_y = y;
12088 for (i = 0; i < MAX_PLAYERS; i++)
12090 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12092 if (!stored_player[i].active || &stored_player[i] == player)
12095 min_x = MIN(min_x, jx);
12096 min_y = MIN(min_y, jy);
12097 max_x = MAX(max_x, jx);
12098 max_y = MAX(max_y, jy);
12101 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12104 static boolean AllPlayersInVisibleScreen()
12108 for (i = 0; i < MAX_PLAYERS; i++)
12110 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12112 if (!stored_player[i].active)
12115 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12122 void ScrollLevel(int dx, int dy)
12124 int scroll_offset = 2 * TILEX_VAR;
12127 BlitBitmap(drawto_field, drawto_field,
12128 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12129 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12130 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12131 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12132 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12133 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12137 x = (dx == 1 ? BX1 : BX2);
12138 for (y = BY1; y <= BY2; y++)
12139 DrawScreenField(x, y);
12144 y = (dy == 1 ? BY1 : BY2);
12145 for (x = BX1; x <= BX2; x++)
12146 DrawScreenField(x, y);
12149 redraw_mask |= REDRAW_FIELD;
12152 static boolean canFallDown(struct PlayerInfo *player)
12154 int jx = player->jx, jy = player->jy;
12156 return (IN_LEV_FIELD(jx, jy + 1) &&
12157 (IS_FREE(jx, jy + 1) ||
12158 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12159 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12160 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12163 static boolean canPassField(int x, int y, int move_dir)
12165 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12166 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12167 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12168 int nextx = x + dx;
12169 int nexty = y + dy;
12170 int element = Feld[x][y];
12172 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12173 !CAN_MOVE(element) &&
12174 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12175 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12176 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12179 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12181 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12182 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12183 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12187 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12188 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12189 (IS_DIGGABLE(Feld[newx][newy]) ||
12190 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12191 canPassField(newx, newy, move_dir)));
12194 static void CheckGravityMovement(struct PlayerInfo *player)
12196 if (player->gravity && !player->programmed_action)
12198 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12199 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12200 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12201 int jx = player->jx, jy = player->jy;
12202 boolean player_is_moving_to_valid_field =
12203 (!player_is_snapping &&
12204 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12205 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12206 boolean player_can_fall_down = canFallDown(player);
12208 if (player_can_fall_down &&
12209 !player_is_moving_to_valid_field)
12210 player->programmed_action = MV_DOWN;
12214 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12216 return CheckGravityMovement(player);
12218 if (player->gravity && !player->programmed_action)
12220 int jx = player->jx, jy = player->jy;
12221 boolean field_under_player_is_free =
12222 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12223 boolean player_is_standing_on_valid_field =
12224 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12225 (IS_WALKABLE(Feld[jx][jy]) &&
12226 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12228 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12229 player->programmed_action = MV_DOWN;
12234 MovePlayerOneStep()
12235 -----------------------------------------------------------------------------
12236 dx, dy: direction (non-diagonal) to try to move the player to
12237 real_dx, real_dy: direction as read from input device (can be diagonal)
12240 boolean MovePlayerOneStep(struct PlayerInfo *player,
12241 int dx, int dy, int real_dx, int real_dy)
12243 int jx = player->jx, jy = player->jy;
12244 int new_jx = jx + dx, new_jy = jy + dy;
12246 boolean player_can_move = !player->cannot_move;
12248 if (!player->active || (!dx && !dy))
12249 return MP_NO_ACTION;
12251 player->MovDir = (dx < 0 ? MV_LEFT :
12252 dx > 0 ? MV_RIGHT :
12254 dy > 0 ? MV_DOWN : MV_NONE);
12256 if (!IN_LEV_FIELD(new_jx, new_jy))
12257 return MP_NO_ACTION;
12259 if (!player_can_move)
12261 if (player->MovPos == 0)
12263 player->is_moving = FALSE;
12264 player->is_digging = FALSE;
12265 player->is_collecting = FALSE;
12266 player->is_snapping = FALSE;
12267 player->is_pushing = FALSE;
12271 if (!options.network && game.centered_player_nr == -1 &&
12272 !AllPlayersInSight(player, new_jx, new_jy))
12273 return MP_NO_ACTION;
12275 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12276 if (can_move != MP_MOVING)
12279 /* check if DigField() has caused relocation of the player */
12280 if (player->jx != jx || player->jy != jy)
12281 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12283 StorePlayer[jx][jy] = 0;
12284 player->last_jx = jx;
12285 player->last_jy = jy;
12286 player->jx = new_jx;
12287 player->jy = new_jy;
12288 StorePlayer[new_jx][new_jy] = player->element_nr;
12290 if (player->move_delay_value_next != -1)
12292 player->move_delay_value = player->move_delay_value_next;
12293 player->move_delay_value_next = -1;
12297 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12299 player->step_counter++;
12301 PlayerVisit[jx][jy] = FrameCounter;
12303 player->is_moving = TRUE;
12306 /* should better be called in MovePlayer(), but this breaks some tapes */
12307 ScrollPlayer(player, SCROLL_INIT);
12313 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12315 int jx = player->jx, jy = player->jy;
12316 int old_jx = jx, old_jy = jy;
12317 int moved = MP_NO_ACTION;
12319 if (!player->active)
12324 if (player->MovPos == 0)
12326 player->is_moving = FALSE;
12327 player->is_digging = FALSE;
12328 player->is_collecting = FALSE;
12329 player->is_snapping = FALSE;
12330 player->is_pushing = FALSE;
12336 if (player->move_delay > 0)
12339 player->move_delay = -1; /* set to "uninitialized" value */
12341 /* store if player is automatically moved to next field */
12342 player->is_auto_moving = (player->programmed_action != MV_NONE);
12344 /* remove the last programmed player action */
12345 player->programmed_action = 0;
12347 if (player->MovPos)
12349 /* should only happen if pre-1.2 tape recordings are played */
12350 /* this is only for backward compatibility */
12352 int original_move_delay_value = player->move_delay_value;
12355 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12359 /* scroll remaining steps with finest movement resolution */
12360 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12362 while (player->MovPos)
12364 ScrollPlayer(player, SCROLL_GO_ON);
12365 ScrollScreen(NULL, SCROLL_GO_ON);
12367 AdvanceFrameAndPlayerCounters(player->index_nr);
12370 BackToFront_WithFrameDelay(0);
12373 player->move_delay_value = original_move_delay_value;
12376 player->is_active = FALSE;
12378 if (player->last_move_dir & MV_HORIZONTAL)
12380 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12381 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12385 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12386 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12389 if (!moved && !player->is_active)
12391 player->is_moving = FALSE;
12392 player->is_digging = FALSE;
12393 player->is_collecting = FALSE;
12394 player->is_snapping = FALSE;
12395 player->is_pushing = FALSE;
12401 if (moved & MP_MOVING && !ScreenMovPos &&
12402 (player->index_nr == game.centered_player_nr ||
12403 game.centered_player_nr == -1))
12405 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12406 int offset = game.scroll_delay_value;
12408 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12410 /* actual player has left the screen -- scroll in that direction */
12411 if (jx != old_jx) /* player has moved horizontally */
12412 scroll_x += (jx - old_jx);
12413 else /* player has moved vertically */
12414 scroll_y += (jy - old_jy);
12418 if (jx != old_jx) /* player has moved horizontally */
12420 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12421 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12422 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12424 /* don't scroll over playfield boundaries */
12425 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12426 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12428 /* don't scroll more than one field at a time */
12429 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12431 /* don't scroll against the player's moving direction */
12432 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12433 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12434 scroll_x = old_scroll_x;
12436 else /* player has moved vertically */
12438 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12439 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12440 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12442 /* don't scroll over playfield boundaries */
12443 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12444 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12446 /* don't scroll more than one field at a time */
12447 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12449 /* don't scroll against the player's moving direction */
12450 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12451 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12452 scroll_y = old_scroll_y;
12456 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12458 if (!options.network && game.centered_player_nr == -1 &&
12459 !AllPlayersInVisibleScreen())
12461 scroll_x = old_scroll_x;
12462 scroll_y = old_scroll_y;
12466 ScrollScreen(player, SCROLL_INIT);
12467 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12472 player->StepFrame = 0;
12474 if (moved & MP_MOVING)
12476 if (old_jx != jx && old_jy == jy)
12477 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12478 else if (old_jx == jx && old_jy != jy)
12479 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12481 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12483 player->last_move_dir = player->MovDir;
12484 player->is_moving = TRUE;
12485 player->is_snapping = FALSE;
12486 player->is_switching = FALSE;
12487 player->is_dropping = FALSE;
12488 player->is_dropping_pressed = FALSE;
12489 player->drop_pressed_delay = 0;
12492 /* should better be called here than above, but this breaks some tapes */
12493 ScrollPlayer(player, SCROLL_INIT);
12498 CheckGravityMovementWhenNotMoving(player);
12500 player->is_moving = FALSE;
12502 /* at this point, the player is allowed to move, but cannot move right now
12503 (e.g. because of something blocking the way) -- ensure that the player
12504 is also allowed to move in the next frame (in old versions before 3.1.1,
12505 the player was forced to wait again for eight frames before next try) */
12507 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12508 player->move_delay = 0; /* allow direct movement in the next frame */
12511 if (player->move_delay == -1) /* not yet initialized by DigField() */
12512 player->move_delay = player->move_delay_value;
12514 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12516 TestIfPlayerTouchesBadThing(jx, jy);
12517 TestIfPlayerTouchesCustomElement(jx, jy);
12520 if (!player->active)
12521 RemovePlayer(player);
12526 void ScrollPlayer(struct PlayerInfo *player, int mode)
12528 int jx = player->jx, jy = player->jy;
12529 int last_jx = player->last_jx, last_jy = player->last_jy;
12530 int move_stepsize = TILEX / player->move_delay_value;
12532 if (!player->active)
12535 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12538 if (mode == SCROLL_INIT)
12540 player->actual_frame_counter = FrameCounter;
12541 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12543 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12544 Feld[last_jx][last_jy] == EL_EMPTY)
12546 int last_field_block_delay = 0; /* start with no blocking at all */
12547 int block_delay_adjustment = player->block_delay_adjustment;
12549 /* if player blocks last field, add delay for exactly one move */
12550 if (player->block_last_field)
12552 last_field_block_delay += player->move_delay_value;
12554 /* when blocking enabled, prevent moving up despite gravity */
12555 if (player->gravity && player->MovDir == MV_UP)
12556 block_delay_adjustment = -1;
12559 /* add block delay adjustment (also possible when not blocking) */
12560 last_field_block_delay += block_delay_adjustment;
12562 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12563 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12566 if (player->MovPos != 0) /* player has not yet reached destination */
12569 else if (!FrameReached(&player->actual_frame_counter, 1))
12572 if (player->MovPos != 0)
12574 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12575 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12577 /* before DrawPlayer() to draw correct player graphic for this case */
12578 if (player->MovPos == 0)
12579 CheckGravityMovement(player);
12582 if (player->MovPos == 0) /* player reached destination field */
12584 if (player->move_delay_reset_counter > 0)
12586 player->move_delay_reset_counter--;
12588 if (player->move_delay_reset_counter == 0)
12590 /* continue with normal speed after quickly moving through gate */
12591 HALVE_PLAYER_SPEED(player);
12593 /* be able to make the next move without delay */
12594 player->move_delay = 0;
12598 player->last_jx = jx;
12599 player->last_jy = jy;
12601 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12602 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12603 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12604 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12605 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12606 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12607 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12608 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12610 DrawPlayer(player); /* needed here only to cleanup last field */
12611 RemovePlayer(player);
12613 if (local_player->friends_still_needed == 0 ||
12614 IS_SP_ELEMENT(Feld[jx][jy]))
12615 PlayerWins(player);
12618 /* this breaks one level: "machine", level 000 */
12620 int move_direction = player->MovDir;
12621 int enter_side = MV_DIR_OPPOSITE(move_direction);
12622 int leave_side = move_direction;
12623 int old_jx = last_jx;
12624 int old_jy = last_jy;
12625 int old_element = Feld[old_jx][old_jy];
12626 int new_element = Feld[jx][jy];
12628 if (IS_CUSTOM_ELEMENT(old_element))
12629 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12631 player->index_bit, leave_side);
12633 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12634 CE_PLAYER_LEAVES_X,
12635 player->index_bit, leave_side);
12637 if (IS_CUSTOM_ELEMENT(new_element))
12638 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12639 player->index_bit, enter_side);
12641 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12642 CE_PLAYER_ENTERS_X,
12643 player->index_bit, enter_side);
12645 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12646 CE_MOVE_OF_X, move_direction);
12649 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12651 TestIfPlayerTouchesBadThing(jx, jy);
12652 TestIfPlayerTouchesCustomElement(jx, jy);
12654 /* needed because pushed element has not yet reached its destination,
12655 so it would trigger a change event at its previous field location */
12656 if (!player->is_pushing)
12657 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12659 if (!player->active)
12660 RemovePlayer(player);
12663 if (!local_player->LevelSolved && level.use_step_counter)
12673 if (TimeLeft <= 10 && setup.time_limit)
12674 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12676 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12678 DisplayGameControlValues();
12680 if (!TimeLeft && setup.time_limit)
12681 for (i = 0; i < MAX_PLAYERS; i++)
12682 KillPlayer(&stored_player[i]);
12684 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12686 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12688 DisplayGameControlValues();
12692 if (tape.single_step && tape.recording && !tape.pausing &&
12693 !player->programmed_action)
12694 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12696 if (!player->programmed_action)
12697 CheckSaveEngineSnapshot(player);
12701 void ScrollScreen(struct PlayerInfo *player, int mode)
12703 static unsigned int screen_frame_counter = 0;
12705 if (mode == SCROLL_INIT)
12707 /* set scrolling step size according to actual player's moving speed */
12708 ScrollStepSize = TILEX / player->move_delay_value;
12710 screen_frame_counter = FrameCounter;
12711 ScreenMovDir = player->MovDir;
12712 ScreenMovPos = player->MovPos;
12713 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12716 else if (!FrameReached(&screen_frame_counter, 1))
12721 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12722 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12723 redraw_mask |= REDRAW_FIELD;
12726 ScreenMovDir = MV_NONE;
12729 void TestIfPlayerTouchesCustomElement(int x, int y)
12731 static int xy[4][2] =
12738 static int trigger_sides[4][2] =
12740 /* center side border side */
12741 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12742 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12743 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12744 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12746 static int touch_dir[4] =
12748 MV_LEFT | MV_RIGHT,
12753 int center_element = Feld[x][y]; /* should always be non-moving! */
12756 for (i = 0; i < NUM_DIRECTIONS; i++)
12758 int xx = x + xy[i][0];
12759 int yy = y + xy[i][1];
12760 int center_side = trigger_sides[i][0];
12761 int border_side = trigger_sides[i][1];
12762 int border_element;
12764 if (!IN_LEV_FIELD(xx, yy))
12767 if (IS_PLAYER(x, y)) /* player found at center element */
12769 struct PlayerInfo *player = PLAYERINFO(x, y);
12771 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12772 border_element = Feld[xx][yy]; /* may be moving! */
12773 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12774 border_element = Feld[xx][yy];
12775 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12776 border_element = MovingOrBlocked2Element(xx, yy);
12778 continue; /* center and border element do not touch */
12780 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12781 player->index_bit, border_side);
12782 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12783 CE_PLAYER_TOUCHES_X,
12784 player->index_bit, border_side);
12787 /* use player element that is initially defined in the level playfield,
12788 not the player element that corresponds to the runtime player number
12789 (example: a level that contains EL_PLAYER_3 as the only player would
12790 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12791 int player_element = PLAYERINFO(x, y)->initial_element;
12793 CheckElementChangeBySide(xx, yy, border_element, player_element,
12794 CE_TOUCHING_X, border_side);
12797 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12799 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12801 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12803 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12804 continue; /* center and border element do not touch */
12807 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12808 player->index_bit, center_side);
12809 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12810 CE_PLAYER_TOUCHES_X,
12811 player->index_bit, center_side);
12814 /* use player element that is initially defined in the level playfield,
12815 not the player element that corresponds to the runtime player number
12816 (example: a level that contains EL_PLAYER_3 as the only player would
12817 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12818 int player_element = PLAYERINFO(xx, yy)->initial_element;
12820 CheckElementChangeBySide(x, y, center_element, player_element,
12821 CE_TOUCHING_X, center_side);
12829 void TestIfElementTouchesCustomElement(int x, int y)
12831 static int xy[4][2] =
12838 static int trigger_sides[4][2] =
12840 /* center side border side */
12841 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12842 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12843 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12844 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12846 static int touch_dir[4] =
12848 MV_LEFT | MV_RIGHT,
12853 boolean change_center_element = FALSE;
12854 int center_element = Feld[x][y]; /* should always be non-moving! */
12855 int border_element_old[NUM_DIRECTIONS];
12858 for (i = 0; i < NUM_DIRECTIONS; i++)
12860 int xx = x + xy[i][0];
12861 int yy = y + xy[i][1];
12862 int border_element;
12864 border_element_old[i] = -1;
12866 if (!IN_LEV_FIELD(xx, yy))
12869 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12870 border_element = Feld[xx][yy]; /* may be moving! */
12871 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12872 border_element = Feld[xx][yy];
12873 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12874 border_element = MovingOrBlocked2Element(xx, yy);
12876 continue; /* center and border element do not touch */
12878 border_element_old[i] = border_element;
12881 for (i = 0; i < NUM_DIRECTIONS; i++)
12883 int xx = x + xy[i][0];
12884 int yy = y + xy[i][1];
12885 int center_side = trigger_sides[i][0];
12886 int border_element = border_element_old[i];
12888 if (border_element == -1)
12891 /* check for change of border element */
12892 CheckElementChangeBySide(xx, yy, border_element, center_element,
12893 CE_TOUCHING_X, center_side);
12895 /* (center element cannot be player, so we dont have to check this here) */
12898 for (i = 0; i < NUM_DIRECTIONS; i++)
12900 int xx = x + xy[i][0];
12901 int yy = y + xy[i][1];
12902 int border_side = trigger_sides[i][1];
12903 int border_element = border_element_old[i];
12905 if (border_element == -1)
12908 /* check for change of center element (but change it only once) */
12909 if (!change_center_element)
12910 change_center_element =
12911 CheckElementChangeBySide(x, y, center_element, border_element,
12912 CE_TOUCHING_X, border_side);
12914 if (IS_PLAYER(xx, yy))
12916 /* use player element that is initially defined in the level playfield,
12917 not the player element that corresponds to the runtime player number
12918 (example: a level that contains EL_PLAYER_3 as the only player would
12919 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12920 int player_element = PLAYERINFO(xx, yy)->initial_element;
12922 CheckElementChangeBySide(x, y, center_element, player_element,
12923 CE_TOUCHING_X, border_side);
12928 void TestIfElementHitsCustomElement(int x, int y, int direction)
12930 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12931 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
12932 int hitx = x + dx, hity = y + dy;
12933 int hitting_element = Feld[x][y];
12934 int touched_element;
12936 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12939 touched_element = (IN_LEV_FIELD(hitx, hity) ?
12940 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12942 if (IN_LEV_FIELD(hitx, hity))
12944 int opposite_direction = MV_DIR_OPPOSITE(direction);
12945 int hitting_side = direction;
12946 int touched_side = opposite_direction;
12947 boolean object_hit = (!IS_MOVING(hitx, hity) ||
12948 MovDir[hitx][hity] != direction ||
12949 ABS(MovPos[hitx][hity]) <= TILEY / 2);
12955 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12956 CE_HITTING_X, touched_side);
12958 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12959 CE_HIT_BY_X, hitting_side);
12961 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12962 CE_HIT_BY_SOMETHING, opposite_direction);
12964 if (IS_PLAYER(hitx, hity))
12966 /* use player element that is initially defined in the level playfield,
12967 not the player element that corresponds to the runtime player number
12968 (example: a level that contains EL_PLAYER_3 as the only player would
12969 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12970 int player_element = PLAYERINFO(hitx, hity)->initial_element;
12972 CheckElementChangeBySide(x, y, hitting_element, player_element,
12973 CE_HITTING_X, touched_side);
12978 /* "hitting something" is also true when hitting the playfield border */
12979 CheckElementChangeBySide(x, y, hitting_element, touched_element,
12980 CE_HITTING_SOMETHING, direction);
12983 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12985 int i, kill_x = -1, kill_y = -1;
12987 int bad_element = -1;
12988 static int test_xy[4][2] =
12995 static int test_dir[4] =
13003 for (i = 0; i < NUM_DIRECTIONS; i++)
13005 int test_x, test_y, test_move_dir, test_element;
13007 test_x = good_x + test_xy[i][0];
13008 test_y = good_y + test_xy[i][1];
13010 if (!IN_LEV_FIELD(test_x, test_y))
13014 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13016 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13018 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13019 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13021 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13022 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13026 bad_element = test_element;
13032 if (kill_x != -1 || kill_y != -1)
13034 if (IS_PLAYER(good_x, good_y))
13036 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13038 if (player->shield_deadly_time_left > 0 &&
13039 !IS_INDESTRUCTIBLE(bad_element))
13040 Bang(kill_x, kill_y);
13041 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13042 KillPlayer(player);
13045 Bang(good_x, good_y);
13049 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13051 int i, kill_x = -1, kill_y = -1;
13052 int bad_element = Feld[bad_x][bad_y];
13053 static int test_xy[4][2] =
13060 static int touch_dir[4] =
13062 MV_LEFT | MV_RIGHT,
13067 static int test_dir[4] =
13075 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13078 for (i = 0; i < NUM_DIRECTIONS; i++)
13080 int test_x, test_y, test_move_dir, test_element;
13082 test_x = bad_x + test_xy[i][0];
13083 test_y = bad_y + test_xy[i][1];
13085 if (!IN_LEV_FIELD(test_x, test_y))
13089 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13091 test_element = Feld[test_x][test_y];
13093 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13094 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13096 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13097 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13099 /* good thing is player or penguin that does not move away */
13100 if (IS_PLAYER(test_x, test_y))
13102 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13104 if (bad_element == EL_ROBOT && player->is_moving)
13105 continue; /* robot does not kill player if he is moving */
13107 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13109 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13110 continue; /* center and border element do not touch */
13118 else if (test_element == EL_PENGUIN)
13128 if (kill_x != -1 || kill_y != -1)
13130 if (IS_PLAYER(kill_x, kill_y))
13132 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13134 if (player->shield_deadly_time_left > 0 &&
13135 !IS_INDESTRUCTIBLE(bad_element))
13136 Bang(bad_x, bad_y);
13137 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13138 KillPlayer(player);
13141 Bang(kill_x, kill_y);
13145 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13147 int bad_element = Feld[bad_x][bad_y];
13148 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13149 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13150 int test_x = bad_x + dx, test_y = bad_y + dy;
13151 int test_move_dir, test_element;
13152 int kill_x = -1, kill_y = -1;
13154 if (!IN_LEV_FIELD(test_x, test_y))
13158 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13160 test_element = Feld[test_x][test_y];
13162 if (test_move_dir != bad_move_dir)
13164 /* good thing can be player or penguin that does not move away */
13165 if (IS_PLAYER(test_x, test_y))
13167 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13169 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13170 player as being hit when he is moving towards the bad thing, because
13171 the "get hit by" condition would be lost after the player stops) */
13172 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13173 return; /* player moves away from bad thing */
13178 else if (test_element == EL_PENGUIN)
13185 if (kill_x != -1 || kill_y != -1)
13187 if (IS_PLAYER(kill_x, kill_y))
13189 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13191 if (player->shield_deadly_time_left > 0 &&
13192 !IS_INDESTRUCTIBLE(bad_element))
13193 Bang(bad_x, bad_y);
13194 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13195 KillPlayer(player);
13198 Bang(kill_x, kill_y);
13202 void TestIfPlayerTouchesBadThing(int x, int y)
13204 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13207 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13209 TestIfGoodThingHitsBadThing(x, y, move_dir);
13212 void TestIfBadThingTouchesPlayer(int x, int y)
13214 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13217 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13219 TestIfBadThingHitsGoodThing(x, y, move_dir);
13222 void TestIfFriendTouchesBadThing(int x, int y)
13224 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13227 void TestIfBadThingTouchesFriend(int x, int y)
13229 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13232 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13234 int i, kill_x = bad_x, kill_y = bad_y;
13235 static int xy[4][2] =
13243 for (i = 0; i < NUM_DIRECTIONS; i++)
13247 x = bad_x + xy[i][0];
13248 y = bad_y + xy[i][1];
13249 if (!IN_LEV_FIELD(x, y))
13252 element = Feld[x][y];
13253 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13254 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13262 if (kill_x != bad_x || kill_y != bad_y)
13263 Bang(bad_x, bad_y);
13266 void KillPlayer(struct PlayerInfo *player)
13268 int jx = player->jx, jy = player->jy;
13270 if (!player->active)
13274 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13275 player->killed, player->active, player->reanimated);
13278 /* the following code was introduced to prevent an infinite loop when calling
13280 -> CheckTriggeredElementChangeExt()
13281 -> ExecuteCustomElementAction()
13283 -> (infinitely repeating the above sequence of function calls)
13284 which occurs when killing the player while having a CE with the setting
13285 "kill player X when explosion of <player X>"; the solution using a new
13286 field "player->killed" was chosen for backwards compatibility, although
13287 clever use of the fields "player->active" etc. would probably also work */
13289 if (player->killed)
13293 player->killed = TRUE;
13295 /* remove accessible field at the player's position */
13296 Feld[jx][jy] = EL_EMPTY;
13298 /* deactivate shield (else Bang()/Explode() would not work right) */
13299 player->shield_normal_time_left = 0;
13300 player->shield_deadly_time_left = 0;
13303 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13304 player->killed, player->active, player->reanimated);
13310 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13311 player->killed, player->active, player->reanimated);
13314 if (player->reanimated) /* killed player may have been reanimated */
13315 player->killed = player->reanimated = FALSE;
13317 BuryPlayer(player);
13320 static void KillPlayerUnlessEnemyProtected(int x, int y)
13322 if (!PLAYER_ENEMY_PROTECTED(x, y))
13323 KillPlayer(PLAYERINFO(x, y));
13326 static void KillPlayerUnlessExplosionProtected(int x, int y)
13328 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13329 KillPlayer(PLAYERINFO(x, y));
13332 void BuryPlayer(struct PlayerInfo *player)
13334 int jx = player->jx, jy = player->jy;
13336 if (!player->active)
13339 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13340 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13342 player->GameOver = TRUE;
13343 RemovePlayer(player);
13346 void RemovePlayer(struct PlayerInfo *player)
13348 int jx = player->jx, jy = player->jy;
13349 int i, found = FALSE;
13351 player->present = FALSE;
13352 player->active = FALSE;
13354 if (!ExplodeField[jx][jy])
13355 StorePlayer[jx][jy] = 0;
13357 if (player->is_moving)
13358 TEST_DrawLevelField(player->last_jx, player->last_jy);
13360 for (i = 0; i < MAX_PLAYERS; i++)
13361 if (stored_player[i].active)
13365 AllPlayersGone = TRUE;
13371 static void setFieldForSnapping(int x, int y, int element, int direction)
13373 struct ElementInfo *ei = &element_info[element];
13374 int direction_bit = MV_DIR_TO_BIT(direction);
13375 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13376 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13377 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13379 Feld[x][y] = EL_ELEMENT_SNAPPING;
13380 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13382 ResetGfxAnimation(x, y);
13384 GfxElement[x][y] = element;
13385 GfxAction[x][y] = action;
13386 GfxDir[x][y] = direction;
13387 GfxFrame[x][y] = -1;
13391 =============================================================================
13392 checkDiagonalPushing()
13393 -----------------------------------------------------------------------------
13394 check if diagonal input device direction results in pushing of object
13395 (by checking if the alternative direction is walkable, diggable, ...)
13396 =============================================================================
13399 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13400 int x, int y, int real_dx, int real_dy)
13402 int jx, jy, dx, dy, xx, yy;
13404 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13407 /* diagonal direction: check alternative direction */
13412 xx = jx + (dx == 0 ? real_dx : 0);
13413 yy = jy + (dy == 0 ? real_dy : 0);
13415 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13419 =============================================================================
13421 -----------------------------------------------------------------------------
13422 x, y: field next to player (non-diagonal) to try to dig to
13423 real_dx, real_dy: direction as read from input device (can be diagonal)
13424 =============================================================================
13427 static int DigField(struct PlayerInfo *player,
13428 int oldx, int oldy, int x, int y,
13429 int real_dx, int real_dy, int mode)
13431 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13432 boolean player_was_pushing = player->is_pushing;
13433 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13434 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13435 int jx = oldx, jy = oldy;
13436 int dx = x - jx, dy = y - jy;
13437 int nextx = x + dx, nexty = y + dy;
13438 int move_direction = (dx == -1 ? MV_LEFT :
13439 dx == +1 ? MV_RIGHT :
13441 dy == +1 ? MV_DOWN : MV_NONE);
13442 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13443 int dig_side = MV_DIR_OPPOSITE(move_direction);
13444 int old_element = Feld[jx][jy];
13445 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13448 if (is_player) /* function can also be called by EL_PENGUIN */
13450 if (player->MovPos == 0)
13452 player->is_digging = FALSE;
13453 player->is_collecting = FALSE;
13456 if (player->MovPos == 0) /* last pushing move finished */
13457 player->is_pushing = FALSE;
13459 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13461 player->is_switching = FALSE;
13462 player->push_delay = -1;
13464 return MP_NO_ACTION;
13468 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13469 old_element = Back[jx][jy];
13471 /* in case of element dropped at player position, check background */
13472 else if (Back[jx][jy] != EL_EMPTY &&
13473 game.engine_version >= VERSION_IDENT(2,2,0,0))
13474 old_element = Back[jx][jy];
13476 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13477 return MP_NO_ACTION; /* field has no opening in this direction */
13479 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13480 return MP_NO_ACTION; /* field has no opening in this direction */
13482 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13486 Feld[jx][jy] = player->artwork_element;
13487 InitMovingField(jx, jy, MV_DOWN);
13488 Store[jx][jy] = EL_ACID;
13489 ContinueMoving(jx, jy);
13490 BuryPlayer(player);
13492 return MP_DONT_RUN_INTO;
13495 if (player_can_move && DONT_RUN_INTO(element))
13497 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13499 return MP_DONT_RUN_INTO;
13502 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13503 return MP_NO_ACTION;
13505 collect_count = element_info[element].collect_count_initial;
13507 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13508 return MP_NO_ACTION;
13510 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13511 player_can_move = player_can_move_or_snap;
13513 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13514 game.engine_version >= VERSION_IDENT(2,2,0,0))
13516 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13517 player->index_bit, dig_side);
13518 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13519 player->index_bit, dig_side);
13521 if (element == EL_DC_LANDMINE)
13524 if (Feld[x][y] != element) /* field changed by snapping */
13527 return MP_NO_ACTION;
13530 if (player->gravity && is_player && !player->is_auto_moving &&
13531 canFallDown(player) && move_direction != MV_DOWN &&
13532 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13533 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13535 if (player_can_move &&
13536 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13538 int sound_element = SND_ELEMENT(element);
13539 int sound_action = ACTION_WALKING;
13541 if (IS_RND_GATE(element))
13543 if (!player->key[RND_GATE_NR(element)])
13544 return MP_NO_ACTION;
13546 else if (IS_RND_GATE_GRAY(element))
13548 if (!player->key[RND_GATE_GRAY_NR(element)])
13549 return MP_NO_ACTION;
13551 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13553 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13554 return MP_NO_ACTION;
13556 else if (element == EL_EXIT_OPEN ||
13557 element == EL_EM_EXIT_OPEN ||
13558 element == EL_EM_EXIT_OPENING ||
13559 element == EL_STEEL_EXIT_OPEN ||
13560 element == EL_EM_STEEL_EXIT_OPEN ||
13561 element == EL_EM_STEEL_EXIT_OPENING ||
13562 element == EL_SP_EXIT_OPEN ||
13563 element == EL_SP_EXIT_OPENING)
13565 sound_action = ACTION_PASSING; /* player is passing exit */
13567 else if (element == EL_EMPTY)
13569 sound_action = ACTION_MOVING; /* nothing to walk on */
13572 /* play sound from background or player, whatever is available */
13573 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13574 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13576 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13578 else if (player_can_move &&
13579 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13581 if (!ACCESS_FROM(element, opposite_direction))
13582 return MP_NO_ACTION; /* field not accessible from this direction */
13584 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13585 return MP_NO_ACTION;
13587 if (IS_EM_GATE(element))
13589 if (!player->key[EM_GATE_NR(element)])
13590 return MP_NO_ACTION;
13592 else if (IS_EM_GATE_GRAY(element))
13594 if (!player->key[EM_GATE_GRAY_NR(element)])
13595 return MP_NO_ACTION;
13597 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13599 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13600 return MP_NO_ACTION;
13602 else if (IS_EMC_GATE(element))
13604 if (!player->key[EMC_GATE_NR(element)])
13605 return MP_NO_ACTION;
13607 else if (IS_EMC_GATE_GRAY(element))
13609 if (!player->key[EMC_GATE_GRAY_NR(element)])
13610 return MP_NO_ACTION;
13612 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13614 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13615 return MP_NO_ACTION;
13617 else if (element == EL_DC_GATE_WHITE ||
13618 element == EL_DC_GATE_WHITE_GRAY ||
13619 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13621 if (player->num_white_keys == 0)
13622 return MP_NO_ACTION;
13624 player->num_white_keys--;
13626 else if (IS_SP_PORT(element))
13628 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13629 element == EL_SP_GRAVITY_PORT_RIGHT ||
13630 element == EL_SP_GRAVITY_PORT_UP ||
13631 element == EL_SP_GRAVITY_PORT_DOWN)
13632 player->gravity = !player->gravity;
13633 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13634 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13635 element == EL_SP_GRAVITY_ON_PORT_UP ||
13636 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13637 player->gravity = TRUE;
13638 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13639 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13640 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13641 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13642 player->gravity = FALSE;
13645 /* automatically move to the next field with double speed */
13646 player->programmed_action = move_direction;
13648 if (player->move_delay_reset_counter == 0)
13650 player->move_delay_reset_counter = 2; /* two double speed steps */
13652 DOUBLE_PLAYER_SPEED(player);
13655 PlayLevelSoundAction(x, y, ACTION_PASSING);
13657 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13661 if (mode != DF_SNAP)
13663 GfxElement[x][y] = GFX_ELEMENT(element);
13664 player->is_digging = TRUE;
13667 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13669 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13670 player->index_bit, dig_side);
13672 if (mode == DF_SNAP)
13674 if (level.block_snap_field)
13675 setFieldForSnapping(x, y, element, move_direction);
13677 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13679 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13680 player->index_bit, dig_side);
13683 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13687 if (is_player && mode != DF_SNAP)
13689 GfxElement[x][y] = element;
13690 player->is_collecting = TRUE;
13693 if (element == EL_SPEED_PILL)
13695 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13697 else if (element == EL_EXTRA_TIME && level.time > 0)
13699 TimeLeft += level.extra_time;
13701 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13703 DisplayGameControlValues();
13705 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13707 player->shield_normal_time_left += level.shield_normal_time;
13708 if (element == EL_SHIELD_DEADLY)
13709 player->shield_deadly_time_left += level.shield_deadly_time;
13711 else if (element == EL_DYNAMITE ||
13712 element == EL_EM_DYNAMITE ||
13713 element == EL_SP_DISK_RED)
13715 if (player->inventory_size < MAX_INVENTORY_SIZE)
13716 player->inventory_element[player->inventory_size++] = element;
13718 DrawGameDoorValues();
13720 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13722 player->dynabomb_count++;
13723 player->dynabombs_left++;
13725 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13727 player->dynabomb_size++;
13729 else if (element == EL_DYNABOMB_INCREASE_POWER)
13731 player->dynabomb_xl = TRUE;
13733 else if (IS_KEY(element))
13735 player->key[KEY_NR(element)] = TRUE;
13737 DrawGameDoorValues();
13739 else if (element == EL_DC_KEY_WHITE)
13741 player->num_white_keys++;
13743 /* display white keys? */
13744 /* DrawGameDoorValues(); */
13746 else if (IS_ENVELOPE(element))
13748 player->show_envelope = element;
13750 else if (element == EL_EMC_LENSES)
13752 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13754 RedrawAllInvisibleElementsForLenses();
13756 else if (element == EL_EMC_MAGNIFIER)
13758 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13760 RedrawAllInvisibleElementsForMagnifier();
13762 else if (IS_DROPPABLE(element) ||
13763 IS_THROWABLE(element)) /* can be collected and dropped */
13767 if (collect_count == 0)
13768 player->inventory_infinite_element = element;
13770 for (i = 0; i < collect_count; i++)
13771 if (player->inventory_size < MAX_INVENTORY_SIZE)
13772 player->inventory_element[player->inventory_size++] = element;
13774 DrawGameDoorValues();
13776 else if (collect_count > 0)
13778 local_player->gems_still_needed -= collect_count;
13779 if (local_player->gems_still_needed < 0)
13780 local_player->gems_still_needed = 0;
13782 game.snapshot.collected_item = TRUE;
13784 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13786 DisplayGameControlValues();
13789 RaiseScoreElement(element);
13790 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13793 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13794 player->index_bit, dig_side);
13796 if (mode == DF_SNAP)
13798 if (level.block_snap_field)
13799 setFieldForSnapping(x, y, element, move_direction);
13801 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13803 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13804 player->index_bit, dig_side);
13807 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13809 if (mode == DF_SNAP && element != EL_BD_ROCK)
13810 return MP_NO_ACTION;
13812 if (CAN_FALL(element) && dy)
13813 return MP_NO_ACTION;
13815 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13816 !(element == EL_SPRING && level.use_spring_bug))
13817 return MP_NO_ACTION;
13819 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13820 ((move_direction & MV_VERTICAL &&
13821 ((element_info[element].move_pattern & MV_LEFT &&
13822 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13823 (element_info[element].move_pattern & MV_RIGHT &&
13824 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13825 (move_direction & MV_HORIZONTAL &&
13826 ((element_info[element].move_pattern & MV_UP &&
13827 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13828 (element_info[element].move_pattern & MV_DOWN &&
13829 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13830 return MP_NO_ACTION;
13832 /* do not push elements already moving away faster than player */
13833 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13834 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13835 return MP_NO_ACTION;
13837 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13839 if (player->push_delay_value == -1 || !player_was_pushing)
13840 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13842 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13844 if (player->push_delay_value == -1)
13845 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13847 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13849 if (!player->is_pushing)
13850 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13853 player->is_pushing = TRUE;
13854 player->is_active = TRUE;
13856 if (!(IN_LEV_FIELD(nextx, nexty) &&
13857 (IS_FREE(nextx, nexty) ||
13858 (IS_SB_ELEMENT(element) &&
13859 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13860 (IS_CUSTOM_ELEMENT(element) &&
13861 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13862 return MP_NO_ACTION;
13864 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13865 return MP_NO_ACTION;
13867 if (player->push_delay == -1) /* new pushing; restart delay */
13868 player->push_delay = 0;
13870 if (player->push_delay < player->push_delay_value &&
13871 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13872 element != EL_SPRING && element != EL_BALLOON)
13874 /* make sure that there is no move delay before next try to push */
13875 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13876 player->move_delay = 0;
13878 return MP_NO_ACTION;
13881 if (IS_CUSTOM_ELEMENT(element) &&
13882 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13884 if (!DigFieldByCE(nextx, nexty, element))
13885 return MP_NO_ACTION;
13888 if (IS_SB_ELEMENT(element))
13890 if (element == EL_SOKOBAN_FIELD_FULL)
13892 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13893 local_player->sokobanfields_still_needed++;
13896 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13898 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13899 local_player->sokobanfields_still_needed--;
13902 Feld[x][y] = EL_SOKOBAN_OBJECT;
13904 if (Back[x][y] == Back[nextx][nexty])
13905 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13906 else if (Back[x][y] != 0)
13907 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13910 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13913 if (local_player->sokobanfields_still_needed == 0 &&
13914 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13916 PlayerWins(player);
13918 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13922 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13924 InitMovingField(x, y, move_direction);
13925 GfxAction[x][y] = ACTION_PUSHING;
13927 if (mode == DF_SNAP)
13928 ContinueMoving(x, y);
13930 MovPos[x][y] = (dx != 0 ? dx : dy);
13932 Pushed[x][y] = TRUE;
13933 Pushed[nextx][nexty] = TRUE;
13935 if (game.engine_version < VERSION_IDENT(2,2,0,7))
13936 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13938 player->push_delay_value = -1; /* get new value later */
13940 /* check for element change _after_ element has been pushed */
13941 if (game.use_change_when_pushing_bug)
13943 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13944 player->index_bit, dig_side);
13945 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13946 player->index_bit, dig_side);
13949 else if (IS_SWITCHABLE(element))
13951 if (PLAYER_SWITCHING(player, x, y))
13953 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13954 player->index_bit, dig_side);
13959 player->is_switching = TRUE;
13960 player->switch_x = x;
13961 player->switch_y = y;
13963 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13965 if (element == EL_ROBOT_WHEEL)
13967 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13971 game.robot_wheel_active = TRUE;
13973 TEST_DrawLevelField(x, y);
13975 else if (element == EL_SP_TERMINAL)
13979 SCAN_PLAYFIELD(xx, yy)
13981 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13985 else if (Feld[xx][yy] == EL_SP_TERMINAL)
13987 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13989 ResetGfxAnimation(xx, yy);
13990 TEST_DrawLevelField(xx, yy);
13994 else if (IS_BELT_SWITCH(element))
13996 ToggleBeltSwitch(x, y);
13998 else if (element == EL_SWITCHGATE_SWITCH_UP ||
13999 element == EL_SWITCHGATE_SWITCH_DOWN ||
14000 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14001 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14003 ToggleSwitchgateSwitch(x, y);
14005 else if (element == EL_LIGHT_SWITCH ||
14006 element == EL_LIGHT_SWITCH_ACTIVE)
14008 ToggleLightSwitch(x, y);
14010 else if (element == EL_TIMEGATE_SWITCH ||
14011 element == EL_DC_TIMEGATE_SWITCH)
14013 ActivateTimegateSwitch(x, y);
14015 else if (element == EL_BALLOON_SWITCH_LEFT ||
14016 element == EL_BALLOON_SWITCH_RIGHT ||
14017 element == EL_BALLOON_SWITCH_UP ||
14018 element == EL_BALLOON_SWITCH_DOWN ||
14019 element == EL_BALLOON_SWITCH_NONE ||
14020 element == EL_BALLOON_SWITCH_ANY)
14022 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14023 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14024 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14025 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14026 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14029 else if (element == EL_LAMP)
14031 Feld[x][y] = EL_LAMP_ACTIVE;
14032 local_player->lights_still_needed--;
14034 ResetGfxAnimation(x, y);
14035 TEST_DrawLevelField(x, y);
14037 else if (element == EL_TIME_ORB_FULL)
14039 Feld[x][y] = EL_TIME_ORB_EMPTY;
14041 if (level.time > 0 || level.use_time_orb_bug)
14043 TimeLeft += level.time_orb_time;
14044 game.no_time_limit = FALSE;
14046 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14048 DisplayGameControlValues();
14051 ResetGfxAnimation(x, y);
14052 TEST_DrawLevelField(x, y);
14054 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14055 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14059 game.ball_state = !game.ball_state;
14061 SCAN_PLAYFIELD(xx, yy)
14063 int e = Feld[xx][yy];
14065 if (game.ball_state)
14067 if (e == EL_EMC_MAGIC_BALL)
14068 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14069 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14070 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14074 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14075 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14076 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14077 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14082 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14083 player->index_bit, dig_side);
14085 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14086 player->index_bit, dig_side);
14088 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14089 player->index_bit, dig_side);
14095 if (!PLAYER_SWITCHING(player, x, y))
14097 player->is_switching = TRUE;
14098 player->switch_x = x;
14099 player->switch_y = y;
14101 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14102 player->index_bit, dig_side);
14103 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14104 player->index_bit, dig_side);
14106 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14107 player->index_bit, dig_side);
14108 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14109 player->index_bit, dig_side);
14112 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14113 player->index_bit, dig_side);
14114 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14115 player->index_bit, dig_side);
14117 return MP_NO_ACTION;
14120 player->push_delay = -1;
14122 if (is_player) /* function can also be called by EL_PENGUIN */
14124 if (Feld[x][y] != element) /* really digged/collected something */
14126 player->is_collecting = !player->is_digging;
14127 player->is_active = TRUE;
14134 static boolean DigFieldByCE(int x, int y, int digging_element)
14136 int element = Feld[x][y];
14138 if (!IS_FREE(x, y))
14140 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14141 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14144 /* no element can dig solid indestructible elements */
14145 if (IS_INDESTRUCTIBLE(element) &&
14146 !IS_DIGGABLE(element) &&
14147 !IS_COLLECTIBLE(element))
14150 if (AmoebaNr[x][y] &&
14151 (element == EL_AMOEBA_FULL ||
14152 element == EL_BD_AMOEBA ||
14153 element == EL_AMOEBA_GROWING))
14155 AmoebaCnt[AmoebaNr[x][y]]--;
14156 AmoebaCnt2[AmoebaNr[x][y]]--;
14159 if (IS_MOVING(x, y))
14160 RemoveMovingField(x, y);
14164 TEST_DrawLevelField(x, y);
14167 /* if digged element was about to explode, prevent the explosion */
14168 ExplodeField[x][y] = EX_TYPE_NONE;
14170 PlayLevelSoundAction(x, y, action);
14173 Store[x][y] = EL_EMPTY;
14175 /* this makes it possible to leave the removed element again */
14176 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14177 Store[x][y] = element;
14182 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14184 int jx = player->jx, jy = player->jy;
14185 int x = jx + dx, y = jy + dy;
14186 int snap_direction = (dx == -1 ? MV_LEFT :
14187 dx == +1 ? MV_RIGHT :
14189 dy == +1 ? MV_DOWN : MV_NONE);
14190 boolean can_continue_snapping = (level.continuous_snapping &&
14191 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14193 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14196 if (!player->active || !IN_LEV_FIELD(x, y))
14204 if (player->MovPos == 0)
14205 player->is_pushing = FALSE;
14207 player->is_snapping = FALSE;
14209 if (player->MovPos == 0)
14211 player->is_moving = FALSE;
14212 player->is_digging = FALSE;
14213 player->is_collecting = FALSE;
14219 /* prevent snapping with already pressed snap key when not allowed */
14220 if (player->is_snapping && !can_continue_snapping)
14223 player->MovDir = snap_direction;
14225 if (player->MovPos == 0)
14227 player->is_moving = FALSE;
14228 player->is_digging = FALSE;
14229 player->is_collecting = FALSE;
14232 player->is_dropping = FALSE;
14233 player->is_dropping_pressed = FALSE;
14234 player->drop_pressed_delay = 0;
14236 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14239 player->is_snapping = TRUE;
14240 player->is_active = TRUE;
14242 if (player->MovPos == 0)
14244 player->is_moving = FALSE;
14245 player->is_digging = FALSE;
14246 player->is_collecting = FALSE;
14249 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14250 TEST_DrawLevelField(player->last_jx, player->last_jy);
14252 TEST_DrawLevelField(x, y);
14257 static boolean DropElement(struct PlayerInfo *player)
14259 int old_element, new_element;
14260 int dropx = player->jx, dropy = player->jy;
14261 int drop_direction = player->MovDir;
14262 int drop_side = drop_direction;
14263 int drop_element = get_next_dropped_element(player);
14265 /* do not drop an element on top of another element; when holding drop key
14266 pressed without moving, dropped element must move away before the next
14267 element can be dropped (this is especially important if the next element
14268 is dynamite, which can be placed on background for historical reasons) */
14269 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14272 if (IS_THROWABLE(drop_element))
14274 dropx += GET_DX_FROM_DIR(drop_direction);
14275 dropy += GET_DY_FROM_DIR(drop_direction);
14277 if (!IN_LEV_FIELD(dropx, dropy))
14281 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14282 new_element = drop_element; /* default: no change when dropping */
14284 /* check if player is active, not moving and ready to drop */
14285 if (!player->active || player->MovPos || player->drop_delay > 0)
14288 /* check if player has anything that can be dropped */
14289 if (new_element == EL_UNDEFINED)
14292 /* only set if player has anything that can be dropped */
14293 player->is_dropping_pressed = TRUE;
14295 /* check if drop key was pressed long enough for EM style dynamite */
14296 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14299 /* check if anything can be dropped at the current position */
14300 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14303 /* collected custom elements can only be dropped on empty fields */
14304 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14307 if (old_element != EL_EMPTY)
14308 Back[dropx][dropy] = old_element; /* store old element on this field */
14310 ResetGfxAnimation(dropx, dropy);
14311 ResetRandomAnimationValue(dropx, dropy);
14313 if (player->inventory_size > 0 ||
14314 player->inventory_infinite_element != EL_UNDEFINED)
14316 if (player->inventory_size > 0)
14318 player->inventory_size--;
14320 DrawGameDoorValues();
14322 if (new_element == EL_DYNAMITE)
14323 new_element = EL_DYNAMITE_ACTIVE;
14324 else if (new_element == EL_EM_DYNAMITE)
14325 new_element = EL_EM_DYNAMITE_ACTIVE;
14326 else if (new_element == EL_SP_DISK_RED)
14327 new_element = EL_SP_DISK_RED_ACTIVE;
14330 Feld[dropx][dropy] = new_element;
14332 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14333 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14334 el2img(Feld[dropx][dropy]), 0);
14336 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14338 /* needed if previous element just changed to "empty" in the last frame */
14339 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14341 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14342 player->index_bit, drop_side);
14343 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14345 player->index_bit, drop_side);
14347 TestIfElementTouchesCustomElement(dropx, dropy);
14349 else /* player is dropping a dyna bomb */
14351 player->dynabombs_left--;
14353 Feld[dropx][dropy] = new_element;
14355 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14356 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14357 el2img(Feld[dropx][dropy]), 0);
14359 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14362 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14363 InitField_WithBug1(dropx, dropy, FALSE);
14365 new_element = Feld[dropx][dropy]; /* element might have changed */
14367 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14368 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14370 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14371 MovDir[dropx][dropy] = drop_direction;
14373 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14375 /* do not cause impact style collision by dropping elements that can fall */
14376 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14379 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14380 player->is_dropping = TRUE;
14382 player->drop_pressed_delay = 0;
14383 player->is_dropping_pressed = FALSE;
14385 player->drop_x = dropx;
14386 player->drop_y = dropy;
14391 /* ------------------------------------------------------------------------- */
14392 /* game sound playing functions */
14393 /* ------------------------------------------------------------------------- */
14395 static int *loop_sound_frame = NULL;
14396 static int *loop_sound_volume = NULL;
14398 void InitPlayLevelSound()
14400 int num_sounds = getSoundListSize();
14402 checked_free(loop_sound_frame);
14403 checked_free(loop_sound_volume);
14405 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14406 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14409 static void PlayLevelSound(int x, int y, int nr)
14411 int sx = SCREENX(x), sy = SCREENY(y);
14412 int volume, stereo_position;
14413 int max_distance = 8;
14414 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14416 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14417 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14420 if (!IN_LEV_FIELD(x, y) ||
14421 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14422 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14425 volume = SOUND_MAX_VOLUME;
14427 if (!IN_SCR_FIELD(sx, sy))
14429 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14430 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14432 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14435 stereo_position = (SOUND_MAX_LEFT +
14436 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14437 (SCR_FIELDX + 2 * max_distance));
14439 if (IS_LOOP_SOUND(nr))
14441 /* This assures that quieter loop sounds do not overwrite louder ones,
14442 while restarting sound volume comparison with each new game frame. */
14444 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14447 loop_sound_volume[nr] = volume;
14448 loop_sound_frame[nr] = FrameCounter;
14451 PlaySoundExt(nr, volume, stereo_position, type);
14454 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14456 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14457 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14458 y < LEVELY(BY1) ? LEVELY(BY1) :
14459 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14463 static void PlayLevelSoundAction(int x, int y, int action)
14465 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14468 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14470 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14472 if (sound_effect != SND_UNDEFINED)
14473 PlayLevelSound(x, y, sound_effect);
14476 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14479 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14481 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14482 PlayLevelSound(x, y, sound_effect);
14485 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14487 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14489 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14490 PlayLevelSound(x, y, sound_effect);
14493 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14495 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14497 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14498 StopSound(sound_effect);
14501 static int getLevelMusicNr()
14503 if (levelset.music[level_nr] != MUS_UNDEFINED)
14504 return levelset.music[level_nr]; /* from config file */
14506 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14509 static void FadeLevelSounds()
14514 static void FadeLevelMusic()
14516 int music_nr = getLevelMusicNr();
14517 char *curr_music = getCurrentlyPlayingMusicFilename();
14518 char *next_music = getMusicInfoEntryFilename(music_nr);
14520 if (!strEqual(curr_music, next_music))
14524 void FadeLevelSoundsAndMusic()
14530 static void PlayLevelMusic()
14532 int music_nr = getLevelMusicNr();
14533 char *curr_music = getCurrentlyPlayingMusicFilename();
14534 char *next_music = getMusicInfoEntryFilename(music_nr);
14536 if (!strEqual(curr_music, next_music))
14537 PlayMusic(music_nr);
14540 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14542 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14543 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14544 int x = xx - 1 - offset;
14545 int y = yy - 1 - offset;
14550 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14554 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14558 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14562 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14566 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14570 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14574 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14577 case SAMPLE_android_clone:
14578 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14581 case SAMPLE_android_move:
14582 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14585 case SAMPLE_spring:
14586 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14590 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14594 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14597 case SAMPLE_eater_eat:
14598 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14602 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14605 case SAMPLE_collect:
14606 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14609 case SAMPLE_diamond:
14610 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14613 case SAMPLE_squash:
14614 /* !!! CHECK THIS !!! */
14616 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14618 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14622 case SAMPLE_wonderfall:
14623 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14627 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14631 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14635 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14639 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14643 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14647 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14650 case SAMPLE_wonder:
14651 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14655 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14658 case SAMPLE_exit_open:
14659 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14662 case SAMPLE_exit_leave:
14663 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14666 case SAMPLE_dynamite:
14667 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14671 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14675 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14679 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14683 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14687 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14691 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14695 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14700 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14702 int element = map_element_SP_to_RND(element_sp);
14703 int action = map_action_SP_to_RND(action_sp);
14704 int offset = (setup.sp_show_border_elements ? 0 : 1);
14705 int x = xx - offset;
14706 int y = yy - offset;
14708 PlayLevelSoundElementAction(x, y, element, action);
14711 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14713 int element = map_element_MM_to_RND(element_mm);
14714 int action = map_action_MM_to_RND(action_mm);
14716 int x = xx - offset;
14717 int y = yy - offset;
14719 if (!IS_MM_ELEMENT(element))
14720 element = EL_MM_DEFAULT;
14722 PlayLevelSoundElementAction(x, y, element, action);
14725 void PlaySound_MM(int sound_mm)
14727 int sound = map_sound_MM_to_RND(sound_mm);
14729 if (sound == SND_UNDEFINED)
14735 void PlaySoundLoop_MM(int sound_mm)
14737 int sound = map_sound_MM_to_RND(sound_mm);
14739 if (sound == SND_UNDEFINED)
14742 PlaySoundLoop(sound);
14745 void StopSound_MM(int sound_mm)
14747 int sound = map_sound_MM_to_RND(sound_mm);
14749 if (sound == SND_UNDEFINED)
14755 void RaiseScore(int value)
14757 local_player->score += value;
14759 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14761 DisplayGameControlValues();
14764 void RaiseScoreElement(int element)
14769 case EL_BD_DIAMOND:
14770 case EL_EMERALD_YELLOW:
14771 case EL_EMERALD_RED:
14772 case EL_EMERALD_PURPLE:
14773 case EL_SP_INFOTRON:
14774 RaiseScore(level.score[SC_EMERALD]);
14777 RaiseScore(level.score[SC_DIAMOND]);
14780 RaiseScore(level.score[SC_CRYSTAL]);
14783 RaiseScore(level.score[SC_PEARL]);
14786 case EL_BD_BUTTERFLY:
14787 case EL_SP_ELECTRON:
14788 RaiseScore(level.score[SC_BUG]);
14791 case EL_BD_FIREFLY:
14792 case EL_SP_SNIKSNAK:
14793 RaiseScore(level.score[SC_SPACESHIP]);
14796 case EL_DARK_YAMYAM:
14797 RaiseScore(level.score[SC_YAMYAM]);
14800 RaiseScore(level.score[SC_ROBOT]);
14803 RaiseScore(level.score[SC_PACMAN]);
14806 RaiseScore(level.score[SC_NUT]);
14809 case EL_EM_DYNAMITE:
14810 case EL_SP_DISK_RED:
14811 case EL_DYNABOMB_INCREASE_NUMBER:
14812 case EL_DYNABOMB_INCREASE_SIZE:
14813 case EL_DYNABOMB_INCREASE_POWER:
14814 RaiseScore(level.score[SC_DYNAMITE]);
14816 case EL_SHIELD_NORMAL:
14817 case EL_SHIELD_DEADLY:
14818 RaiseScore(level.score[SC_SHIELD]);
14820 case EL_EXTRA_TIME:
14821 RaiseScore(level.extra_time_score);
14835 case EL_DC_KEY_WHITE:
14836 RaiseScore(level.score[SC_KEY]);
14839 RaiseScore(element_info[element].collect_score);
14844 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14846 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14848 /* closing door required in case of envelope style request dialogs */
14850 CloseDoor(DOOR_CLOSE_1);
14852 #if defined(NETWORK_AVALIABLE)
14853 if (options.network)
14854 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14859 FadeSkipNextFadeIn();
14861 SetGameStatus(GAME_MODE_MAIN);
14866 else /* continue playing the game */
14868 if (tape.playing && tape.deactivate_display)
14869 TapeDeactivateDisplayOff(TRUE);
14871 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14873 if (tape.playing && tape.deactivate_display)
14874 TapeDeactivateDisplayOn();
14878 void RequestQuitGame(boolean ask_if_really_quit)
14880 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14881 boolean skip_request = AllPlayersGone || quick_quit;
14883 RequestQuitGameExt(skip_request, quick_quit,
14884 "Do you really want to quit the game?");
14888 /* ------------------------------------------------------------------------- */
14889 /* random generator functions */
14890 /* ------------------------------------------------------------------------- */
14892 unsigned int InitEngineRandom_RND(int seed)
14894 game.num_random_calls = 0;
14896 return InitEngineRandom(seed);
14899 unsigned int RND(int max)
14903 game.num_random_calls++;
14905 return GetEngineRandom(max);
14912 /* ------------------------------------------------------------------------- */
14913 /* game engine snapshot handling functions */
14914 /* ------------------------------------------------------------------------- */
14916 struct EngineSnapshotInfo
14918 /* runtime values for custom element collect score */
14919 int collect_score[NUM_CUSTOM_ELEMENTS];
14921 /* runtime values for group element choice position */
14922 int choice_pos[NUM_GROUP_ELEMENTS];
14924 /* runtime values for belt position animations */
14925 int belt_graphic[4][NUM_BELT_PARTS];
14926 int belt_anim_mode[4][NUM_BELT_PARTS];
14929 static struct EngineSnapshotInfo engine_snapshot_rnd;
14930 static char *snapshot_level_identifier = NULL;
14931 static int snapshot_level_nr = -1;
14933 static void SaveEngineSnapshotValues_RND()
14935 static int belt_base_active_element[4] =
14937 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14938 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14939 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14940 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14944 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14946 int element = EL_CUSTOM_START + i;
14948 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14951 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14953 int element = EL_GROUP_START + i;
14955 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14958 for (i = 0; i < 4; i++)
14960 for (j = 0; j < NUM_BELT_PARTS; j++)
14962 int element = belt_base_active_element[i] + j;
14963 int graphic = el2img(element);
14964 int anim_mode = graphic_info[graphic].anim_mode;
14966 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14967 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14972 static void LoadEngineSnapshotValues_RND()
14974 unsigned int num_random_calls = game.num_random_calls;
14977 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14979 int element = EL_CUSTOM_START + i;
14981 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14984 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14986 int element = EL_GROUP_START + i;
14988 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14991 for (i = 0; i < 4; i++)
14993 for (j = 0; j < NUM_BELT_PARTS; j++)
14995 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14996 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14998 graphic_info[graphic].anim_mode = anim_mode;
15002 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15004 InitRND(tape.random_seed);
15005 for (i = 0; i < num_random_calls; i++)
15009 if (game.num_random_calls != num_random_calls)
15011 Error(ERR_INFO, "number of random calls out of sync");
15012 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15013 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15014 Error(ERR_EXIT, "this should not happen -- please debug");
15018 void FreeEngineSnapshotSingle()
15020 FreeSnapshotSingle();
15022 setString(&snapshot_level_identifier, NULL);
15023 snapshot_level_nr = -1;
15026 void FreeEngineSnapshotList()
15028 FreeSnapshotList();
15031 ListNode *SaveEngineSnapshotBuffers()
15033 ListNode *buffers = NULL;
15035 /* copy some special values to a structure better suited for the snapshot */
15037 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15038 SaveEngineSnapshotValues_RND();
15039 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15040 SaveEngineSnapshotValues_EM();
15041 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15042 SaveEngineSnapshotValues_SP(&buffers);
15043 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15044 SaveEngineSnapshotValues_MM(&buffers);
15046 /* save values stored in special snapshot structure */
15048 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15049 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15050 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15051 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15052 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15053 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15054 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15055 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15057 /* save further RND engine values */
15059 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15060 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15061 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15063 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15064 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15065 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15066 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15068 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15069 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15070 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15071 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15072 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15074 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15075 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15076 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15078 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15080 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15082 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15083 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15085 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15086 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15087 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15088 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15089 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15090 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15091 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15092 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15093 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15094 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15095 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15096 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15097 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15098 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15099 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15100 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15101 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15102 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15104 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15105 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15107 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15108 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15109 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15111 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15112 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15114 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15115 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15116 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15117 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15118 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15120 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15121 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15124 ListNode *node = engine_snapshot_list_rnd;
15127 while (node != NULL)
15129 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15134 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15140 void SaveEngineSnapshotSingle()
15142 ListNode *buffers = SaveEngineSnapshotBuffers();
15144 /* finally save all snapshot buffers to single snapshot */
15145 SaveSnapshotSingle(buffers);
15147 /* save level identification information */
15148 setString(&snapshot_level_identifier, leveldir_current->identifier);
15149 snapshot_level_nr = level_nr;
15152 boolean CheckSaveEngineSnapshotToList()
15154 boolean save_snapshot =
15155 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15156 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15157 game.snapshot.changed_action) ||
15158 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15159 game.snapshot.collected_item));
15161 game.snapshot.changed_action = FALSE;
15162 game.snapshot.collected_item = FALSE;
15163 game.snapshot.save_snapshot = save_snapshot;
15165 return save_snapshot;
15168 void SaveEngineSnapshotToList()
15170 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15174 ListNode *buffers = SaveEngineSnapshotBuffers();
15176 /* finally save all snapshot buffers to snapshot list */
15177 SaveSnapshotToList(buffers);
15180 void SaveEngineSnapshotToListInitial()
15182 FreeEngineSnapshotList();
15184 SaveEngineSnapshotToList();
15187 void LoadEngineSnapshotValues()
15189 /* restore special values from snapshot structure */
15191 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15192 LoadEngineSnapshotValues_RND();
15193 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15194 LoadEngineSnapshotValues_EM();
15195 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15196 LoadEngineSnapshotValues_SP();
15197 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15198 LoadEngineSnapshotValues_MM();
15201 void LoadEngineSnapshotSingle()
15203 LoadSnapshotSingle();
15205 LoadEngineSnapshotValues();
15208 void LoadEngineSnapshot_Undo(int steps)
15210 LoadSnapshotFromList_Older(steps);
15212 LoadEngineSnapshotValues();
15215 void LoadEngineSnapshot_Redo(int steps)
15217 LoadSnapshotFromList_Newer(steps);
15219 LoadEngineSnapshotValues();
15222 boolean CheckEngineSnapshotSingle()
15224 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15225 snapshot_level_nr == level_nr);
15228 boolean CheckEngineSnapshotList()
15230 return CheckSnapshotList();
15234 /* ---------- new game button stuff ---------------------------------------- */
15242 } gamebutton_info[NUM_GAME_BUTTONS] =
15245 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15246 GAME_CTRL_ID_STOP, "stop game"
15249 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15250 GAME_CTRL_ID_PAUSE, "pause game"
15253 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15254 GAME_CTRL_ID_PLAY, "play game"
15257 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15258 GAME_CTRL_ID_UNDO, "undo step"
15261 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15262 GAME_CTRL_ID_REDO, "redo step"
15265 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15266 GAME_CTRL_ID_SAVE, "save game"
15269 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15270 GAME_CTRL_ID_PAUSE2, "pause game"
15273 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15274 GAME_CTRL_ID_LOAD, "load game"
15277 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15278 SOUND_CTRL_ID_MUSIC, "background music on/off"
15281 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15282 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
15285 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15286 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
15290 void CreateGameButtons()
15294 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15296 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15297 struct XY *pos = gamebutton_info[i].pos;
15298 struct GadgetInfo *gi;
15301 unsigned int event_mask;
15302 int base_x = (tape.show_game_buttons ? VX : DX);
15303 int base_y = (tape.show_game_buttons ? VY : DY);
15304 int gd_x = gfx->src_x;
15305 int gd_y = gfx->src_y;
15306 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15307 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15308 int gd_xa = gfx->src_x + gfx->active_xoffset;
15309 int gd_ya = gfx->src_y + gfx->active_yoffset;
15310 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15311 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15314 if (gfx->bitmap == NULL)
15316 game_gadget[id] = NULL;
15321 if (id == GAME_CTRL_ID_STOP ||
15322 id == GAME_CTRL_ID_PLAY ||
15323 id == GAME_CTRL_ID_SAVE ||
15324 id == GAME_CTRL_ID_LOAD)
15326 button_type = GD_TYPE_NORMAL_BUTTON;
15328 event_mask = GD_EVENT_RELEASED;
15330 else if (id == GAME_CTRL_ID_UNDO ||
15331 id == GAME_CTRL_ID_REDO)
15333 button_type = GD_TYPE_NORMAL_BUTTON;
15335 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15339 button_type = GD_TYPE_CHECK_BUTTON;
15341 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15342 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15343 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15344 event_mask = GD_EVENT_PRESSED;
15347 gi = CreateGadget(GDI_CUSTOM_ID, id,
15348 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15349 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15350 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15351 GDI_WIDTH, gfx->width,
15352 GDI_HEIGHT, gfx->height,
15353 GDI_TYPE, button_type,
15354 GDI_STATE, GD_BUTTON_UNPRESSED,
15355 GDI_CHECKED, checked,
15356 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15357 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15358 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15359 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15360 GDI_DIRECT_DRAW, FALSE,
15361 GDI_EVENT_MASK, event_mask,
15362 GDI_CALLBACK_ACTION, HandleGameButtons,
15366 Error(ERR_EXIT, "cannot create gadget");
15368 game_gadget[id] = gi;
15372 void FreeGameButtons()
15376 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15377 FreeGadget(game_gadget[i]);
15380 static void UnmapGameButtonsAtSamePosition(int id)
15384 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15386 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15387 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15388 UnmapGadget(game_gadget[i]);
15391 static void UnmapGameButtonsAtSamePosition_All()
15393 if (setup.show_snapshot_buttons)
15395 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15396 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15397 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15401 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15402 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15403 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15407 static void MapGameButtonsAtSamePosition(int id)
15411 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15413 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15414 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15415 MapGadget(game_gadget[i]);
15417 UnmapGameButtonsAtSamePosition_All();
15420 void MapUndoRedoButtons()
15422 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15423 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15425 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15426 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15428 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15431 void UnmapUndoRedoButtons()
15433 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15434 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15436 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15437 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15439 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15442 void MapGameButtons()
15446 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15447 if (i != GAME_CTRL_ID_UNDO &&
15448 i != GAME_CTRL_ID_REDO)
15449 MapGadget(game_gadget[i]);
15451 UnmapGameButtonsAtSamePosition_All();
15453 RedrawGameButtons();
15456 void UnmapGameButtons()
15460 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15461 UnmapGadget(game_gadget[i]);
15464 void RedrawGameButtons()
15468 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15469 RedrawGadget(game_gadget[i]);
15471 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15472 redraw_mask &= ~REDRAW_ALL;
15475 void GameUndoRedoExt()
15477 ClearPlayerAction();
15479 tape.pausing = TRUE;
15482 UpdateAndDisplayGameControlValues();
15484 DrawCompleteVideoDisplay();
15485 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15486 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15487 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15492 void GameUndo(int steps)
15494 if (!CheckEngineSnapshotList())
15497 LoadEngineSnapshot_Undo(steps);
15502 void GameRedo(int steps)
15504 if (!CheckEngineSnapshotList())
15507 LoadEngineSnapshot_Redo(steps);
15512 static void HandleGameButtonsExt(int id, int button)
15514 static boolean game_undo_executed = FALSE;
15515 int steps = BUTTON_STEPSIZE(button);
15516 boolean handle_game_buttons =
15517 (game_status == GAME_MODE_PLAYING ||
15518 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15520 if (!handle_game_buttons)
15525 case GAME_CTRL_ID_STOP:
15526 if (game_status == GAME_MODE_MAIN)
15532 RequestQuitGame(TRUE);
15536 case GAME_CTRL_ID_PAUSE:
15537 case GAME_CTRL_ID_PAUSE2:
15538 if (options.network && game_status == GAME_MODE_PLAYING)
15540 #if defined(NETWORK_AVALIABLE)
15542 SendToServer_ContinuePlaying();
15544 SendToServer_PausePlaying();
15548 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15550 game_undo_executed = FALSE;
15554 case GAME_CTRL_ID_PLAY:
15555 if (game_status == GAME_MODE_MAIN)
15557 StartGameActions(options.network, setup.autorecord, level.random_seed);
15559 else if (tape.pausing)
15561 #if defined(NETWORK_AVALIABLE)
15562 if (options.network)
15563 SendToServer_ContinuePlaying();
15566 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15570 case GAME_CTRL_ID_UNDO:
15571 // Important: When using "save snapshot when collecting an item" mode,
15572 // load last (current) snapshot for first "undo" after pressing "pause"
15573 // (else the last-but-one snapshot would be loaded, because the snapshot
15574 // pointer already points to the last snapshot when pressing "pause",
15575 // which is fine for "every step/move" mode, but not for "every collect")
15576 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15577 !game_undo_executed)
15580 game_undo_executed = TRUE;
15585 case GAME_CTRL_ID_REDO:
15589 case GAME_CTRL_ID_SAVE:
15593 case GAME_CTRL_ID_LOAD:
15597 case SOUND_CTRL_ID_MUSIC:
15598 if (setup.sound_music)
15600 setup.sound_music = FALSE;
15604 else if (audio.music_available)
15606 setup.sound = setup.sound_music = TRUE;
15608 SetAudioMode(setup.sound);
15614 case SOUND_CTRL_ID_LOOPS:
15615 if (setup.sound_loops)
15616 setup.sound_loops = FALSE;
15617 else if (audio.loops_available)
15619 setup.sound = setup.sound_loops = TRUE;
15621 SetAudioMode(setup.sound);
15625 case SOUND_CTRL_ID_SIMPLE:
15626 if (setup.sound_simple)
15627 setup.sound_simple = FALSE;
15628 else if (audio.sound_available)
15630 setup.sound = setup.sound_simple = TRUE;
15632 SetAudioMode(setup.sound);
15641 static void HandleGameButtons(struct GadgetInfo *gi)
15643 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15646 void HandleSoundButtonKeys(Key key)
15649 if (key == setup.shortcut.sound_simple)
15650 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15651 else if (key == setup.shortcut.sound_loops)
15652 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15653 else if (key == setup.shortcut.sound_music)
15654 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);