1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
26 #define DEBUG_INIT_PLAYER 1
27 #define DEBUG_PLAYER_ACTIONS 0
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE FALSE
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX 0
34 #define USE_QUICKSAND_IMPACT_BUGFIX 0
35 #define USE_DELAYED_GFX_REDRAW 0
36 #define USE_NEW_PLAYER_ASSIGNMENTS 1
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y) \
40 GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y) \
42 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
44 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y) \
46 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
48 #define TEST_DrawLevelField(x, y) \
50 #define TEST_DrawLevelFieldCrumbled(x, y) \
51 DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
53 DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y) \
55 DrawTwinkleOnField(x, y)
64 /* for MovePlayer() */
65 #define MP_NO_ACTION 0
68 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
70 /* for ScrollPlayer() */
72 #define SCROLL_GO_ON 1
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START 0
76 #define EX_TYPE_NONE 0
77 #define EX_TYPE_NORMAL (1 << 0)
78 #define EX_TYPE_CENTER (1 << 1)
79 #define EX_TYPE_BORDER (1 << 2)
80 #define EX_TYPE_CROSS (1 << 3)
81 #define EX_TYPE_DYNA (1 << 4)
82 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
84 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER 0
91 #define GAME_PANEL_GEMS 1
92 #define GAME_PANEL_INVENTORY_COUNT 2
93 #define GAME_PANEL_INVENTORY_FIRST_1 3
94 #define GAME_PANEL_INVENTORY_FIRST_2 4
95 #define GAME_PANEL_INVENTORY_FIRST_3 5
96 #define GAME_PANEL_INVENTORY_FIRST_4 6
97 #define GAME_PANEL_INVENTORY_FIRST_5 7
98 #define GAME_PANEL_INVENTORY_FIRST_6 8
99 #define GAME_PANEL_INVENTORY_FIRST_7 9
100 #define GAME_PANEL_INVENTORY_FIRST_8 10
101 #define GAME_PANEL_INVENTORY_LAST_1 11
102 #define GAME_PANEL_INVENTORY_LAST_2 12
103 #define GAME_PANEL_INVENTORY_LAST_3 13
104 #define GAME_PANEL_INVENTORY_LAST_4 14
105 #define GAME_PANEL_INVENTORY_LAST_5 15
106 #define GAME_PANEL_INVENTORY_LAST_6 16
107 #define GAME_PANEL_INVENTORY_LAST_7 17
108 #define GAME_PANEL_INVENTORY_LAST_8 18
109 #define GAME_PANEL_KEY_1 19
110 #define GAME_PANEL_KEY_2 20
111 #define GAME_PANEL_KEY_3 21
112 #define GAME_PANEL_KEY_4 22
113 #define GAME_PANEL_KEY_5 23
114 #define GAME_PANEL_KEY_6 24
115 #define GAME_PANEL_KEY_7 25
116 #define GAME_PANEL_KEY_8 26
117 #define GAME_PANEL_KEY_WHITE 27
118 #define GAME_PANEL_KEY_WHITE_COUNT 28
119 #define GAME_PANEL_SCORE 29
120 #define GAME_PANEL_HIGHSCORE 30
121 #define GAME_PANEL_TIME 31
122 #define GAME_PANEL_TIME_HH 32
123 #define GAME_PANEL_TIME_MM 33
124 #define GAME_PANEL_TIME_SS 34
125 #define GAME_PANEL_TIME_ANIM 35
126 #define GAME_PANEL_HEALTH 36
127 #define GAME_PANEL_HEALTH_ANIM 37
128 #define GAME_PANEL_FRAME 38
129 #define GAME_PANEL_SHIELD_NORMAL 39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME 40
131 #define GAME_PANEL_SHIELD_DEADLY 41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME 42
133 #define GAME_PANEL_EXIT 43
134 #define GAME_PANEL_EMC_MAGIC_BALL 44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45
136 #define GAME_PANEL_LIGHT_SWITCH 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME 47
138 #define GAME_PANEL_TIMEGATE_SWITCH 48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 49
140 #define GAME_PANEL_SWITCHGATE_SWITCH 50
141 #define GAME_PANEL_EMC_LENSES 51
142 #define GAME_PANEL_EMC_LENSES_TIME 52
143 #define GAME_PANEL_EMC_MAGNIFIER 53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME 54
145 #define GAME_PANEL_BALLOON_SWITCH 55
146 #define GAME_PANEL_DYNABOMB_NUMBER 56
147 #define GAME_PANEL_DYNABOMB_SIZE 57
148 #define GAME_PANEL_DYNABOMB_POWER 58
149 #define GAME_PANEL_PENGUINS 59
150 #define GAME_PANEL_SOKOBAN_OBJECTS 60
151 #define GAME_PANEL_SOKOBAN_FIELDS 61
152 #define GAME_PANEL_ROBOT_WHEEL 62
153 #define GAME_PANEL_CONVEYOR_BELT_1 63
154 #define GAME_PANEL_CONVEYOR_BELT_2 64
155 #define GAME_PANEL_CONVEYOR_BELT_3 65
156 #define GAME_PANEL_CONVEYOR_BELT_4 66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70
161 #define GAME_PANEL_MAGIC_WALL 71
162 #define GAME_PANEL_MAGIC_WALL_TIME 72
163 #define GAME_PANEL_GRAVITY_STATE 73
164 #define GAME_PANEL_GRAPHIC_1 74
165 #define GAME_PANEL_GRAPHIC_2 75
166 #define GAME_PANEL_GRAPHIC_3 76
167 #define GAME_PANEL_GRAPHIC_4 77
168 #define GAME_PANEL_GRAPHIC_5 78
169 #define GAME_PANEL_GRAPHIC_6 79
170 #define GAME_PANEL_GRAPHIC_7 80
171 #define GAME_PANEL_GRAPHIC_8 81
172 #define GAME_PANEL_ELEMENT_1 82
173 #define GAME_PANEL_ELEMENT_2 83
174 #define GAME_PANEL_ELEMENT_3 84
175 #define GAME_PANEL_ELEMENT_4 85
176 #define GAME_PANEL_ELEMENT_5 86
177 #define GAME_PANEL_ELEMENT_6 87
178 #define GAME_PANEL_ELEMENT_7 88
179 #define GAME_PANEL_ELEMENT_8 89
180 #define GAME_PANEL_ELEMENT_COUNT_1 90
181 #define GAME_PANEL_ELEMENT_COUNT_2 91
182 #define GAME_PANEL_ELEMENT_COUNT_3 92
183 #define GAME_PANEL_ELEMENT_COUNT_4 93
184 #define GAME_PANEL_ELEMENT_COUNT_5 94
185 #define GAME_PANEL_ELEMENT_COUNT_6 95
186 #define GAME_PANEL_ELEMENT_COUNT_7 96
187 #define GAME_PANEL_ELEMENT_COUNT_8 97
188 #define GAME_PANEL_CE_SCORE_1 98
189 #define GAME_PANEL_CE_SCORE_2 99
190 #define GAME_PANEL_CE_SCORE_3 100
191 #define GAME_PANEL_CE_SCORE_4 101
192 #define GAME_PANEL_CE_SCORE_5 102
193 #define GAME_PANEL_CE_SCORE_6 103
194 #define GAME_PANEL_CE_SCORE_7 104
195 #define GAME_PANEL_CE_SCORE_8 105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT 106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT 107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT 108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT 109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT 110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT 111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT 112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT 113
204 #define GAME_PANEL_PLAYER_NAME 114
205 #define GAME_PANEL_LEVEL_NAME 115
206 #define GAME_PANEL_LEVEL_AUTHOR 116
208 #define NUM_GAME_PANEL_CONTROLS 117
210 struct GamePanelOrderInfo
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
218 struct GamePanelControlInfo
222 struct TextPosInfo *pos;
225 int graphic, graphic_active;
227 int value, last_value;
228 int frame, last_frame;
233 static struct GamePanelControlInfo game_panel_controls[] =
236 GAME_PANEL_LEVEL_NUMBER,
237 &game.panel.level_number,
246 GAME_PANEL_INVENTORY_COUNT,
247 &game.panel.inventory_count,
251 GAME_PANEL_INVENTORY_FIRST_1,
252 &game.panel.inventory_first[0],
256 GAME_PANEL_INVENTORY_FIRST_2,
257 &game.panel.inventory_first[1],
261 GAME_PANEL_INVENTORY_FIRST_3,
262 &game.panel.inventory_first[2],
266 GAME_PANEL_INVENTORY_FIRST_4,
267 &game.panel.inventory_first[3],
271 GAME_PANEL_INVENTORY_FIRST_5,
272 &game.panel.inventory_first[4],
276 GAME_PANEL_INVENTORY_FIRST_6,
277 &game.panel.inventory_first[5],
281 GAME_PANEL_INVENTORY_FIRST_7,
282 &game.panel.inventory_first[6],
286 GAME_PANEL_INVENTORY_FIRST_8,
287 &game.panel.inventory_first[7],
291 GAME_PANEL_INVENTORY_LAST_1,
292 &game.panel.inventory_last[0],
296 GAME_PANEL_INVENTORY_LAST_2,
297 &game.panel.inventory_last[1],
301 GAME_PANEL_INVENTORY_LAST_3,
302 &game.panel.inventory_last[2],
306 GAME_PANEL_INVENTORY_LAST_4,
307 &game.panel.inventory_last[3],
311 GAME_PANEL_INVENTORY_LAST_5,
312 &game.panel.inventory_last[4],
316 GAME_PANEL_INVENTORY_LAST_6,
317 &game.panel.inventory_last[5],
321 GAME_PANEL_INVENTORY_LAST_7,
322 &game.panel.inventory_last[6],
326 GAME_PANEL_INVENTORY_LAST_8,
327 &game.panel.inventory_last[7],
371 GAME_PANEL_KEY_WHITE,
372 &game.panel.key_white,
376 GAME_PANEL_KEY_WHITE_COUNT,
377 &game.panel.key_white_count,
386 GAME_PANEL_HIGHSCORE,
387 &game.panel.highscore,
411 GAME_PANEL_TIME_ANIM,
412 &game.panel.time_anim,
415 IMG_GFX_GAME_PANEL_TIME_ANIM,
416 IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
424 GAME_PANEL_HEALTH_ANIM,
425 &game.panel.health_anim,
428 IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429 IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
437 GAME_PANEL_SHIELD_NORMAL,
438 &game.panel.shield_normal,
442 GAME_PANEL_SHIELD_NORMAL_TIME,
443 &game.panel.shield_normal_time,
447 GAME_PANEL_SHIELD_DEADLY,
448 &game.panel.shield_deadly,
452 GAME_PANEL_SHIELD_DEADLY_TIME,
453 &game.panel.shield_deadly_time,
462 GAME_PANEL_EMC_MAGIC_BALL,
463 &game.panel.emc_magic_ball,
467 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468 &game.panel.emc_magic_ball_switch,
472 GAME_PANEL_LIGHT_SWITCH,
473 &game.panel.light_switch,
477 GAME_PANEL_LIGHT_SWITCH_TIME,
478 &game.panel.light_switch_time,
482 GAME_PANEL_TIMEGATE_SWITCH,
483 &game.panel.timegate_switch,
487 GAME_PANEL_TIMEGATE_SWITCH_TIME,
488 &game.panel.timegate_switch_time,
492 GAME_PANEL_SWITCHGATE_SWITCH,
493 &game.panel.switchgate_switch,
497 GAME_PANEL_EMC_LENSES,
498 &game.panel.emc_lenses,
502 GAME_PANEL_EMC_LENSES_TIME,
503 &game.panel.emc_lenses_time,
507 GAME_PANEL_EMC_MAGNIFIER,
508 &game.panel.emc_magnifier,
512 GAME_PANEL_EMC_MAGNIFIER_TIME,
513 &game.panel.emc_magnifier_time,
517 GAME_PANEL_BALLOON_SWITCH,
518 &game.panel.balloon_switch,
522 GAME_PANEL_DYNABOMB_NUMBER,
523 &game.panel.dynabomb_number,
527 GAME_PANEL_DYNABOMB_SIZE,
528 &game.panel.dynabomb_size,
532 GAME_PANEL_DYNABOMB_POWER,
533 &game.panel.dynabomb_power,
538 &game.panel.penguins,
542 GAME_PANEL_SOKOBAN_OBJECTS,
543 &game.panel.sokoban_objects,
547 GAME_PANEL_SOKOBAN_FIELDS,
548 &game.panel.sokoban_fields,
552 GAME_PANEL_ROBOT_WHEEL,
553 &game.panel.robot_wheel,
557 GAME_PANEL_CONVEYOR_BELT_1,
558 &game.panel.conveyor_belt[0],
562 GAME_PANEL_CONVEYOR_BELT_2,
563 &game.panel.conveyor_belt[1],
567 GAME_PANEL_CONVEYOR_BELT_3,
568 &game.panel.conveyor_belt[2],
572 GAME_PANEL_CONVEYOR_BELT_4,
573 &game.panel.conveyor_belt[3],
577 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578 &game.panel.conveyor_belt_switch[0],
582 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583 &game.panel.conveyor_belt_switch[1],
587 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588 &game.panel.conveyor_belt_switch[2],
592 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593 &game.panel.conveyor_belt_switch[3],
597 GAME_PANEL_MAGIC_WALL,
598 &game.panel.magic_wall,
602 GAME_PANEL_MAGIC_WALL_TIME,
603 &game.panel.magic_wall_time,
607 GAME_PANEL_GRAVITY_STATE,
608 &game.panel.gravity_state,
612 GAME_PANEL_GRAPHIC_1,
613 &game.panel.graphic[0],
617 GAME_PANEL_GRAPHIC_2,
618 &game.panel.graphic[1],
622 GAME_PANEL_GRAPHIC_3,
623 &game.panel.graphic[2],
627 GAME_PANEL_GRAPHIC_4,
628 &game.panel.graphic[3],
632 GAME_PANEL_GRAPHIC_5,
633 &game.panel.graphic[4],
637 GAME_PANEL_GRAPHIC_6,
638 &game.panel.graphic[5],
642 GAME_PANEL_GRAPHIC_7,
643 &game.panel.graphic[6],
647 GAME_PANEL_GRAPHIC_8,
648 &game.panel.graphic[7],
652 GAME_PANEL_ELEMENT_1,
653 &game.panel.element[0],
657 GAME_PANEL_ELEMENT_2,
658 &game.panel.element[1],
662 GAME_PANEL_ELEMENT_3,
663 &game.panel.element[2],
667 GAME_PANEL_ELEMENT_4,
668 &game.panel.element[3],
672 GAME_PANEL_ELEMENT_5,
673 &game.panel.element[4],
677 GAME_PANEL_ELEMENT_6,
678 &game.panel.element[5],
682 GAME_PANEL_ELEMENT_7,
683 &game.panel.element[6],
687 GAME_PANEL_ELEMENT_8,
688 &game.panel.element[7],
692 GAME_PANEL_ELEMENT_COUNT_1,
693 &game.panel.element_count[0],
697 GAME_PANEL_ELEMENT_COUNT_2,
698 &game.panel.element_count[1],
702 GAME_PANEL_ELEMENT_COUNT_3,
703 &game.panel.element_count[2],
707 GAME_PANEL_ELEMENT_COUNT_4,
708 &game.panel.element_count[3],
712 GAME_PANEL_ELEMENT_COUNT_5,
713 &game.panel.element_count[4],
717 GAME_PANEL_ELEMENT_COUNT_6,
718 &game.panel.element_count[5],
722 GAME_PANEL_ELEMENT_COUNT_7,
723 &game.panel.element_count[6],
727 GAME_PANEL_ELEMENT_COUNT_8,
728 &game.panel.element_count[7],
732 GAME_PANEL_CE_SCORE_1,
733 &game.panel.ce_score[0],
737 GAME_PANEL_CE_SCORE_2,
738 &game.panel.ce_score[1],
742 GAME_PANEL_CE_SCORE_3,
743 &game.panel.ce_score[2],
747 GAME_PANEL_CE_SCORE_4,
748 &game.panel.ce_score[3],
752 GAME_PANEL_CE_SCORE_5,
753 &game.panel.ce_score[4],
757 GAME_PANEL_CE_SCORE_6,
758 &game.panel.ce_score[5],
762 GAME_PANEL_CE_SCORE_7,
763 &game.panel.ce_score[6],
767 GAME_PANEL_CE_SCORE_8,
768 &game.panel.ce_score[7],
772 GAME_PANEL_CE_SCORE_1_ELEMENT,
773 &game.panel.ce_score_element[0],
777 GAME_PANEL_CE_SCORE_2_ELEMENT,
778 &game.panel.ce_score_element[1],
782 GAME_PANEL_CE_SCORE_3_ELEMENT,
783 &game.panel.ce_score_element[2],
787 GAME_PANEL_CE_SCORE_4_ELEMENT,
788 &game.panel.ce_score_element[3],
792 GAME_PANEL_CE_SCORE_5_ELEMENT,
793 &game.panel.ce_score_element[4],
797 GAME_PANEL_CE_SCORE_6_ELEMENT,
798 &game.panel.ce_score_element[5],
802 GAME_PANEL_CE_SCORE_7_ELEMENT,
803 &game.panel.ce_score_element[6],
807 GAME_PANEL_CE_SCORE_8_ELEMENT,
808 &game.panel.ce_score_element[7],
812 GAME_PANEL_PLAYER_NAME,
813 &game.panel.player_name,
817 GAME_PANEL_LEVEL_NAME,
818 &game.panel.level_name,
822 GAME_PANEL_LEVEL_AUTHOR,
823 &game.panel.level_author,
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING 3
836 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION 2
838 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF -1
842 #define INITIAL_MOVE_DELAY_ON 0
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED 32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED 4
848 #define MOVE_DELAY_MAX_SPEED 1
850 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
853 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
858 (x) > SBX_Right + MIDPOSX ? SBX_Right :\
860 #define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861 (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 #define MM_HEALTH(x) (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP 0
1009 #define GAME_CTRL_ID_PAUSE 1
1010 #define GAME_CTRL_ID_PLAY 2
1011 #define GAME_CTRL_ID_UNDO 3
1012 #define GAME_CTRL_ID_REDO 4
1013 #define GAME_CTRL_ID_SAVE 5
1014 #define GAME_CTRL_ID_PAUSE2 6
1015 #define GAME_CTRL_ID_LOAD 7
1016 #define GAME_CTRL_ID_PANEL_STOP 8
1017 #define GAME_CTRL_ID_PANEL_PAUSE 9
1018 #define GAME_CTRL_ID_PANEL_PLAY 10
1019 #define SOUND_CTRL_ID_MUSIC 11
1020 #define SOUND_CTRL_ID_LOOPS 12
1021 #define SOUND_CTRL_ID_SIMPLE 13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC 14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS 15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE 16
1026 #define NUM_GAME_BUTTONS 17
1029 /* forward declaration for internal use */
1031 static void CreateField(int, int, int);
1033 static void ResetGfxAnimation(int, int);
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev) \
1065 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1067 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1069 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1071 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev) \
1075 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1077 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1079 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1091 static void HandleGameButtons(struct GadgetInfo *);
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1125 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1127 if (recursion_loop_detected) \
1130 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1132 recursion_loop_detected = TRUE; \
1133 recursion_loop_element = (e); \
1136 recursion_loop_depth++; \
1139 #define RECURSION_LOOP_DETECTION_END() \
1141 recursion_loop_depth--; \
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1148 static int map_player_action[MAX_PLAYERS];
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after */
1153 /* a specified time, eventually calling a function when changing */
1154 /* ------------------------------------------------------------------------- */
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1174 struct ChangingElementInfo
1179 void (*pre_change_function)(int x, int y);
1180 void (*change_function)(int x, int y);
1181 void (*post_change_function)(int x, int y);
1184 static struct ChangingElementInfo change_delay_list[] =
1219 EL_STEEL_EXIT_OPENING,
1227 EL_STEEL_EXIT_CLOSING,
1228 EL_STEEL_EXIT_CLOSED,
1251 EL_EM_STEEL_EXIT_OPENING,
1252 EL_EM_STEEL_EXIT_OPEN,
1259 EL_EM_STEEL_EXIT_CLOSING,
1283 EL_SWITCHGATE_OPENING,
1291 EL_SWITCHGATE_CLOSING,
1292 EL_SWITCHGATE_CLOSED,
1299 EL_TIMEGATE_OPENING,
1307 EL_TIMEGATE_CLOSING,
1316 EL_ACID_SPLASH_LEFT,
1324 EL_ACID_SPLASH_RIGHT,
1333 EL_SP_BUGGY_BASE_ACTIVATING,
1340 EL_SP_BUGGY_BASE_ACTIVATING,
1341 EL_SP_BUGGY_BASE_ACTIVE,
1348 EL_SP_BUGGY_BASE_ACTIVE,
1372 EL_ROBOT_WHEEL_ACTIVE,
1380 EL_TIMEGATE_SWITCH_ACTIVE,
1388 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389 EL_DC_TIMEGATE_SWITCH,
1396 EL_EMC_MAGIC_BALL_ACTIVE,
1397 EL_EMC_MAGIC_BALL_ACTIVE,
1404 EL_EMC_SPRING_BUMPER_ACTIVE,
1405 EL_EMC_SPRING_BUMPER,
1412 EL_DIAGONAL_SHRINKING,
1420 EL_DIAGONAL_GROWING,
1441 int push_delay_fixed, push_delay_random;
1445 { EL_SPRING, 0, 0 },
1446 { EL_BALLOON, 0, 0 },
1448 { EL_SOKOBAN_OBJECT, 2, 0 },
1449 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1450 { EL_SATELLITE, 2, 0 },
1451 { EL_SP_DISK_YELLOW, 2, 0 },
1453 { EL_UNDEFINED, 0, 0 },
1461 move_stepsize_list[] =
1463 { EL_AMOEBA_DROP, 2 },
1464 { EL_AMOEBA_DROPPING, 2 },
1465 { EL_QUICKSAND_FILLING, 1 },
1466 { EL_QUICKSAND_EMPTYING, 1 },
1467 { EL_QUICKSAND_FAST_FILLING, 2 },
1468 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469 { EL_MAGIC_WALL_FILLING, 2 },
1470 { EL_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_BD_MAGIC_WALL_FILLING, 2 },
1472 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1473 { EL_DC_MAGIC_WALL_FILLING, 2 },
1474 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1476 { EL_UNDEFINED, 0 },
1484 collect_count_list[] =
1487 { EL_BD_DIAMOND, 1 },
1488 { EL_EMERALD_YELLOW, 1 },
1489 { EL_EMERALD_RED, 1 },
1490 { EL_EMERALD_PURPLE, 1 },
1492 { EL_SP_INFOTRON, 1 },
1496 { EL_UNDEFINED, 0 },
1504 access_direction_list[] =
1506 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1508 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1509 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1510 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1511 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1512 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1513 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1514 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1515 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1516 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1518 { EL_SP_PORT_LEFT, MV_RIGHT },
1519 { EL_SP_PORT_RIGHT, MV_LEFT },
1520 { EL_SP_PORT_UP, MV_DOWN },
1521 { EL_SP_PORT_DOWN, MV_UP },
1522 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1523 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1524 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1529 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1530 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1531 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1532 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1533 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1534 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1535 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1536 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1538 { EL_UNDEFINED, MV_NONE }
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1546 IS_JUST_CHANGING(x, y))
1548 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1556 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1557 (y) >= 0 && (y) <= lev_fieldy - 1; \
1558 (y) += playfield_scan_delta_y) \
1559 for ((x) = playfield_scan_start_x; \
1560 (x) >= 0 && (x) <= lev_fieldx - 1; \
1561 (x) += playfield_scan_delta_x)
1564 void DEBUG_SetMaximumDynamite()
1568 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570 local_player->inventory_element[local_player->inventory_size++] =
1575 static void InitPlayfieldScanModeVars()
1577 if (game.use_reverse_scan_direction)
1579 playfield_scan_start_x = lev_fieldx - 1;
1580 playfield_scan_start_y = lev_fieldy - 1;
1582 playfield_scan_delta_x = -1;
1583 playfield_scan_delta_y = -1;
1587 playfield_scan_start_x = 0;
1588 playfield_scan_start_y = 0;
1590 playfield_scan_delta_x = 1;
1591 playfield_scan_delta_y = 1;
1595 static void InitPlayfieldScanMode(int mode)
1597 game.use_reverse_scan_direction =
1598 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600 InitPlayfieldScanModeVars();
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1606 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608 /* make sure that stepsize value is always a power of 2 */
1609 move_stepsize = (1 << log_2(move_stepsize));
1611 return TILEX / move_stepsize;
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1617 int player_nr = player->index_nr;
1618 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621 /* do no immediately change move delay -- the player might just be moving */
1622 player->move_delay_value_next = move_delay;
1624 /* information if player can move must be set separately */
1625 player->cannot_move = cannot_move;
1629 player->move_delay = game.initial_move_delay[player_nr];
1630 player->move_delay_value = game.initial_move_delay_value[player_nr];
1632 player->move_delay_value_next = -1;
1634 player->move_delay_reset_counter = 0;
1638 void GetPlayerConfig()
1640 GameFrameDelay = setup.game_frame_delay;
1642 if (!audio.sound_available)
1643 setup.sound_simple = FALSE;
1645 if (!audio.loops_available)
1646 setup.sound_loops = FALSE;
1648 if (!audio.music_available)
1649 setup.sound_music = FALSE;
1651 if (!video.fullscreen_available)
1652 setup.fullscreen = FALSE;
1654 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656 SetAudioMode(setup.sound);
1659 int GetElementFromGroupElement(int element)
1661 if (IS_GROUP_ELEMENT(element))
1663 struct ElementGroupInfo *group = element_info[element].group;
1664 int last_anim_random_frame = gfx.anim_random_frame;
1667 if (group->choice_mode == ANIM_RANDOM)
1668 gfx.anim_random_frame = RND(group->num_elements_resolved);
1670 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671 group->choice_mode, 0,
1674 if (group->choice_mode == ANIM_RANDOM)
1675 gfx.anim_random_frame = last_anim_random_frame;
1677 group->choice_pos++;
1679 element = group->element_resolved[element_pos];
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 if (element == EL_SP_MURPHY)
1691 if (stored_player[0].present)
1693 Feld[x][y] = EL_SP_MURPHY_CLONE;
1699 stored_player[0].initial_element = element;
1700 stored_player[0].use_murphy = TRUE;
1702 if (!level.use_artwork_element[0])
1703 stored_player[0].artwork_element = EL_SP_MURPHY;
1706 Feld[x][y] = EL_PLAYER_1;
1712 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713 int jx = player->jx, jy = player->jy;
1715 player->present = TRUE;
1717 player->block_last_field = (element == EL_SP_MURPHY ?
1718 level.sp_block_last_field :
1719 level.block_last_field);
1721 /* ---------- initialize player's last field block delay --------------- */
1723 /* always start with reliable default value (no adjustment needed) */
1724 player->block_delay_adjustment = 0;
1726 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727 if (player->block_last_field && element == EL_SP_MURPHY)
1728 player->block_delay_adjustment = 1;
1730 /* special case 2: in game engines before 3.1.1, blocking was different */
1731 if (game.use_block_last_field_bug)
1732 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734 if (!options.network || player->connected)
1736 player->active = TRUE;
1738 /* remove potentially duplicate players */
1739 if (StorePlayer[jx][jy] == Feld[x][y])
1740 StorePlayer[jx][jy] = 0;
1742 StorePlayer[x][y] = Feld[x][y];
1744 #if DEBUG_INIT_PLAYER
1747 printf("- player element %d activated", player->element_nr);
1748 printf(" (local player is %d and currently %s)\n",
1749 local_player->element_nr,
1750 local_player->active ? "active" : "not active");
1755 Feld[x][y] = EL_EMPTY;
1757 player->jx = player->last_jx = x;
1758 player->jy = player->last_jy = y;
1763 int player_nr = GET_PLAYER_NR(element);
1764 struct PlayerInfo *player = &stored_player[player_nr];
1766 if (player->active && player->killed)
1767 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1771 static void InitField(int x, int y, boolean init_game)
1773 int element = Feld[x][y];
1782 InitPlayerField(x, y, element, init_game);
1785 case EL_SOKOBAN_FIELD_PLAYER:
1786 element = Feld[x][y] = EL_PLAYER_1;
1787 InitField(x, y, init_game);
1789 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790 InitField(x, y, init_game);
1793 case EL_SOKOBAN_FIELD_EMPTY:
1794 local_player->sokobanfields_still_needed++;
1798 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1816 case EL_SPACESHIP_RIGHT:
1817 case EL_SPACESHIP_UP:
1818 case EL_SPACESHIP_LEFT:
1819 case EL_SPACESHIP_DOWN:
1820 case EL_BD_BUTTERFLY:
1821 case EL_BD_BUTTERFLY_RIGHT:
1822 case EL_BD_BUTTERFLY_UP:
1823 case EL_BD_BUTTERFLY_LEFT:
1824 case EL_BD_BUTTERFLY_DOWN:
1826 case EL_BD_FIREFLY_RIGHT:
1827 case EL_BD_FIREFLY_UP:
1828 case EL_BD_FIREFLY_LEFT:
1829 case EL_BD_FIREFLY_DOWN:
1830 case EL_PACMAN_RIGHT:
1832 case EL_PACMAN_LEFT:
1833 case EL_PACMAN_DOWN:
1835 case EL_YAMYAM_LEFT:
1836 case EL_YAMYAM_RIGHT:
1838 case EL_YAMYAM_DOWN:
1839 case EL_DARK_YAMYAM:
1842 case EL_SP_SNIKSNAK:
1843 case EL_SP_ELECTRON:
1852 case EL_AMOEBA_FULL:
1857 case EL_AMOEBA_DROP:
1858 if (y == lev_fieldy - 1)
1860 Feld[x][y] = EL_AMOEBA_GROWING;
1861 Store[x][y] = EL_AMOEBA_WET;
1865 case EL_DYNAMITE_ACTIVE:
1866 case EL_SP_DISK_RED_ACTIVE:
1867 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871 MovDelay[x][y] = 96;
1874 case EL_EM_DYNAMITE_ACTIVE:
1875 MovDelay[x][y] = 32;
1879 local_player->lights_still_needed++;
1883 local_player->friends_still_needed++;
1888 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1891 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1905 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1911 game.belt_dir[belt_nr] = belt_dir;
1912 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914 else /* more than one switch -- set it like the first switch */
1916 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1921 case EL_LIGHT_SWITCH_ACTIVE:
1923 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926 case EL_INVISIBLE_STEELWALL:
1927 case EL_INVISIBLE_WALL:
1928 case EL_INVISIBLE_SAND:
1929 if (game.light_time_left > 0 ||
1930 game.lenses_time_left > 0)
1931 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934 case EL_EMC_MAGIC_BALL:
1935 if (game.ball_state)
1936 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939 case EL_EMC_MAGIC_BALL_SWITCH:
1940 if (game.ball_state)
1941 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944 case EL_TRIGGER_PLAYER:
1945 case EL_TRIGGER_ELEMENT:
1946 case EL_TRIGGER_CE_VALUE:
1947 case EL_TRIGGER_CE_SCORE:
1949 case EL_ANY_ELEMENT:
1950 case EL_CURRENT_CE_VALUE:
1951 case EL_CURRENT_CE_SCORE:
1968 /* reference elements should not be used on the playfield */
1969 Feld[x][y] = EL_EMPTY;
1973 if (IS_CUSTOM_ELEMENT(element))
1975 if (CAN_MOVE(element))
1978 if (!element_info[element].use_last_ce_value || init_game)
1979 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981 else if (IS_GROUP_ELEMENT(element))
1983 Feld[x][y] = GetElementFromGroupElement(element);
1985 InitField(x, y, init_game);
1992 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 InitField(x, y, init_game);
1999 /* not needed to call InitMovDir() -- already done by InitField()! */
2000 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001 CAN_MOVE(Feld[x][y]))
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 int old_element = Feld[x][y];
2009 InitField(x, y, init_game);
2011 /* not needed to call InitMovDir() -- already done by InitField()! */
2012 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013 CAN_MOVE(old_element) &&
2014 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2017 /* this case is in fact a combination of not less than three bugs:
2018 first, it calls InitMovDir() for elements that can move, although this is
2019 already done by InitField(); then, it checks the element that was at this
2020 field _before_ the call to InitField() (which can change it); lastly, it
2021 was not called for "mole with direction" elements, which were treated as
2022 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2026 static int get_key_element_from_nr(int key_nr)
2028 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030 EL_EM_KEY_1 : EL_KEY_1);
2032 return key_base_element + key_nr;
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2037 return (player->inventory_size > 0 ?
2038 player->inventory_element[player->inventory_size - 1] :
2039 player->inventory_infinite_element != EL_UNDEFINED ?
2040 player->inventory_infinite_element :
2041 player->dynabombs_left > 0 ?
2042 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 /* pos >= 0: get element from bottom of the stack;
2049 pos < 0: get element from top of the stack */
2053 int min_inventory_size = -pos;
2054 int inventory_pos = player->inventory_size - min_inventory_size;
2055 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057 return (player->inventory_size >= min_inventory_size ?
2058 player->inventory_element[inventory_pos] :
2059 player->inventory_infinite_element != EL_UNDEFINED ?
2060 player->inventory_infinite_element :
2061 player->dynabombs_left >= min_dynabombs_left ?
2062 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2067 int min_dynabombs_left = pos + 1;
2068 int min_inventory_size = pos + 1 - player->dynabombs_left;
2069 int inventory_pos = pos - player->dynabombs_left;
2071 return (player->inventory_infinite_element != EL_UNDEFINED ?
2072 player->inventory_infinite_element :
2073 player->dynabombs_left >= min_dynabombs_left ?
2074 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075 player->inventory_size >= min_inventory_size ?
2076 player->inventory_element[inventory_pos] :
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2087 if (gpo1->sort_priority != gpo2->sort_priority)
2088 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090 compare_result = gpo1->nr - gpo2->nr;
2092 return compare_result;
2095 int getPlayerInventorySize(int player_nr)
2097 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098 return level.native_em_level->ply[player_nr]->dynamite;
2099 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100 return level.native_sp_level->game_sp->red_disk_count;
2102 return stored_player[player_nr].inventory_size;
2105 void InitGameControlValues()
2109 for (i = 0; game_panel_controls[i].nr != -1; i++)
2111 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113 struct TextPosInfo *pos = gpc->pos;
2115 int type = gpc->type;
2119 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120 Error(ERR_EXIT, "this should not happen -- please debug");
2123 /* force update of game controls after initialization */
2124 gpc->value = gpc->last_value = -1;
2125 gpc->frame = gpc->last_frame = -1;
2126 gpc->gfx_frame = -1;
2128 /* determine panel value width for later calculation of alignment */
2129 if (type == TYPE_INTEGER || type == TYPE_STRING)
2131 pos->width = pos->size * getFontWidth(pos->font);
2132 pos->height = getFontHeight(pos->font);
2134 else if (type == TYPE_ELEMENT)
2136 pos->width = pos->size;
2137 pos->height = pos->size;
2140 /* fill structure for game panel draw order */
2142 gpo->sort_priority = pos->sort_priority;
2145 /* sort game panel controls according to sort_priority and control number */
2146 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2150 void UpdatePlayfieldElementCount()
2152 boolean use_element_count = FALSE;
2155 /* first check if it is needed at all to calculate playfield element count */
2156 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158 use_element_count = TRUE;
2160 if (!use_element_count)
2163 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164 element_info[i].element_count = 0;
2166 SCAN_PLAYFIELD(x, y)
2168 element_info[Feld[x][y]].element_count++;
2171 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173 if (IS_IN_GROUP(j, i))
2174 element_info[EL_GROUP_START + i].element_count +=
2175 element_info[j].element_count;
2178 void UpdateGameControlValues()
2181 int time = (local_player->LevelSolved ?
2182 local_player->LevelSolved_CountingTime :
2183 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184 level.native_em_level->lev->time :
2185 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186 level.native_sp_level->game_sp->time_played :
2187 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188 game_mm.energy_left :
2189 game.no_time_limit ? TimePlayed : TimeLeft);
2190 int score = (local_player->LevelSolved ?
2191 local_player->LevelSolved_CountingScore :
2192 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193 level.native_em_level->lev->score :
2194 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195 level.native_sp_level->game_sp->score :
2196 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198 local_player->score);
2199 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200 level.native_em_level->lev->required :
2201 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202 level.native_sp_level->game_sp->infotrons_still_needed :
2203 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204 game_mm.kettles_still_needed :
2205 local_player->gems_still_needed);
2206 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207 level.native_em_level->lev->required > 0 :
2208 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211 game_mm.kettles_still_needed > 0 ||
2212 game_mm.lights_still_needed > 0 :
2213 local_player->gems_still_needed > 0 ||
2214 local_player->sokobanfields_still_needed > 0 ||
2215 local_player->lights_still_needed > 0);
2216 int health = (local_player->LevelSolved ?
2217 local_player->LevelSolved_CountingHealth :
2218 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219 MM_HEALTH(game_mm.laser_overload_value) :
2220 local_player->health);
2222 UpdatePlayfieldElementCount();
2224 /* update game panel control values */
2226 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230 for (i = 0; i < MAX_NUM_KEYS; i++)
2231 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235 if (game.centered_player_nr == -1)
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 /* only one player in Supaplex game engine */
2240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243 for (k = 0; k < MAX_NUM_KEYS; k++)
2245 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247 if (level.native_em_level->ply[i]->keys & (1 << k))
2248 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249 get_key_element_from_nr(k);
2251 else if (stored_player[i].key[k])
2252 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253 get_key_element_from_nr(k);
2256 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257 getPlayerInventorySize(i);
2259 if (stored_player[i].num_white_keys > 0)
2260 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264 stored_player[i].num_white_keys;
2269 int player_nr = game.centered_player_nr;
2271 for (k = 0; k < MAX_NUM_KEYS; k++)
2273 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 get_key_element_from_nr(k);
2279 else if (stored_player[player_nr].key[k])
2280 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281 get_key_element_from_nr(k);
2284 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 getPlayerInventorySize(player_nr);
2287 if (stored_player[player_nr].num_white_keys > 0)
2288 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291 stored_player[player_nr].num_white_keys;
2294 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297 get_inventory_element_from_pos(local_player, i);
2298 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299 get_inventory_element_from_pos(local_player, -i - 1);
2302 game_panel_controls[GAME_PANEL_SCORE].value = score;
2303 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305 game_panel_controls[GAME_PANEL_TIME].value = time;
2307 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311 if (level.time == 0)
2312 game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314 game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316 game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317 game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325 local_player->shield_normal_time_left;
2326 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330 local_player->shield_deadly_time_left;
2332 game_panel_controls[GAME_PANEL_EXIT].value =
2333 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339 EL_EMC_MAGIC_BALL_SWITCH);
2341 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344 game.light_time_left;
2346 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349 game.timegate_time_left;
2351 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357 game.lenses_time_left;
2359 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362 game.magnify_time_left;
2364 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2366 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2368 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2369 EL_BALLOON_SWITCH_NONE);
2371 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372 local_player->dynabomb_count;
2373 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374 local_player->dynabomb_size;
2375 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378 game_panel_controls[GAME_PANEL_PENGUINS].value =
2379 local_player->friends_still_needed;
2381 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382 local_player->sokobanfields_still_needed;
2383 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384 local_player->sokobanfields_still_needed;
2386 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389 for (i = 0; i < NUM_BELTS; i++)
2391 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401 game.magic_wall_time_left;
2403 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404 local_player->gravity;
2406 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412 game.panel.element[i].id : EL_UNDEFINED);
2414 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417 element_info[game.panel.element_count[i].id].element_count : 0);
2419 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422 element_info[game.panel.ce_score[i].id].collect_score : 0);
2424 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427 element_info[game.panel.ce_score_element[i].id].collect_score :
2430 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434 /* update game panel control frames */
2436 for (i = 0; game_panel_controls[i].nr != -1; i++)
2438 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440 if (gpc->type == TYPE_ELEMENT)
2442 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444 int last_anim_random_frame = gfx.anim_random_frame;
2445 int element = gpc->value;
2446 int graphic = el2panelimg(element);
2448 if (gpc->value != gpc->last_value)
2451 gpc->gfx_random = INIT_GFX_RANDOM();
2457 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459 gpc->gfx_random = INIT_GFX_RANDOM();
2462 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463 gfx.anim_random_frame = gpc->gfx_random;
2465 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466 gpc->gfx_frame = element_info[element].collect_score;
2468 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472 gfx.anim_random_frame = last_anim_random_frame;
2475 else if (gpc->type == TYPE_GRAPHIC)
2477 if (gpc->graphic != IMG_UNDEFINED)
2479 int last_anim_random_frame = gfx.anim_random_frame;
2480 int graphic = gpc->graphic;
2482 if (gpc->value != gpc->last_value)
2485 gpc->gfx_random = INIT_GFX_RANDOM();
2491 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493 gpc->gfx_random = INIT_GFX_RANDOM();
2496 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497 gfx.anim_random_frame = gpc->gfx_random;
2499 gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502 gfx.anim_random_frame = last_anim_random_frame;
2508 void DisplayGameControlValues()
2510 boolean redraw_panel = FALSE;
2513 for (i = 0; game_panel_controls[i].nr != -1; i++)
2515 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517 if (PANEL_DEACTIVATED(gpc->pos))
2520 if (gpc->value == gpc->last_value &&
2521 gpc->frame == gpc->last_frame)
2524 redraw_panel = TRUE;
2530 /* copy default game door content to main double buffer */
2532 /* !!! CHECK AGAIN !!! */
2533 SetPanelBackground();
2534 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537 /* redraw game control buttons */
2538 RedrawGameButtons();
2540 SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544 int nr = game_panel_order[i].nr;
2545 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546 struct TextPosInfo *pos = gpc->pos;
2547 int type = gpc->type;
2548 int value = gpc->value;
2549 int frame = gpc->frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2558 gpc->last_value = value;
2559 gpc->last_frame = frame;
2561 if (type == TYPE_INTEGER)
2563 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 nr == GAME_PANEL_TIME)
2566 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568 if (use_dynamic_size) /* use dynamic number of digits */
2570 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 int size2 = size1 + 1;
2573 int font1 = pos->font;
2574 int font2 = pos->font_alt;
2576 size = (value < value_change ? size1 : size2);
2577 font = (value < value_change ? font1 : font2);
2581 /* correct text size if "digits" is zero or less */
2583 size = strlen(int2str(value, size));
2585 /* dynamically correct text alignment */
2586 pos->width = size * getFontWidth(font);
2588 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589 int2str(value, size), font, mask_mode);
2591 else if (type == TYPE_ELEMENT)
2593 int element, graphic;
2597 int dst_x = PANEL_XPOS(pos);
2598 int dst_y = PANEL_YPOS(pos);
2600 if (value != EL_UNDEFINED && value != EL_EMPTY)
2603 graphic = el2panelimg(value);
2605 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613 width = graphic_info[graphic].width * size / TILESIZE;
2614 height = graphic_info[graphic].height * size / TILESIZE;
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2624 else if (type == TYPE_GRAPHIC)
2626 int graphic = gpc->graphic;
2627 int graphic_active = gpc->graphic_active;
2631 int dst_x = PANEL_XPOS(pos);
2632 int dst_y = PANEL_YPOS(pos);
2633 boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634 level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636 if (graphic != IMG_UNDEFINED && !skip)
2638 if (pos->style == STYLE_REVERSE)
2639 value = 100 - value;
2641 getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643 if (pos->direction & MV_HORIZONTAL)
2645 width = graphic_info[graphic_active].width * value / 100;
2646 height = graphic_info[graphic_active].height;
2648 if (pos->direction == MV_LEFT)
2650 src_x += graphic_info[graphic_active].width - width;
2651 dst_x += graphic_info[graphic_active].width - width;
2656 width = graphic_info[graphic_active].width;
2657 height = graphic_info[graphic_active].height * value / 100;
2659 if (pos->direction == MV_UP)
2661 src_y += graphic_info[graphic_active].height - height;
2662 dst_y += graphic_info[graphic_active].height - height;
2667 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675 if (pos->direction & MV_HORIZONTAL)
2677 if (pos->direction == MV_RIGHT)
2684 dst_x = PANEL_XPOS(pos);
2687 width = graphic_info[graphic].width - width;
2691 if (pos->direction == MV_DOWN)
2698 dst_y = PANEL_YPOS(pos);
2701 height = graphic_info[graphic].height - height;
2705 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2712 else if (type == TYPE_STRING)
2714 boolean active = (value != 0);
2715 char *state_normal = "off";
2716 char *state_active = "on";
2717 char *state = (active ? state_active : state_normal);
2718 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2720 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2721 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2723 if (nr == GAME_PANEL_GRAVITY_STATE)
2725 int font1 = pos->font; /* (used for normal state) */
2726 int font2 = pos->font_alt; /* (used for active state) */
2728 font = (active ? font2 : font1);
2737 /* don't truncate output if "chars" is zero or less */
2740 /* dynamically correct text alignment */
2741 pos->width = size * getFontWidth(font);
2744 s_cut = getStringCopyN(s, size);
2746 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747 s_cut, font, mask_mode);
2753 redraw_mask |= REDRAW_DOOR_1;
2756 SetGameStatus(GAME_MODE_PLAYING);
2759 void UpdateAndDisplayGameControlValues()
2761 if (tape.deactivate_display)
2764 UpdateGameControlValues();
2765 DisplayGameControlValues();
2768 void UpdateGameDoorValues()
2770 UpdateGameControlValues();
2773 void DrawGameDoorValues()
2775 DisplayGameControlValues();
2780 =============================================================================
2782 -----------------------------------------------------------------------------
2783 initialize game engine due to level / tape version number
2784 =============================================================================
2787 static void InitGameEngine()
2789 int i, j, k, l, x, y;
2791 /* set game engine from tape file when re-playing, else from level file */
2792 game.engine_version = (tape.playing ? tape.engine_version :
2793 level.game_version);
2795 /* set single or multi-player game mode (needed for re-playing tapes) */
2796 game.team_mode = setup.team_mode;
2800 int num_players = 0;
2802 for (i = 0; i < MAX_PLAYERS; i++)
2803 if (tape.player_participates[i])
2806 /* multi-player tapes contain input data for more than one player */
2807 game.team_mode = (num_players > 1);
2810 /* ---------------------------------------------------------------------- */
2811 /* set flags for bugs and changes according to active game engine version */
2812 /* ---------------------------------------------------------------------- */
2815 Summary of bugfix/change:
2816 Fixed handling for custom elements that change when pushed by the player.
2818 Fixed/changed in version:
2822 Before 3.1.0, custom elements that "change when pushing" changed directly
2823 after the player started pushing them (until then handled in "DigField()").
2824 Since 3.1.0, these custom elements are not changed until the "pushing"
2825 move of the element is finished (now handled in "ContinueMoving()").
2827 Affected levels/tapes:
2828 The first condition is generally needed for all levels/tapes before version
2829 3.1.0, which might use the old behaviour before it was changed; known tapes
2830 that are affected are some tapes from the level set "Walpurgis Gardens" by
2832 The second condition is an exception from the above case and is needed for
2833 the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834 above (including some development versions of 3.1.0), but before it was
2835 known that this change would break tapes like the above and was fixed in
2836 3.1.1, so that the changed behaviour was active although the engine version
2837 while recording maybe was before 3.1.0. There is at least one tape that is
2838 affected by this exception, which is the tape for the one-level set "Bug
2839 Machine" by Juergen Bonhagen.
2842 game.use_change_when_pushing_bug =
2843 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846 tape.game_version < VERSION_IDENT(3,1,1,0)));
2849 Summary of bugfix/change:
2850 Fixed handling for blocking the field the player leaves when moving.
2852 Fixed/changed in version:
2856 Before 3.1.1, when "block last field when moving" was enabled, the field
2857 the player is leaving when moving was blocked for the time of the move,
2858 and was directly unblocked afterwards. This resulted in the last field
2859 being blocked for exactly one less than the number of frames of one player
2860 move. Additionally, even when blocking was disabled, the last field was
2861 blocked for exactly one frame.
2862 Since 3.1.1, due to changes in player movement handling, the last field
2863 is not blocked at all when blocking is disabled. When blocking is enabled,
2864 the last field is blocked for exactly the number of frames of one player
2865 move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866 last field is blocked for exactly one more than the number of frames of
2869 Affected levels/tapes:
2870 (!!! yet to be determined -- probably many !!!)
2873 game.use_block_last_field_bug =
2874 (game.engine_version < VERSION_IDENT(3,1,1,0));
2876 game_em.use_single_button =
2877 (game.engine_version > VERSION_IDENT(4,0,0,2));
2879 game_em.use_snap_key_bug =
2880 (game.engine_version < VERSION_IDENT(4,0,1,0));
2882 /* ---------------------------------------------------------------------- */
2884 /* set maximal allowed number of custom element changes per game frame */
2885 game.max_num_changes_per_frame = 1;
2887 /* default scan direction: scan playfield from top/left to bottom/right */
2888 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890 /* dynamically adjust element properties according to game engine version */
2891 InitElementPropertiesEngine(game.engine_version);
2894 printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895 printf(" tape version == %06d [%s] [file: %06d]\n",
2896 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898 printf(" => game.engine_version == %06d\n", game.engine_version);
2901 /* ---------- initialize player's initial move delay --------------------- */
2903 /* dynamically adjust player properties according to level information */
2904 for (i = 0; i < MAX_PLAYERS; i++)
2905 game.initial_move_delay_value[i] =
2906 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908 /* dynamically adjust player properties according to game engine version */
2909 for (i = 0; i < MAX_PLAYERS; i++)
2910 game.initial_move_delay[i] =
2911 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912 game.initial_move_delay_value[i] : 0);
2914 /* ---------- initialize player's initial push delay --------------------- */
2916 /* dynamically adjust player properties according to game engine version */
2917 game.initial_push_delay_value =
2918 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920 /* ---------- initialize changing elements ------------------------------- */
2922 /* initialize changing elements information */
2923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925 struct ElementInfo *ei = &element_info[i];
2927 /* this pointer might have been changed in the level editor */
2928 ei->change = &ei->change_page[0];
2930 if (!IS_CUSTOM_ELEMENT(i))
2932 ei->change->target_element = EL_EMPTY_SPACE;
2933 ei->change->delay_fixed = 0;
2934 ei->change->delay_random = 0;
2935 ei->change->delay_frames = 1;
2938 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940 ei->has_change_event[j] = FALSE;
2942 ei->event_page_nr[j] = 0;
2943 ei->event_page[j] = &ei->change_page[0];
2947 /* add changing elements from pre-defined list */
2948 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951 struct ElementInfo *ei = &element_info[ch_delay->element];
2953 ei->change->target_element = ch_delay->target_element;
2954 ei->change->delay_fixed = ch_delay->change_delay;
2956 ei->change->pre_change_function = ch_delay->pre_change_function;
2957 ei->change->change_function = ch_delay->change_function;
2958 ei->change->post_change_function = ch_delay->post_change_function;
2960 ei->change->can_change = TRUE;
2961 ei->change->can_change_or_has_action = TRUE;
2963 ei->has_change_event[CE_DELAY] = TRUE;
2965 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969 /* ---------- initialize internal run-time variables --------------------- */
2971 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975 for (j = 0; j < ei->num_change_pages; j++)
2977 ei->change_page[j].can_change_or_has_action =
2978 (ei->change_page[j].can_change |
2979 ei->change_page[j].has_action);
2983 /* add change events from custom element configuration */
2984 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988 for (j = 0; j < ei->num_change_pages; j++)
2990 if (!ei->change_page[j].can_change_or_has_action)
2993 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995 /* only add event page for the first page found with this event */
2996 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998 ei->has_change_event[k] = TRUE;
3000 ei->event_page_nr[k] = j;
3001 ei->event_page[k] = &ei->change_page[j];
3007 /* ---------- initialize reference elements in change conditions --------- */
3009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011 int element = EL_CUSTOM_START + i;
3012 struct ElementInfo *ei = &element_info[element];
3014 for (j = 0; j < ei->num_change_pages; j++)
3016 int trigger_element = ei->change_page[j].initial_trigger_element;
3018 if (trigger_element >= EL_PREV_CE_8 &&
3019 trigger_element <= EL_NEXT_CE_8)
3020 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022 ei->change_page[j].trigger_element = trigger_element;
3026 /* ---------- initialize run-time trigger player and element ------------- */
3028 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032 for (j = 0; j < ei->num_change_pages; j++)
3034 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038 ei->change_page[j].actual_trigger_ce_value = 0;
3039 ei->change_page[j].actual_trigger_ce_score = 0;
3043 /* ---------- initialize trigger events ---------------------------------- */
3045 /* initialize trigger events information */
3046 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048 trigger_events[i][j] = FALSE;
3050 /* add trigger events from element change event properties */
3051 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053 struct ElementInfo *ei = &element_info[i];
3055 for (j = 0; j < ei->num_change_pages; j++)
3057 if (!ei->change_page[j].can_change_or_has_action)
3060 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062 int trigger_element = ei->change_page[j].trigger_element;
3064 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066 if (ei->change_page[j].has_event[k])
3068 if (IS_GROUP_ELEMENT(trigger_element))
3070 struct ElementGroupInfo *group =
3071 element_info[trigger_element].group;
3073 for (l = 0; l < group->num_elements_resolved; l++)
3074 trigger_events[group->element_resolved[l]][k] = TRUE;
3076 else if (trigger_element == EL_ANY_ELEMENT)
3077 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078 trigger_events[l][k] = TRUE;
3080 trigger_events[trigger_element][k] = TRUE;
3087 /* ---------- initialize push delay -------------------------------------- */
3089 /* initialize push delay values to default */
3090 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092 if (!IS_CUSTOM_ELEMENT(i))
3094 /* set default push delay values (corrected since version 3.0.7-1) */
3095 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097 element_info[i].push_delay_fixed = 2;
3098 element_info[i].push_delay_random = 8;
3102 element_info[i].push_delay_fixed = 8;
3103 element_info[i].push_delay_random = 8;
3108 /* set push delay value for certain elements from pre-defined list */
3109 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111 int e = push_delay_list[i].element;
3113 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3114 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117 /* set push delay value for Supaplex elements for newer engine versions */
3118 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122 if (IS_SP_ELEMENT(i))
3124 /* set SP push delay to just enough to push under a falling zonk */
3125 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127 element_info[i].push_delay_fixed = delay;
3128 element_info[i].push_delay_random = 0;
3133 /* ---------- initialize move stepsize ----------------------------------- */
3135 /* initialize move stepsize values to default */
3136 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137 if (!IS_CUSTOM_ELEMENT(i))
3138 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140 /* set move stepsize value for certain elements from pre-defined list */
3141 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143 int e = move_stepsize_list[i].element;
3145 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148 /* ---------- initialize collect score ----------------------------------- */
3150 /* initialize collect score values for custom elements from initial value */
3151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152 if (IS_CUSTOM_ELEMENT(i))
3153 element_info[i].collect_score = element_info[i].collect_score_initial;
3155 /* ---------- initialize collect count ----------------------------------- */
3157 /* initialize collect count values for non-custom elements */
3158 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159 if (!IS_CUSTOM_ELEMENT(i))
3160 element_info[i].collect_count_initial = 0;
3162 /* add collect count values for all elements from pre-defined list */
3163 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164 element_info[collect_count_list[i].element].collect_count_initial =
3165 collect_count_list[i].count;
3167 /* ---------- initialize access direction -------------------------------- */
3169 /* initialize access direction values to default (access from every side) */
3170 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171 if (!IS_CUSTOM_ELEMENT(i))
3172 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174 /* set access direction value for certain elements from pre-defined list */
3175 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176 element_info[access_direction_list[i].element].access_direction =
3177 access_direction_list[i].direction;
3179 /* ---------- initialize explosion content ------------------------------- */
3180 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182 if (IS_CUSTOM_ELEMENT(i))
3185 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189 element_info[i].content.e[x][y] =
3190 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192 i == EL_PLAYER_3 ? EL_EMERALD :
3193 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194 i == EL_MOLE ? EL_EMERALD_RED :
3195 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200 i == EL_WALL_EMERALD ? EL_EMERALD :
3201 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206 i == EL_WALL_PEARL ? EL_PEARL :
3207 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212 /* ---------- initialize recursion detection ------------------------------ */
3213 recursion_loop_depth = 0;
3214 recursion_loop_detected = FALSE;
3215 recursion_loop_element = EL_UNDEFINED;
3217 /* ---------- initialize graphics engine ---------------------------------- */
3218 game.scroll_delay_value =
3219 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220 setup.scroll_delay ? setup.scroll_delay_value : 0);
3221 game.scroll_delay_value =
3222 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224 /* ---------- initialize game engine snapshots ---------------------------- */
3225 for (i = 0; i < MAX_PLAYERS; i++)
3226 game.snapshot.last_action[i] = 0;
3227 game.snapshot.changed_action = FALSE;
3228 game.snapshot.collected_item = FALSE;
3229 game.snapshot.mode =
3230 (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231 SNAPSHOT_MODE_EVERY_STEP :
3232 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233 SNAPSHOT_MODE_EVERY_MOVE :
3234 strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235 SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236 game.snapshot.save_snapshot = FALSE;
3238 /* ---------- initialize level time for Supaplex engine ------------------- */
3239 /* Supaplex levels with time limit currently unsupported -- should be added */
3240 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3244 int get_num_special_action(int element, int action_first, int action_last)
3246 int num_special_action = 0;
3249 for (i = action_first; i <= action_last; i++)
3251 boolean found = FALSE;
3253 for (j = 0; j < NUM_DIRECTIONS; j++)
3254 if (el_act_dir2img(element, i, j) !=
3255 el_act_dir2img(element, ACTION_DEFAULT, j))
3259 num_special_action++;
3264 return num_special_action;
3269 =============================================================================
3271 -----------------------------------------------------------------------------
3272 initialize and start new game
3273 =============================================================================
3278 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280 int fade_mask = REDRAW_FIELD;
3282 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3283 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3284 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3285 int initial_move_dir = MV_DOWN;
3288 // required here to update video display before fading (FIX THIS)
3289 DrawMaskedBorder(REDRAW_DOOR_2);
3291 if (!game.restart_level)
3292 CloseDoor(DOOR_CLOSE_1);
3294 SetGameStatus(GAME_MODE_PLAYING);
3296 if (level_editor_test_game)
3297 FadeSkipNextFadeIn();
3299 FadeSetEnterScreen();
3301 if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302 fade_mask = REDRAW_ALL;
3304 FadeLevelSoundsAndMusic();
3306 ExpireSoundLoops(TRUE);
3308 if (!level_editor_test_game)
3311 /* needed if different viewport properties defined for playing */
3312 ChangeViewportPropertiesIfNeeded();
3316 DrawCompleteVideoDisplay();
3318 OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3321 InitGameControlValues();
3323 /* don't play tapes over network */
3324 network_playing = (options.network && !tape.playing);
3326 for (i = 0; i < MAX_PLAYERS; i++)
3328 struct PlayerInfo *player = &stored_player[i];
3330 player->index_nr = i;
3331 player->index_bit = (1 << i);
3332 player->element_nr = EL_PLAYER_1 + i;
3334 player->present = FALSE;
3335 player->active = FALSE;
3336 player->mapped = FALSE;
3338 player->killed = FALSE;
3339 player->reanimated = FALSE;
3342 player->effective_action = 0;
3343 player->programmed_action = 0;
3345 player->mouse_action.lx = 0;
3346 player->mouse_action.ly = 0;
3347 player->mouse_action.button = 0;
3349 player->effective_mouse_action.lx = 0;
3350 player->effective_mouse_action.ly = 0;
3351 player->effective_mouse_action.button = 0;
3354 player->score_final = 0;
3356 player->health = MAX_HEALTH;
3357 player->health_final = MAX_HEALTH;
3359 player->gems_still_needed = level.gems_needed;
3360 player->sokobanfields_still_needed = 0;
3361 player->lights_still_needed = 0;
3362 player->friends_still_needed = 0;
3364 for (j = 0; j < MAX_NUM_KEYS; j++)
3365 player->key[j] = FALSE;
3367 player->num_white_keys = 0;
3369 player->dynabomb_count = 0;
3370 player->dynabomb_size = 1;
3371 player->dynabombs_left = 0;
3372 player->dynabomb_xl = FALSE;
3374 player->MovDir = initial_move_dir;
3377 player->GfxDir = initial_move_dir;
3378 player->GfxAction = ACTION_DEFAULT;
3380 player->StepFrame = 0;
3382 player->initial_element = player->element_nr;
3383 player->artwork_element =
3384 (level.use_artwork_element[i] ? level.artwork_element[i] :
3385 player->element_nr);
3386 player->use_murphy = FALSE;
3388 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3389 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3391 player->gravity = level.initial_player_gravity[i];
3393 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3395 player->actual_frame_counter = 0;
3397 player->step_counter = 0;
3399 player->last_move_dir = initial_move_dir;
3401 player->is_active = FALSE;
3403 player->is_waiting = FALSE;
3404 player->is_moving = FALSE;
3405 player->is_auto_moving = FALSE;
3406 player->is_digging = FALSE;
3407 player->is_snapping = FALSE;
3408 player->is_collecting = FALSE;
3409 player->is_pushing = FALSE;
3410 player->is_switching = FALSE;
3411 player->is_dropping = FALSE;
3412 player->is_dropping_pressed = FALSE;
3414 player->is_bored = FALSE;
3415 player->is_sleeping = FALSE;
3417 player->was_waiting = TRUE;
3418 player->was_moving = FALSE;
3419 player->was_snapping = FALSE;
3420 player->was_dropping = FALSE;
3422 player->force_dropping = FALSE;
3424 player->frame_counter_bored = -1;
3425 player->frame_counter_sleeping = -1;
3427 player->anim_delay_counter = 0;
3428 player->post_delay_counter = 0;
3430 player->dir_waiting = initial_move_dir;
3431 player->action_waiting = ACTION_DEFAULT;
3432 player->last_action_waiting = ACTION_DEFAULT;
3433 player->special_action_bored = ACTION_DEFAULT;
3434 player->special_action_sleeping = ACTION_DEFAULT;
3436 player->switch_x = -1;
3437 player->switch_y = -1;
3439 player->drop_x = -1;
3440 player->drop_y = -1;
3442 player->show_envelope = 0;
3444 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3446 player->push_delay = -1; /* initialized when pushing starts */
3447 player->push_delay_value = game.initial_push_delay_value;
3449 player->drop_delay = 0;
3450 player->drop_pressed_delay = 0;
3452 player->last_jx = -1;
3453 player->last_jy = -1;
3457 player->shield_normal_time_left = 0;
3458 player->shield_deadly_time_left = 0;
3460 player->inventory_infinite_element = EL_UNDEFINED;
3461 player->inventory_size = 0;
3463 if (level.use_initial_inventory[i])
3465 for (j = 0; j < level.initial_inventory_size[i]; j++)
3467 int element = level.initial_inventory_content[i][j];
3468 int collect_count = element_info[element].collect_count_initial;
3471 if (!IS_CUSTOM_ELEMENT(element))
3474 if (collect_count == 0)
3475 player->inventory_infinite_element = element;
3477 for (k = 0; k < collect_count; k++)
3478 if (player->inventory_size < MAX_INVENTORY_SIZE)
3479 player->inventory_element[player->inventory_size++] = element;
3483 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3484 SnapField(player, 0, 0);
3486 player->LevelSolved = FALSE;
3487 player->GameOver = FALSE;
3489 player->LevelSolved_GameWon = FALSE;
3490 player->LevelSolved_GameEnd = FALSE;
3491 player->LevelSolved_PanelOff = FALSE;
3492 player->LevelSolved_SaveTape = FALSE;
3493 player->LevelSolved_SaveScore = FALSE;
3495 player->LevelSolved_CountingTime = 0;
3496 player->LevelSolved_CountingScore = 0;
3497 player->LevelSolved_CountingHealth = 0;
3499 map_player_action[i] = i;
3502 network_player_action_received = FALSE;
3504 #if defined(NETWORK_AVALIABLE)
3505 /* initial null action */
3506 if (network_playing)
3507 SendToServer_MovePlayer(MV_NONE);
3516 TimeLeft = level.time;
3519 ScreenMovDir = MV_NONE;
3523 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3525 AllPlayersGone = FALSE;
3527 game.no_time_limit = (level.time == 0);
3529 game.yamyam_content_nr = 0;
3530 game.robot_wheel_active = FALSE;
3531 game.magic_wall_active = FALSE;
3532 game.magic_wall_time_left = 0;
3533 game.light_time_left = 0;
3534 game.timegate_time_left = 0;
3535 game.switchgate_pos = 0;
3536 game.wind_direction = level.wind_direction_initial;
3538 game.lenses_time_left = 0;
3539 game.magnify_time_left = 0;
3541 game.ball_state = level.ball_state_initial;
3542 game.ball_content_nr = 0;
3544 game.envelope_active = FALSE;
3546 /* set focus to local player for network games, else to all players */
3547 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3548 game.centered_player_nr_next = game.centered_player_nr;
3549 game.set_centered_player = FALSE;
3551 if (network_playing && tape.recording)
3553 /* store client dependent player focus when recording network games */
3554 tape.centered_player_nr_next = game.centered_player_nr_next;
3555 tape.set_centered_player = TRUE;
3558 for (i = 0; i < NUM_BELTS; i++)
3560 game.belt_dir[i] = MV_NONE;
3561 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3564 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3565 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3567 #if DEBUG_INIT_PLAYER
3570 printf("Player status at level initialization:\n");
3574 SCAN_PLAYFIELD(x, y)
3576 Feld[x][y] = level.field[x][y];
3577 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3578 ChangeDelay[x][y] = 0;
3579 ChangePage[x][y] = -1;
3580 CustomValue[x][y] = 0; /* initialized in InitField() */
3581 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3583 WasJustMoving[x][y] = 0;
3584 WasJustFalling[x][y] = 0;
3585 CheckCollision[x][y] = 0;
3586 CheckImpact[x][y] = 0;
3588 Pushed[x][y] = FALSE;
3590 ChangeCount[x][y] = 0;
3591 ChangeEvent[x][y] = -1;
3593 ExplodePhase[x][y] = 0;
3594 ExplodeDelay[x][y] = 0;
3595 ExplodeField[x][y] = EX_TYPE_NONE;
3597 RunnerVisit[x][y] = 0;
3598 PlayerVisit[x][y] = 0;
3601 GfxRandom[x][y] = INIT_GFX_RANDOM();
3602 GfxElement[x][y] = EL_UNDEFINED;
3603 GfxAction[x][y] = ACTION_DEFAULT;
3604 GfxDir[x][y] = MV_NONE;
3605 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3608 SCAN_PLAYFIELD(x, y)
3610 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3612 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3614 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3617 InitField(x, y, TRUE);
3619 ResetGfxAnimation(x, y);
3624 for (i = 0; i < MAX_PLAYERS; i++)
3626 struct PlayerInfo *player = &stored_player[i];
3628 /* set number of special actions for bored and sleeping animation */
3629 player->num_special_action_bored =
3630 get_num_special_action(player->artwork_element,
3631 ACTION_BORING_1, ACTION_BORING_LAST);
3632 player->num_special_action_sleeping =
3633 get_num_special_action(player->artwork_element,
3634 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3637 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3638 emulate_sb ? EMU_SOKOBAN :
3639 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3641 /* initialize type of slippery elements */
3642 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3644 if (!IS_CUSTOM_ELEMENT(i))
3646 /* default: elements slip down either to the left or right randomly */
3647 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3649 /* SP style elements prefer to slip down on the left side */
3650 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3651 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3653 /* BD style elements prefer to slip down on the left side */
3654 if (game.emulation == EMU_BOULDERDASH)
3655 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3659 /* initialize explosion and ignition delay */
3660 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3662 if (!IS_CUSTOM_ELEMENT(i))
3665 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3666 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3667 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3668 int last_phase = (num_phase + 1) * delay;
3669 int half_phase = (num_phase / 2) * delay;
3671 element_info[i].explosion_delay = last_phase - 1;
3672 element_info[i].ignition_delay = half_phase;
3674 if (i == EL_BLACK_ORB)
3675 element_info[i].ignition_delay = 1;
3679 /* correct non-moving belts to start moving left */
3680 for (i = 0; i < NUM_BELTS; i++)
3681 if (game.belt_dir[i] == MV_NONE)
3682 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3684 #if USE_NEW_PLAYER_ASSIGNMENTS
3685 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3686 /* choose default local player */
3687 local_player = &stored_player[0];
3689 for (i = 0; i < MAX_PLAYERS; i++)
3690 stored_player[i].connected = FALSE;
3692 local_player->connected = TRUE;
3693 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3697 for (i = 0; i < MAX_PLAYERS; i++)
3698 stored_player[i].connected = tape.player_participates[i];
3700 else if (game.team_mode && !options.network)
3702 /* try to guess locally connected team mode players (needed for correct
3703 assignment of player figures from level to locally playing players) */
3705 for (i = 0; i < MAX_PLAYERS; i++)
3706 if (setup.input[i].use_joystick ||
3707 setup.input[i].key.left != KSYM_UNDEFINED)
3708 stored_player[i].connected = TRUE;
3711 #if DEBUG_INIT_PLAYER
3714 printf("Player status after level initialization:\n");
3716 for (i = 0; i < MAX_PLAYERS; i++)
3718 struct PlayerInfo *player = &stored_player[i];
3720 printf("- player %d: present == %d, connected == %d, active == %d",
3726 if (local_player == player)
3727 printf(" (local player)");
3734 #if DEBUG_INIT_PLAYER
3736 printf("Reassigning players ...\n");
3739 /* check if any connected player was not found in playfield */
3740 for (i = 0; i < MAX_PLAYERS; i++)
3742 struct PlayerInfo *player = &stored_player[i];
3744 if (player->connected && !player->present)
3746 struct PlayerInfo *field_player = NULL;
3748 #if DEBUG_INIT_PLAYER
3750 printf("- looking for field player for player %d ...\n", i + 1);
3753 /* assign first free player found that is present in the playfield */
3755 /* first try: look for unmapped playfield player that is not connected */
3756 for (j = 0; j < MAX_PLAYERS; j++)
3757 if (field_player == NULL &&
3758 stored_player[j].present &&
3759 !stored_player[j].mapped &&
3760 !stored_player[j].connected)
3761 field_player = &stored_player[j];
3763 /* second try: look for *any* unmapped playfield player */
3764 for (j = 0; j < MAX_PLAYERS; j++)
3765 if (field_player == NULL &&
3766 stored_player[j].present &&
3767 !stored_player[j].mapped)
3768 field_player = &stored_player[j];
3770 if (field_player != NULL)
3772 int jx = field_player->jx, jy = field_player->jy;
3774 #if DEBUG_INIT_PLAYER
3776 printf("- found player %d\n", field_player->index_nr + 1);
3779 player->present = FALSE;
3780 player->active = FALSE;
3782 field_player->present = TRUE;
3783 field_player->active = TRUE;
3786 player->initial_element = field_player->initial_element;
3787 player->artwork_element = field_player->artwork_element;
3789 player->block_last_field = field_player->block_last_field;
3790 player->block_delay_adjustment = field_player->block_delay_adjustment;
3793 StorePlayer[jx][jy] = field_player->element_nr;
3795 field_player->jx = field_player->last_jx = jx;
3796 field_player->jy = field_player->last_jy = jy;
3798 if (local_player == player)
3799 local_player = field_player;
3801 map_player_action[field_player->index_nr] = i;
3803 field_player->mapped = TRUE;
3805 #if DEBUG_INIT_PLAYER
3807 printf("- map_player_action[%d] == %d\n",
3808 field_player->index_nr + 1, i + 1);
3813 if (player->connected && player->present)
3814 player->mapped = TRUE;
3817 #if DEBUG_INIT_PLAYER
3820 printf("Player status after player assignment (first stage):\n");
3822 for (i = 0; i < MAX_PLAYERS; i++)
3824 struct PlayerInfo *player = &stored_player[i];
3826 printf("- player %d: present == %d, connected == %d, active == %d",
3832 if (local_player == player)
3833 printf(" (local player)");
3842 /* check if any connected player was not found in playfield */
3843 for (i = 0; i < MAX_PLAYERS; i++)
3845 struct PlayerInfo *player = &stored_player[i];
3847 if (player->connected && !player->present)
3849 for (j = 0; j < MAX_PLAYERS; j++)
3851 struct PlayerInfo *field_player = &stored_player[j];
3852 int jx = field_player->jx, jy = field_player->jy;
3854 /* assign first free player found that is present in the playfield */
3855 if (field_player->present && !field_player->connected)
3857 player->present = TRUE;
3858 player->active = TRUE;
3860 field_player->present = FALSE;
3861 field_player->active = FALSE;
3863 player->initial_element = field_player->initial_element;
3864 player->artwork_element = field_player->artwork_element;
3866 player->block_last_field = field_player->block_last_field;
3867 player->block_delay_adjustment = field_player->block_delay_adjustment;
3869 StorePlayer[jx][jy] = player->element_nr;
3871 player->jx = player->last_jx = jx;
3872 player->jy = player->last_jy = jy;
3882 printf("::: local_player->present == %d\n", local_player->present);
3887 /* when playing a tape, eliminate all players who do not participate */
3889 #if USE_NEW_PLAYER_ASSIGNMENTS
3891 if (!game.team_mode)
3893 for (i = 0; i < MAX_PLAYERS; i++)
3895 if (stored_player[i].active &&
3896 !tape.player_participates[map_player_action[i]])
3898 struct PlayerInfo *player = &stored_player[i];
3899 int jx = player->jx, jy = player->jy;
3901 #if DEBUG_INIT_PLAYER
3903 printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3906 player->active = FALSE;
3907 StorePlayer[jx][jy] = 0;
3908 Feld[jx][jy] = EL_EMPTY;
3915 for (i = 0; i < MAX_PLAYERS; i++)
3917 if (stored_player[i].active &&
3918 !tape.player_participates[i])
3920 struct PlayerInfo *player = &stored_player[i];
3921 int jx = player->jx, jy = player->jy;
3923 player->active = FALSE;
3924 StorePlayer[jx][jy] = 0;
3925 Feld[jx][jy] = EL_EMPTY;
3930 else if (!options.network && !game.team_mode) /* && !tape.playing */
3932 /* when in single player mode, eliminate all but the first active player */
3934 for (i = 0; i < MAX_PLAYERS; i++)
3936 if (stored_player[i].active)
3938 for (j = i + 1; j < MAX_PLAYERS; j++)
3940 if (stored_player[j].active)
3942 struct PlayerInfo *player = &stored_player[j];
3943 int jx = player->jx, jy = player->jy;
3945 player->active = FALSE;
3946 player->present = FALSE;
3948 StorePlayer[jx][jy] = 0;
3949 Feld[jx][jy] = EL_EMPTY;
3956 /* when recording the game, store which players take part in the game */
3959 #if USE_NEW_PLAYER_ASSIGNMENTS
3960 for (i = 0; i < MAX_PLAYERS; i++)
3961 if (stored_player[i].connected)
3962 tape.player_participates[i] = TRUE;
3964 for (i = 0; i < MAX_PLAYERS; i++)
3965 if (stored_player[i].active)
3966 tape.player_participates[i] = TRUE;
3970 #if DEBUG_INIT_PLAYER
3973 printf("Player status after player assignment (final stage):\n");
3975 for (i = 0; i < MAX_PLAYERS; i++)
3977 struct PlayerInfo *player = &stored_player[i];
3979 printf("- player %d: present == %d, connected == %d, active == %d",
3985 if (local_player == player)
3986 printf(" (local player)");
3993 if (BorderElement == EL_EMPTY)
3996 SBX_Right = lev_fieldx - SCR_FIELDX;
3998 SBY_Lower = lev_fieldy - SCR_FIELDY;
4003 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4005 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4008 if (full_lev_fieldx <= SCR_FIELDX)
4009 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4010 if (full_lev_fieldy <= SCR_FIELDY)
4011 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4013 if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4015 if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4018 /* if local player not found, look for custom element that might create
4019 the player (make some assumptions about the right custom element) */
4020 if (!local_player->present)
4022 int start_x = 0, start_y = 0;
4023 int found_rating = 0;
4024 int found_element = EL_UNDEFINED;
4025 int player_nr = local_player->index_nr;
4027 SCAN_PLAYFIELD(x, y)
4029 int element = Feld[x][y];
4034 if (level.use_start_element[player_nr] &&
4035 level.start_element[player_nr] == element &&
4042 found_element = element;
4045 if (!IS_CUSTOM_ELEMENT(element))
4048 if (CAN_CHANGE(element))
4050 for (i = 0; i < element_info[element].num_change_pages; i++)
4052 /* check for player created from custom element as single target */
4053 content = element_info[element].change_page[i].target_element;
4054 is_player = ELEM_IS_PLAYER(content);
4056 if (is_player && (found_rating < 3 ||
4057 (found_rating == 3 && element < found_element)))
4063 found_element = element;
4068 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4070 /* check for player created from custom element as explosion content */
4071 content = element_info[element].content.e[xx][yy];
4072 is_player = ELEM_IS_PLAYER(content);
4074 if (is_player && (found_rating < 2 ||
4075 (found_rating == 2 && element < found_element)))
4077 start_x = x + xx - 1;
4078 start_y = y + yy - 1;
4081 found_element = element;
4084 if (!CAN_CHANGE(element))
4087 for (i = 0; i < element_info[element].num_change_pages; i++)
4089 /* check for player created from custom element as extended target */
4091 element_info[element].change_page[i].target_content.e[xx][yy];
4093 is_player = ELEM_IS_PLAYER(content);
4095 if (is_player && (found_rating < 1 ||
4096 (found_rating == 1 && element < found_element)))
4098 start_x = x + xx - 1;
4099 start_y = y + yy - 1;
4102 found_element = element;
4108 scroll_x = SCROLL_POSITION_X(start_x);
4109 scroll_y = SCROLL_POSITION_Y(start_y);
4113 scroll_x = SCROLL_POSITION_X(local_player->jx);
4114 scroll_y = SCROLL_POSITION_Y(local_player->jy);
4117 /* !!! FIX THIS (START) !!! */
4118 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4120 InitGameEngine_EM();
4122 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4124 InitGameEngine_SP();
4126 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4128 InitGameEngine_MM();
4132 DrawLevel(REDRAW_FIELD);
4135 /* after drawing the level, correct some elements */
4136 if (game.timegate_time_left == 0)
4137 CloseAllOpenTimegates();
4140 /* blit playfield from scroll buffer to normal back buffer for fading in */
4141 BlitScreenToBitmap(backbuffer);
4142 /* !!! FIX THIS (END) !!! */
4144 DrawMaskedBorder(fade_mask);
4149 // full screen redraw is required at this point in the following cases:
4150 // - special editor door undrawn when game was started from level editor
4151 // - drawing area (playfield) was changed and has to be removed completely
4152 redraw_mask = REDRAW_ALL;
4156 if (!game.restart_level)
4158 /* copy default game door content to main double buffer */
4160 /* !!! CHECK AGAIN !!! */
4161 SetPanelBackground();
4162 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4163 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4166 SetPanelBackground();
4167 SetDrawBackgroundMask(REDRAW_DOOR_1);
4169 UpdateAndDisplayGameControlValues();
4171 if (!game.restart_level)
4177 CreateGameButtons();
4182 /* copy actual game door content to door double buffer for OpenDoor() */
4183 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4185 OpenDoor(DOOR_OPEN_ALL);
4187 PlaySound(SND_GAME_STARTING);
4189 if (setup.sound_music)
4192 KeyboardAutoRepeatOffUnlessAutoplay();
4194 #if DEBUG_INIT_PLAYER
4197 printf("Player status (final):\n");
4199 for (i = 0; i < MAX_PLAYERS; i++)
4201 struct PlayerInfo *player = &stored_player[i];
4203 printf("- player %d: present == %d, connected == %d, active == %d",
4209 if (local_player == player)
4210 printf(" (local player)");
4223 if (!game.restart_level && !tape.playing)
4225 LevelStats_incPlayed(level_nr);
4227 SaveLevelSetup_SeriesInfo();
4230 game.restart_level = FALSE;
4231 game.restart_game_message = NULL;
4233 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4234 InitGameActions_MM();
4236 SaveEngineSnapshotToListInitial();
4239 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4240 int actual_player_x, int actual_player_y)
4242 /* this is used for non-R'n'D game engines to update certain engine values */
4244 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4246 actual_player_x = correctLevelPosX_EM(actual_player_x);
4247 actual_player_y = correctLevelPosY_EM(actual_player_y);
4250 /* needed to determine if sounds are played within the visible screen area */
4251 scroll_x = actual_scroll_x;
4252 scroll_y = actual_scroll_y;
4254 /* needed to get player position for "follow finger" playing input method */
4255 local_player->jx = actual_player_x;
4256 local_player->jy = actual_player_y;
4259 void InitMovDir(int x, int y)
4261 int i, element = Feld[x][y];
4262 static int xy[4][2] =
4269 static int direction[3][4] =
4271 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4272 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4273 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4282 Feld[x][y] = EL_BUG;
4283 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4286 case EL_SPACESHIP_RIGHT:
4287 case EL_SPACESHIP_UP:
4288 case EL_SPACESHIP_LEFT:
4289 case EL_SPACESHIP_DOWN:
4290 Feld[x][y] = EL_SPACESHIP;
4291 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4294 case EL_BD_BUTTERFLY_RIGHT:
4295 case EL_BD_BUTTERFLY_UP:
4296 case EL_BD_BUTTERFLY_LEFT:
4297 case EL_BD_BUTTERFLY_DOWN:
4298 Feld[x][y] = EL_BD_BUTTERFLY;
4299 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4302 case EL_BD_FIREFLY_RIGHT:
4303 case EL_BD_FIREFLY_UP:
4304 case EL_BD_FIREFLY_LEFT:
4305 case EL_BD_FIREFLY_DOWN:
4306 Feld[x][y] = EL_BD_FIREFLY;
4307 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4310 case EL_PACMAN_RIGHT:
4312 case EL_PACMAN_LEFT:
4313 case EL_PACMAN_DOWN:
4314 Feld[x][y] = EL_PACMAN;
4315 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4318 case EL_YAMYAM_LEFT:
4319 case EL_YAMYAM_RIGHT:
4321 case EL_YAMYAM_DOWN:
4322 Feld[x][y] = EL_YAMYAM;
4323 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4326 case EL_SP_SNIKSNAK:
4327 MovDir[x][y] = MV_UP;
4330 case EL_SP_ELECTRON:
4331 MovDir[x][y] = MV_LEFT;
4338 Feld[x][y] = EL_MOLE;
4339 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4343 if (IS_CUSTOM_ELEMENT(element))
4345 struct ElementInfo *ei = &element_info[element];
4346 int move_direction_initial = ei->move_direction_initial;
4347 int move_pattern = ei->move_pattern;
4349 if (move_direction_initial == MV_START_PREVIOUS)
4351 if (MovDir[x][y] != MV_NONE)
4354 move_direction_initial = MV_START_AUTOMATIC;
4357 if (move_direction_initial == MV_START_RANDOM)
4358 MovDir[x][y] = 1 << RND(4);
4359 else if (move_direction_initial & MV_ANY_DIRECTION)
4360 MovDir[x][y] = move_direction_initial;
4361 else if (move_pattern == MV_ALL_DIRECTIONS ||
4362 move_pattern == MV_TURNING_LEFT ||
4363 move_pattern == MV_TURNING_RIGHT ||
4364 move_pattern == MV_TURNING_LEFT_RIGHT ||
4365 move_pattern == MV_TURNING_RIGHT_LEFT ||
4366 move_pattern == MV_TURNING_RANDOM)
4367 MovDir[x][y] = 1 << RND(4);
4368 else if (move_pattern == MV_HORIZONTAL)
4369 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4370 else if (move_pattern == MV_VERTICAL)
4371 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4372 else if (move_pattern & MV_ANY_DIRECTION)
4373 MovDir[x][y] = element_info[element].move_pattern;
4374 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4375 move_pattern == MV_ALONG_RIGHT_SIDE)
4377 /* use random direction as default start direction */
4378 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4379 MovDir[x][y] = 1 << RND(4);
4381 for (i = 0; i < NUM_DIRECTIONS; i++)
4383 int x1 = x + xy[i][0];
4384 int y1 = y + xy[i][1];
4386 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4388 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4389 MovDir[x][y] = direction[0][i];
4391 MovDir[x][y] = direction[1][i];
4400 MovDir[x][y] = 1 << RND(4);
4402 if (element != EL_BUG &&
4403 element != EL_SPACESHIP &&
4404 element != EL_BD_BUTTERFLY &&
4405 element != EL_BD_FIREFLY)
4408 for (i = 0; i < NUM_DIRECTIONS; i++)
4410 int x1 = x + xy[i][0];
4411 int y1 = y + xy[i][1];
4413 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4415 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4417 MovDir[x][y] = direction[0][i];
4420 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4421 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4423 MovDir[x][y] = direction[1][i];
4432 GfxDir[x][y] = MovDir[x][y];
4435 void InitAmoebaNr(int x, int y)
4438 int group_nr = AmoebeNachbarNr(x, y);
4442 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4444 if (AmoebaCnt[i] == 0)
4452 AmoebaNr[x][y] = group_nr;
4453 AmoebaCnt[group_nr]++;
4454 AmoebaCnt2[group_nr]++;
4457 static void PlayerWins(struct PlayerInfo *player)
4459 player->LevelSolved = TRUE;
4460 player->GameOver = TRUE;
4462 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4463 level.native_em_level->lev->score :
4464 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4467 player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4468 MM_HEALTH(game_mm.laser_overload_value) :
4471 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4473 player->LevelSolved_CountingScore = player->score_final;
4474 player->LevelSolved_CountingHealth = player->health_final;
4479 static int time_count_steps;
4480 static int time, time_final;
4481 static int score, score_final;
4482 static int health, health_final;
4483 static int game_over_delay_1 = 0;
4484 static int game_over_delay_2 = 0;
4485 static int game_over_delay_3 = 0;
4486 int game_over_delay_value_1 = 50;
4487 int game_over_delay_value_2 = 25;
4488 int game_over_delay_value_3 = 50;
4490 if (!local_player->LevelSolved_GameWon)
4494 /* do not start end game actions before the player stops moving (to exit) */
4495 if (local_player->MovPos)
4498 local_player->LevelSolved_GameWon = TRUE;
4499 local_player->LevelSolved_SaveTape = tape.recording;
4500 local_player->LevelSolved_SaveScore = !tape.playing;
4504 LevelStats_incSolved(level_nr);
4506 SaveLevelSetup_SeriesInfo();
4509 if (tape.auto_play) /* tape might already be stopped here */
4510 tape.auto_play_level_solved = TRUE;
4514 game_over_delay_1 = 0;
4515 game_over_delay_2 = 0;
4516 game_over_delay_3 = game_over_delay_value_3;
4518 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4519 score = score_final = local_player->score_final;
4520 health = health_final = local_player->health_final;
4522 if (level.score[SC_TIME_BONUS] > 0)
4527 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4529 else if (game.no_time_limit && TimePlayed < 999)
4532 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4535 time_count_steps = MAX(1, ABS(time_final - time) / 100);
4537 game_over_delay_1 = game_over_delay_value_1;
4539 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4542 score_final += health * level.score[SC_TIME_BONUS];
4544 game_over_delay_2 = game_over_delay_value_2;
4547 local_player->score_final = score_final;
4548 local_player->health_final = health_final;
4551 if (level_editor_test_game)
4554 score = score_final;
4556 local_player->LevelSolved_CountingTime = time;
4557 local_player->LevelSolved_CountingScore = score;
4559 game_panel_controls[GAME_PANEL_TIME].value = time;
4560 game_panel_controls[GAME_PANEL_SCORE].value = score;
4562 DisplayGameControlValues();
4565 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4567 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4569 /* close exit door after last player */
4570 if ((AllPlayersGone &&
4571 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4572 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4573 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4574 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4575 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4577 int element = Feld[ExitX][ExitY];
4579 Feld[ExitX][ExitY] =
4580 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4581 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4582 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4583 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4584 EL_EM_STEEL_EXIT_CLOSING);
4586 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4589 /* player disappears */
4590 DrawLevelField(ExitX, ExitY);
4593 for (i = 0; i < MAX_PLAYERS; i++)
4595 struct PlayerInfo *player = &stored_player[i];
4597 if (player->present)
4599 RemovePlayer(player);
4601 /* player disappears */
4602 DrawLevelField(player->jx, player->jy);
4607 PlaySound(SND_GAME_WINNING);
4610 if (game_over_delay_1 > 0)
4612 game_over_delay_1--;
4617 if (time != time_final)
4619 int time_to_go = ABS(time_final - time);
4620 int time_count_dir = (time < time_final ? +1 : -1);
4622 if (time_to_go < time_count_steps)
4623 time_count_steps = 1;
4625 time += time_count_steps * time_count_dir;
4626 score += time_count_steps * level.score[SC_TIME_BONUS];
4628 local_player->LevelSolved_CountingTime = time;
4629 local_player->LevelSolved_CountingScore = score;
4631 game_panel_controls[GAME_PANEL_TIME].value = time;
4632 game_panel_controls[GAME_PANEL_SCORE].value = score;
4634 DisplayGameControlValues();
4636 if (time == time_final)
4637 StopSound(SND_GAME_LEVELTIME_BONUS);
4638 else if (setup.sound_loops)
4639 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4641 PlaySound(SND_GAME_LEVELTIME_BONUS);
4646 if (game_over_delay_2 > 0)
4648 game_over_delay_2--;
4653 if (health != health_final)
4655 int health_count_dir = (health < health_final ? +1 : -1);
4657 health += health_count_dir;
4658 score += level.score[SC_TIME_BONUS];
4660 local_player->LevelSolved_CountingHealth = health;
4661 local_player->LevelSolved_CountingScore = score;
4663 game_panel_controls[GAME_PANEL_HEALTH].value = health;
4664 game_panel_controls[GAME_PANEL_SCORE].value = score;
4666 DisplayGameControlValues();
4668 if (health == health_final)
4669 StopSound(SND_GAME_LEVELTIME_BONUS);
4670 else if (setup.sound_loops)
4671 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4673 PlaySound(SND_GAME_LEVELTIME_BONUS);
4678 local_player->LevelSolved_PanelOff = TRUE;
4680 if (game_over_delay_3 > 0)
4682 game_over_delay_3--;
4693 boolean raise_level = FALSE;
4695 local_player->LevelSolved_GameEnd = TRUE;
4697 if (local_player->LevelSolved_SaveTape)
4699 /* make sure that request dialog to save tape does not open door again */
4700 if (!global.use_envelope_request)
4701 CloseDoor(DOOR_CLOSE_1);
4703 SaveTapeChecked_LevelSolved(tape.level_nr); /* ask to save tape */
4706 /* if no tape is to be saved, close both doors simultaneously */
4707 CloseDoor(DOOR_CLOSE_ALL);
4709 if (level_editor_test_game)
4711 SetGameStatus(GAME_MODE_MAIN);
4718 if (!local_player->LevelSolved_SaveScore)
4720 SetGameStatus(GAME_MODE_MAIN);
4727 if (level_nr == leveldir_current->handicap_level)
4729 leveldir_current->handicap_level++;
4731 SaveLevelSetup_SeriesInfo();
4734 if (setup.increment_levels &&
4735 level_nr < leveldir_current->last_level)
4736 raise_level = TRUE; /* advance to next level */
4738 if ((hi_pos = NewHiScore()) >= 0)
4740 SetGameStatus(GAME_MODE_SCORES);
4742 DrawHallOfFame(hi_pos);
4752 SetGameStatus(GAME_MODE_MAIN);
4768 boolean one_score_entry_per_name = !program.many_scores_per_name;
4770 LoadScore(level_nr);
4772 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4773 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4776 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4778 if (local_player->score_final > highscore[k].Score)
4780 /* player has made it to the hall of fame */
4782 if (k < MAX_SCORE_ENTRIES - 1)
4784 int m = MAX_SCORE_ENTRIES - 1;
4786 if (one_score_entry_per_name)
4788 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4789 if (strEqual(setup.player_name, highscore[l].Name))
4792 if (m == k) /* player's new highscore overwrites his old one */
4796 for (l = m; l > k; l--)
4798 strcpy(highscore[l].Name, highscore[l - 1].Name);
4799 highscore[l].Score = highscore[l - 1].Score;
4805 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4806 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4807 highscore[k].Score = local_player->score_final;
4812 else if (one_score_entry_per_name &&
4813 !strncmp(setup.player_name, highscore[k].Name,
4814 MAX_PLAYER_NAME_LEN))
4815 break; /* player already there with a higher score */
4819 SaveScore(level_nr);
4824 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4826 int element = Feld[x][y];
4827 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4828 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4829 int horiz_move = (dx != 0);
4830 int sign = (horiz_move ? dx : dy);
4831 int step = sign * element_info[element].move_stepsize;
4833 /* special values for move stepsize for spring and things on conveyor belt */
4836 if (CAN_FALL(element) &&
4837 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4838 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4839 else if (element == EL_SPRING)
4840 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4846 inline static int getElementMoveStepsize(int x, int y)
4848 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4851 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4853 if (player->GfxAction != action || player->GfxDir != dir)
4855 player->GfxAction = action;
4856 player->GfxDir = dir;
4858 player->StepFrame = 0;
4862 static void ResetGfxFrame(int x, int y)
4864 // profiling showed that "autotest" spends 10~20% of its time in this function
4865 if (DrawingDeactivatedField())
4868 int element = Feld[x][y];
4869 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4871 if (graphic_info[graphic].anim_global_sync)
4872 GfxFrame[x][y] = FrameCounter;
4873 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4874 GfxFrame[x][y] = CustomValue[x][y];
4875 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4876 GfxFrame[x][y] = element_info[element].collect_score;
4877 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4878 GfxFrame[x][y] = ChangeDelay[x][y];
4881 static void ResetGfxAnimation(int x, int y)
4883 GfxAction[x][y] = ACTION_DEFAULT;
4884 GfxDir[x][y] = MovDir[x][y];
4887 ResetGfxFrame(x, y);
4890 static void ResetRandomAnimationValue(int x, int y)
4892 GfxRandom[x][y] = INIT_GFX_RANDOM();
4895 void InitMovingField(int x, int y, int direction)
4897 int element = Feld[x][y];
4898 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4899 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4902 boolean is_moving_before, is_moving_after;
4904 /* check if element was/is moving or being moved before/after mode change */
4905 is_moving_before = (WasJustMoving[x][y] != 0);
4906 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4908 /* reset animation only for moving elements which change direction of moving
4909 or which just started or stopped moving
4910 (else CEs with property "can move" / "not moving" are reset each frame) */
4911 if (is_moving_before != is_moving_after ||
4912 direction != MovDir[x][y])
4913 ResetGfxAnimation(x, y);
4915 MovDir[x][y] = direction;
4916 GfxDir[x][y] = direction;
4918 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4919 direction == MV_DOWN && CAN_FALL(element) ?
4920 ACTION_FALLING : ACTION_MOVING);
4922 /* this is needed for CEs with property "can move" / "not moving" */
4924 if (is_moving_after)
4926 if (Feld[newx][newy] == EL_EMPTY)
4927 Feld[newx][newy] = EL_BLOCKED;
4929 MovDir[newx][newy] = MovDir[x][y];
4931 CustomValue[newx][newy] = CustomValue[x][y];
4933 GfxFrame[newx][newy] = GfxFrame[x][y];
4934 GfxRandom[newx][newy] = GfxRandom[x][y];
4935 GfxAction[newx][newy] = GfxAction[x][y];
4936 GfxDir[newx][newy] = GfxDir[x][y];
4940 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4942 int direction = MovDir[x][y];
4943 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4944 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4950 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4952 int oldx = x, oldy = y;
4953 int direction = MovDir[x][y];
4955 if (direction == MV_LEFT)
4957 else if (direction == MV_RIGHT)
4959 else if (direction == MV_UP)
4961 else if (direction == MV_DOWN)
4964 *comes_from_x = oldx;
4965 *comes_from_y = oldy;
4968 int MovingOrBlocked2Element(int x, int y)
4970 int element = Feld[x][y];
4972 if (element == EL_BLOCKED)
4976 Blocked2Moving(x, y, &oldx, &oldy);
4977 return Feld[oldx][oldy];
4983 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4985 /* like MovingOrBlocked2Element(), but if element is moving
4986 and (x,y) is the field the moving element is just leaving,
4987 return EL_BLOCKED instead of the element value */
4988 int element = Feld[x][y];
4990 if (IS_MOVING(x, y))
4992 if (element == EL_BLOCKED)
4996 Blocked2Moving(x, y, &oldx, &oldy);
4997 return Feld[oldx][oldy];
5006 static void RemoveField(int x, int y)
5008 Feld[x][y] = EL_EMPTY;
5014 CustomValue[x][y] = 0;
5017 ChangeDelay[x][y] = 0;
5018 ChangePage[x][y] = -1;
5019 Pushed[x][y] = FALSE;
5021 GfxElement[x][y] = EL_UNDEFINED;
5022 GfxAction[x][y] = ACTION_DEFAULT;
5023 GfxDir[x][y] = MV_NONE;
5026 void RemoveMovingField(int x, int y)
5028 int oldx = x, oldy = y, newx = x, newy = y;
5029 int element = Feld[x][y];
5030 int next_element = EL_UNDEFINED;
5032 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5035 if (IS_MOVING(x, y))
5037 Moving2Blocked(x, y, &newx, &newy);
5039 if (Feld[newx][newy] != EL_BLOCKED)
5041 /* element is moving, but target field is not free (blocked), but
5042 already occupied by something different (example: acid pool);
5043 in this case, only remove the moving field, but not the target */
5045 RemoveField(oldx, oldy);
5047 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5049 TEST_DrawLevelField(oldx, oldy);
5054 else if (element == EL_BLOCKED)
5056 Blocked2Moving(x, y, &oldx, &oldy);
5057 if (!IS_MOVING(oldx, oldy))
5061 if (element == EL_BLOCKED &&
5062 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5063 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5064 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5065 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5066 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5067 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5068 next_element = get_next_element(Feld[oldx][oldy]);
5070 RemoveField(oldx, oldy);
5071 RemoveField(newx, newy);
5073 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5075 if (next_element != EL_UNDEFINED)
5076 Feld[oldx][oldy] = next_element;
5078 TEST_DrawLevelField(oldx, oldy);
5079 TEST_DrawLevelField(newx, newy);
5082 void DrawDynamite(int x, int y)
5084 int sx = SCREENX(x), sy = SCREENY(y);
5085 int graphic = el2img(Feld[x][y]);
5088 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5091 if (IS_WALKABLE_INSIDE(Back[x][y]))
5095 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5096 else if (Store[x][y])
5097 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5099 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5101 if (Back[x][y] || Store[x][y])
5102 DrawGraphicThruMask(sx, sy, graphic, frame);
5104 DrawGraphic(sx, sy, graphic, frame);
5107 void CheckDynamite(int x, int y)
5109 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5113 if (MovDelay[x][y] != 0)
5116 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5122 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5127 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5129 boolean num_checked_players = 0;
5132 for (i = 0; i < MAX_PLAYERS; i++)
5134 if (stored_player[i].active)
5136 int sx = stored_player[i].jx;
5137 int sy = stored_player[i].jy;
5139 if (num_checked_players == 0)
5146 *sx1 = MIN(*sx1, sx);
5147 *sy1 = MIN(*sy1, sy);
5148 *sx2 = MAX(*sx2, sx);
5149 *sy2 = MAX(*sy2, sy);
5152 num_checked_players++;
5157 static boolean checkIfAllPlayersFitToScreen_RND()
5159 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5161 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5163 return (sx2 - sx1 < SCR_FIELDX &&
5164 sy2 - sy1 < SCR_FIELDY);
5167 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5169 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5171 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5173 *sx = (sx1 + sx2) / 2;
5174 *sy = (sy1 + sy2) / 2;
5177 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5178 boolean center_screen, boolean quick_relocation)
5180 unsigned int frame_delay_value_old = GetVideoFrameDelay();
5181 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5182 boolean no_delay = (tape.warp_forward);
5183 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5184 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5185 int new_scroll_x, new_scroll_y;
5187 if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5189 /* case 1: quick relocation inside visible screen (without scrolling) */
5196 if (!level.shifted_relocation || center_screen)
5198 /* relocation _with_ centering of screen */
5200 new_scroll_x = SCROLL_POSITION_X(x);
5201 new_scroll_y = SCROLL_POSITION_Y(y);
5205 /* relocation _without_ centering of screen */
5207 int center_scroll_x = SCROLL_POSITION_X(old_x);
5208 int center_scroll_y = SCROLL_POSITION_Y(old_y);
5209 int offset_x = x + (scroll_x - center_scroll_x);
5210 int offset_y = y + (scroll_y - center_scroll_y);
5212 /* for new screen position, apply previous offset to center position */
5213 new_scroll_x = SCROLL_POSITION_X(offset_x);
5214 new_scroll_y = SCROLL_POSITION_Y(offset_y);
5217 if (quick_relocation)
5219 /* case 2: quick relocation (redraw without visible scrolling) */
5221 scroll_x = new_scroll_x;
5222 scroll_y = new_scroll_y;
5229 /* case 3: visible relocation (with scrolling to new position) */
5231 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5233 SetVideoFrameDelay(wait_delay_value);
5235 while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5238 int fx = FX, fy = FY;
5240 dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5241 dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5243 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5249 fx += dx * TILEX / 2;
5250 fy += dy * TILEY / 2;
5252 ScrollLevel(dx, dy);
5255 /* scroll in two steps of half tile size to make things smoother */
5256 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5258 /* scroll second step to align at full tile size */
5259 BlitScreenToBitmap(window);
5265 SetVideoFrameDelay(frame_delay_value_old);
5268 void RelocatePlayer(int jx, int jy, int el_player_raw)
5270 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5271 int player_nr = GET_PLAYER_NR(el_player);
5272 struct PlayerInfo *player = &stored_player[player_nr];
5273 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5274 boolean no_delay = (tape.warp_forward);
5275 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5276 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5277 int old_jx = player->jx;
5278 int old_jy = player->jy;
5279 int old_element = Feld[old_jx][old_jy];
5280 int element = Feld[jx][jy];
5281 boolean player_relocated = (old_jx != jx || old_jy != jy);
5283 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5284 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5285 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5286 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5287 int leave_side_horiz = move_dir_horiz;
5288 int leave_side_vert = move_dir_vert;
5289 int enter_side = enter_side_horiz | enter_side_vert;
5290 int leave_side = leave_side_horiz | leave_side_vert;
5292 if (player->GameOver) /* do not reanimate dead player */
5295 if (!player_relocated) /* no need to relocate the player */
5298 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5300 RemoveField(jx, jy); /* temporarily remove newly placed player */
5301 DrawLevelField(jx, jy);
5304 if (player->present)
5306 while (player->MovPos)
5308 ScrollPlayer(player, SCROLL_GO_ON);
5309 ScrollScreen(NULL, SCROLL_GO_ON);
5311 AdvanceFrameAndPlayerCounters(player->index_nr);
5315 BackToFront_WithFrameDelay(wait_delay_value);
5318 DrawPlayer(player); /* needed here only to cleanup last field */
5319 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5321 player->is_moving = FALSE;
5324 if (IS_CUSTOM_ELEMENT(old_element))
5325 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5327 player->index_bit, leave_side);
5329 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5331 player->index_bit, leave_side);
5333 Feld[jx][jy] = el_player;
5334 InitPlayerField(jx, jy, el_player, TRUE);
5336 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5337 possible that the relocation target field did not contain a player element,
5338 but a walkable element, to which the new player was relocated -- in this
5339 case, restore that (already initialized!) element on the player field */
5340 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5342 Feld[jx][jy] = element; /* restore previously existing element */
5345 /* only visually relocate centered player */
5346 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5347 FALSE, level.instant_relocation);
5349 TestIfPlayerTouchesBadThing(jx, jy);
5350 TestIfPlayerTouchesCustomElement(jx, jy);
5352 if (IS_CUSTOM_ELEMENT(element))
5353 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5354 player->index_bit, enter_side);
5356 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5357 player->index_bit, enter_side);
5359 if (player->is_switching)
5361 /* ensure that relocation while still switching an element does not cause
5362 a new element to be treated as also switched directly after relocation
5363 (this is important for teleporter switches that teleport the player to
5364 a place where another teleporter switch is in the same direction, which
5365 would then incorrectly be treated as immediately switched before the
5366 direction key that caused the switch was released) */
5368 player->switch_x += jx - old_jx;
5369 player->switch_y += jy - old_jy;
5373 void Explode(int ex, int ey, int phase, int mode)
5379 /* !!! eliminate this variable !!! */
5380 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5382 if (game.explosions_delayed)
5384 ExplodeField[ex][ey] = mode;
5388 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5390 int center_element = Feld[ex][ey];
5391 int artwork_element, explosion_element; /* set these values later */
5393 /* remove things displayed in background while burning dynamite */
5394 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5397 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5399 /* put moving element to center field (and let it explode there) */
5400 center_element = MovingOrBlocked2Element(ex, ey);
5401 RemoveMovingField(ex, ey);
5402 Feld[ex][ey] = center_element;
5405 /* now "center_element" is finally determined -- set related values now */
5406 artwork_element = center_element; /* for custom player artwork */
5407 explosion_element = center_element; /* for custom player artwork */
5409 if (IS_PLAYER(ex, ey))
5411 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5413 artwork_element = stored_player[player_nr].artwork_element;
5415 if (level.use_explosion_element[player_nr])
5417 explosion_element = level.explosion_element[player_nr];
5418 artwork_element = explosion_element;
5422 if (mode == EX_TYPE_NORMAL ||
5423 mode == EX_TYPE_CENTER ||
5424 mode == EX_TYPE_CROSS)
5425 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5427 last_phase = element_info[explosion_element].explosion_delay + 1;
5429 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5431 int xx = x - ex + 1;
5432 int yy = y - ey + 1;
5435 if (!IN_LEV_FIELD(x, y) ||
5436 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5437 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5440 element = Feld[x][y];
5442 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5444 element = MovingOrBlocked2Element(x, y);
5446 if (!IS_EXPLOSION_PROOF(element))
5447 RemoveMovingField(x, y);
5450 /* indestructible elements can only explode in center (but not flames) */
5451 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5452 mode == EX_TYPE_BORDER)) ||
5453 element == EL_FLAMES)
5456 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5457 behaviour, for example when touching a yamyam that explodes to rocks
5458 with active deadly shield, a rock is created under the player !!! */
5459 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5461 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5462 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5463 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5465 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5468 if (IS_ACTIVE_BOMB(element))
5470 /* re-activate things under the bomb like gate or penguin */
5471 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5478 /* save walkable background elements while explosion on same tile */
5479 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5480 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5481 Back[x][y] = element;
5483 /* ignite explodable elements reached by other explosion */
5484 if (element == EL_EXPLOSION)
5485 element = Store2[x][y];
5487 if (AmoebaNr[x][y] &&
5488 (element == EL_AMOEBA_FULL ||
5489 element == EL_BD_AMOEBA ||
5490 element == EL_AMOEBA_GROWING))
5492 AmoebaCnt[AmoebaNr[x][y]]--;
5493 AmoebaCnt2[AmoebaNr[x][y]]--;
5498 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5500 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5502 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5504 if (PLAYERINFO(ex, ey)->use_murphy)
5505 Store[x][y] = EL_EMPTY;
5508 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5509 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5510 else if (ELEM_IS_PLAYER(center_element))
5511 Store[x][y] = EL_EMPTY;
5512 else if (center_element == EL_YAMYAM)
5513 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5514 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5515 Store[x][y] = element_info[center_element].content.e[xx][yy];
5517 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5518 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5519 otherwise) -- FIX THIS !!! */
5520 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5521 Store[x][y] = element_info[element].content.e[1][1];
5523 else if (!CAN_EXPLODE(element))
5524 Store[x][y] = element_info[element].content.e[1][1];
5527 Store[x][y] = EL_EMPTY;
5529 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5530 center_element == EL_AMOEBA_TO_DIAMOND)
5531 Store2[x][y] = element;
5533 Feld[x][y] = EL_EXPLOSION;
5534 GfxElement[x][y] = artwork_element;
5536 ExplodePhase[x][y] = 1;
5537 ExplodeDelay[x][y] = last_phase;
5542 if (center_element == EL_YAMYAM)
5543 game.yamyam_content_nr =
5544 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5556 GfxFrame[x][y] = 0; /* restart explosion animation */
5558 last_phase = ExplodeDelay[x][y];
5560 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5562 /* this can happen if the player leaves an explosion just in time */
5563 if (GfxElement[x][y] == EL_UNDEFINED)
5564 GfxElement[x][y] = EL_EMPTY;
5566 border_element = Store2[x][y];
5567 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5568 border_element = StorePlayer[x][y];
5570 if (phase == element_info[border_element].ignition_delay ||
5571 phase == last_phase)
5573 boolean border_explosion = FALSE;
5575 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5576 !PLAYER_EXPLOSION_PROTECTED(x, y))
5578 KillPlayerUnlessExplosionProtected(x, y);
5579 border_explosion = TRUE;
5581 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5583 Feld[x][y] = Store2[x][y];
5586 border_explosion = TRUE;
5588 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5590 AmoebeUmwandeln(x, y);
5592 border_explosion = TRUE;
5595 /* if an element just explodes due to another explosion (chain-reaction),
5596 do not immediately end the new explosion when it was the last frame of
5597 the explosion (as it would be done in the following "if"-statement!) */
5598 if (border_explosion && phase == last_phase)
5602 if (phase == last_phase)
5606 element = Feld[x][y] = Store[x][y];
5607 Store[x][y] = Store2[x][y] = 0;
5608 GfxElement[x][y] = EL_UNDEFINED;
5610 /* player can escape from explosions and might therefore be still alive */
5611 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5612 element <= EL_PLAYER_IS_EXPLODING_4)
5614 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5615 int explosion_element = EL_PLAYER_1 + player_nr;
5616 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5617 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5619 if (level.use_explosion_element[player_nr])
5620 explosion_element = level.explosion_element[player_nr];
5622 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5623 element_info[explosion_element].content.e[xx][yy]);
5626 /* restore probably existing indestructible background element */
5627 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5628 element = Feld[x][y] = Back[x][y];
5631 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5632 GfxDir[x][y] = MV_NONE;
5633 ChangeDelay[x][y] = 0;
5634 ChangePage[x][y] = -1;
5636 CustomValue[x][y] = 0;
5638 InitField_WithBug2(x, y, FALSE);
5640 TEST_DrawLevelField(x, y);
5642 TestIfElementTouchesCustomElement(x, y);
5644 if (GFX_CRUMBLED(element))
5645 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5647 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5648 StorePlayer[x][y] = 0;
5650 if (ELEM_IS_PLAYER(element))
5651 RelocatePlayer(x, y, element);
5653 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5655 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5656 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5659 TEST_DrawLevelFieldCrumbled(x, y);
5661 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5663 DrawLevelElement(x, y, Back[x][y]);
5664 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5666 else if (IS_WALKABLE_UNDER(Back[x][y]))
5668 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5669 DrawLevelElementThruMask(x, y, Back[x][y]);
5671 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5672 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5676 void DynaExplode(int ex, int ey)
5679 int dynabomb_element = Feld[ex][ey];
5680 int dynabomb_size = 1;
5681 boolean dynabomb_xl = FALSE;
5682 struct PlayerInfo *player;
5683 static int xy[4][2] =
5691 if (IS_ACTIVE_BOMB(dynabomb_element))
5693 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5694 dynabomb_size = player->dynabomb_size;
5695 dynabomb_xl = player->dynabomb_xl;
5696 player->dynabombs_left++;
5699 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5701 for (i = 0; i < NUM_DIRECTIONS; i++)
5703 for (j = 1; j <= dynabomb_size; j++)
5705 int x = ex + j * xy[i][0];
5706 int y = ey + j * xy[i][1];
5709 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5712 element = Feld[x][y];
5714 /* do not restart explosions of fields with active bombs */
5715 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5718 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5720 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5721 !IS_DIGGABLE(element) && !dynabomb_xl)
5727 void Bang(int x, int y)
5729 int element = MovingOrBlocked2Element(x, y);
5730 int explosion_type = EX_TYPE_NORMAL;
5732 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5734 struct PlayerInfo *player = PLAYERINFO(x, y);
5736 element = Feld[x][y] = player->initial_element;
5738 if (level.use_explosion_element[player->index_nr])
5740 int explosion_element = level.explosion_element[player->index_nr];
5742 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5743 explosion_type = EX_TYPE_CROSS;
5744 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5745 explosion_type = EX_TYPE_CENTER;
5753 case EL_BD_BUTTERFLY:
5756 case EL_DARK_YAMYAM:
5760 RaiseScoreElement(element);
5763 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5764 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5765 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5766 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5767 case EL_DYNABOMB_INCREASE_NUMBER:
5768 case EL_DYNABOMB_INCREASE_SIZE:
5769 case EL_DYNABOMB_INCREASE_POWER:
5770 explosion_type = EX_TYPE_DYNA;
5773 case EL_DC_LANDMINE:
5774 explosion_type = EX_TYPE_CENTER;
5779 case EL_LAMP_ACTIVE:
5780 case EL_AMOEBA_TO_DIAMOND:
5781 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5782 explosion_type = EX_TYPE_CENTER;
5786 if (element_info[element].explosion_type == EXPLODES_CROSS)
5787 explosion_type = EX_TYPE_CROSS;
5788 else if (element_info[element].explosion_type == EXPLODES_1X1)
5789 explosion_type = EX_TYPE_CENTER;
5793 if (explosion_type == EX_TYPE_DYNA)
5796 Explode(x, y, EX_PHASE_START, explosion_type);
5798 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5801 void SplashAcid(int x, int y)
5803 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5804 (!IN_LEV_FIELD(x - 1, y - 2) ||
5805 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5806 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5808 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5809 (!IN_LEV_FIELD(x + 1, y - 2) ||
5810 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5811 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5813 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5816 static void InitBeltMovement()
5818 static int belt_base_element[4] =
5820 EL_CONVEYOR_BELT_1_LEFT,
5821 EL_CONVEYOR_BELT_2_LEFT,
5822 EL_CONVEYOR_BELT_3_LEFT,
5823 EL_CONVEYOR_BELT_4_LEFT
5825 static int belt_base_active_element[4] =
5827 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5828 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5829 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5830 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5835 /* set frame order for belt animation graphic according to belt direction */
5836 for (i = 0; i < NUM_BELTS; i++)
5840 for (j = 0; j < NUM_BELT_PARTS; j++)
5842 int element = belt_base_active_element[belt_nr] + j;
5843 int graphic_1 = el2img(element);
5844 int graphic_2 = el2panelimg(element);
5846 if (game.belt_dir[i] == MV_LEFT)
5848 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5849 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5853 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5854 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5859 SCAN_PLAYFIELD(x, y)
5861 int element = Feld[x][y];
5863 for (i = 0; i < NUM_BELTS; i++)
5865 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5867 int e_belt_nr = getBeltNrFromBeltElement(element);
5870 if (e_belt_nr == belt_nr)
5872 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5874 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5881 static void ToggleBeltSwitch(int x, int y)
5883 static int belt_base_element[4] =
5885 EL_CONVEYOR_BELT_1_LEFT,
5886 EL_CONVEYOR_BELT_2_LEFT,
5887 EL_CONVEYOR_BELT_3_LEFT,
5888 EL_CONVEYOR_BELT_4_LEFT
5890 static int belt_base_active_element[4] =
5892 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5893 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5894 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5895 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5897 static int belt_base_switch_element[4] =
5899 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5900 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5901 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5902 EL_CONVEYOR_BELT_4_SWITCH_LEFT
5904 static int belt_move_dir[4] =
5912 int element = Feld[x][y];
5913 int belt_nr = getBeltNrFromBeltSwitchElement(element);
5914 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5915 int belt_dir = belt_move_dir[belt_dir_nr];
5918 if (!IS_BELT_SWITCH(element))
5921 game.belt_dir_nr[belt_nr] = belt_dir_nr;
5922 game.belt_dir[belt_nr] = belt_dir;
5924 if (belt_dir_nr == 3)
5927 /* set frame order for belt animation graphic according to belt direction */
5928 for (i = 0; i < NUM_BELT_PARTS; i++)
5930 int element = belt_base_active_element[belt_nr] + i;
5931 int graphic_1 = el2img(element);
5932 int graphic_2 = el2panelimg(element);
5934 if (belt_dir == MV_LEFT)
5936 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5937 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5941 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5942 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5946 SCAN_PLAYFIELD(xx, yy)
5948 int element = Feld[xx][yy];
5950 if (IS_BELT_SWITCH(element))
5952 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5954 if (e_belt_nr == belt_nr)
5956 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5957 TEST_DrawLevelField(xx, yy);
5960 else if (IS_BELT(element) && belt_dir != MV_NONE)
5962 int e_belt_nr = getBeltNrFromBeltElement(element);
5964 if (e_belt_nr == belt_nr)
5966 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5968 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5969 TEST_DrawLevelField(xx, yy);
5972 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5974 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5976 if (e_belt_nr == belt_nr)
5978 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5980 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5981 TEST_DrawLevelField(xx, yy);
5987 static void ToggleSwitchgateSwitch(int x, int y)
5991 game.switchgate_pos = !game.switchgate_pos;
5993 SCAN_PLAYFIELD(xx, yy)
5995 int element = Feld[xx][yy];
5997 if (element == EL_SWITCHGATE_SWITCH_UP)
5999 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6000 TEST_DrawLevelField(xx, yy);
6002 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6004 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6005 TEST_DrawLevelField(xx, yy);
6007 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6009 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6010 TEST_DrawLevelField(xx, yy);
6012 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6014 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6015 TEST_DrawLevelField(xx, yy);
6017 else if (element == EL_SWITCHGATE_OPEN ||
6018 element == EL_SWITCHGATE_OPENING)
6020 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6022 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6024 else if (element == EL_SWITCHGATE_CLOSED ||
6025 element == EL_SWITCHGATE_CLOSING)
6027 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6029 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6034 static int getInvisibleActiveFromInvisibleElement(int element)
6036 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6037 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6038 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6042 static int getInvisibleFromInvisibleActiveElement(int element)
6044 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6045 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6046 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6050 static void RedrawAllLightSwitchesAndInvisibleElements()
6054 SCAN_PLAYFIELD(x, y)
6056 int element = Feld[x][y];
6058 if (element == EL_LIGHT_SWITCH &&
6059 game.light_time_left > 0)
6061 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6062 TEST_DrawLevelField(x, y);
6064 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6065 game.light_time_left == 0)
6067 Feld[x][y] = EL_LIGHT_SWITCH;
6068 TEST_DrawLevelField(x, y);
6070 else if (element == EL_EMC_DRIPPER &&
6071 game.light_time_left > 0)
6073 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6074 TEST_DrawLevelField(x, y);
6076 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6077 game.light_time_left == 0)
6079 Feld[x][y] = EL_EMC_DRIPPER;
6080 TEST_DrawLevelField(x, y);
6082 else if (element == EL_INVISIBLE_STEELWALL ||
6083 element == EL_INVISIBLE_WALL ||
6084 element == EL_INVISIBLE_SAND)
6086 if (game.light_time_left > 0)
6087 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6089 TEST_DrawLevelField(x, y);
6091 /* uncrumble neighbour fields, if needed */
6092 if (element == EL_INVISIBLE_SAND)
6093 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6095 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6096 element == EL_INVISIBLE_WALL_ACTIVE ||
6097 element == EL_INVISIBLE_SAND_ACTIVE)
6099 if (game.light_time_left == 0)
6100 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6102 TEST_DrawLevelField(x, y);
6104 /* re-crumble neighbour fields, if needed */
6105 if (element == EL_INVISIBLE_SAND)
6106 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6111 static void RedrawAllInvisibleElementsForLenses()
6115 SCAN_PLAYFIELD(x, y)
6117 int element = Feld[x][y];
6119 if (element == EL_EMC_DRIPPER &&
6120 game.lenses_time_left > 0)
6122 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6123 TEST_DrawLevelField(x, y);
6125 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6126 game.lenses_time_left == 0)
6128 Feld[x][y] = EL_EMC_DRIPPER;
6129 TEST_DrawLevelField(x, y);
6131 else if (element == EL_INVISIBLE_STEELWALL ||
6132 element == EL_INVISIBLE_WALL ||
6133 element == EL_INVISIBLE_SAND)
6135 if (game.lenses_time_left > 0)
6136 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6138 TEST_DrawLevelField(x, y);
6140 /* uncrumble neighbour fields, if needed */
6141 if (element == EL_INVISIBLE_SAND)
6142 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6144 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6145 element == EL_INVISIBLE_WALL_ACTIVE ||
6146 element == EL_INVISIBLE_SAND_ACTIVE)
6148 if (game.lenses_time_left == 0)
6149 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6151 TEST_DrawLevelField(x, y);
6153 /* re-crumble neighbour fields, if needed */
6154 if (element == EL_INVISIBLE_SAND)
6155 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6160 static void RedrawAllInvisibleElementsForMagnifier()
6164 SCAN_PLAYFIELD(x, y)
6166 int element = Feld[x][y];
6168 if (element == EL_EMC_FAKE_GRASS &&
6169 game.magnify_time_left > 0)
6171 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6172 TEST_DrawLevelField(x, y);
6174 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6175 game.magnify_time_left == 0)
6177 Feld[x][y] = EL_EMC_FAKE_GRASS;
6178 TEST_DrawLevelField(x, y);
6180 else if (IS_GATE_GRAY(element) &&
6181 game.magnify_time_left > 0)
6183 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6184 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6185 IS_EM_GATE_GRAY(element) ?
6186 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6187 IS_EMC_GATE_GRAY(element) ?
6188 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6189 IS_DC_GATE_GRAY(element) ?
6190 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6192 TEST_DrawLevelField(x, y);
6194 else if (IS_GATE_GRAY_ACTIVE(element) &&
6195 game.magnify_time_left == 0)
6197 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6198 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6199 IS_EM_GATE_GRAY_ACTIVE(element) ?
6200 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6201 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6202 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6203 IS_DC_GATE_GRAY_ACTIVE(element) ?
6204 EL_DC_GATE_WHITE_GRAY :
6206 TEST_DrawLevelField(x, y);
6211 static void ToggleLightSwitch(int x, int y)
6213 int element = Feld[x][y];
6215 game.light_time_left =
6216 (element == EL_LIGHT_SWITCH ?
6217 level.time_light * FRAMES_PER_SECOND : 0);
6219 RedrawAllLightSwitchesAndInvisibleElements();
6222 static void ActivateTimegateSwitch(int x, int y)
6226 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6228 SCAN_PLAYFIELD(xx, yy)
6230 int element = Feld[xx][yy];
6232 if (element == EL_TIMEGATE_CLOSED ||
6233 element == EL_TIMEGATE_CLOSING)
6235 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6236 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6240 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6242 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6243 TEST_DrawLevelField(xx, yy);
6249 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6250 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6253 void Impact(int x, int y)
6255 boolean last_line = (y == lev_fieldy - 1);
6256 boolean object_hit = FALSE;
6257 boolean impact = (last_line || object_hit);
6258 int element = Feld[x][y];
6259 int smashed = EL_STEELWALL;
6261 if (!last_line) /* check if element below was hit */
6263 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6266 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6267 MovDir[x][y + 1] != MV_DOWN ||
6268 MovPos[x][y + 1] <= TILEY / 2));
6270 /* do not smash moving elements that left the smashed field in time */
6271 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6272 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6275 #if USE_QUICKSAND_IMPACT_BUGFIX
6276 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6278 RemoveMovingField(x, y + 1);
6279 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6280 Feld[x][y + 2] = EL_ROCK;
6281 TEST_DrawLevelField(x, y + 2);
6286 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6288 RemoveMovingField(x, y + 1);
6289 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6290 Feld[x][y + 2] = EL_ROCK;
6291 TEST_DrawLevelField(x, y + 2);
6298 smashed = MovingOrBlocked2Element(x, y + 1);
6300 impact = (last_line || object_hit);
6303 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6305 SplashAcid(x, y + 1);
6309 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6310 /* only reset graphic animation if graphic really changes after impact */
6312 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6314 ResetGfxAnimation(x, y);
6315 TEST_DrawLevelField(x, y);
6318 if (impact && CAN_EXPLODE_IMPACT(element))
6323 else if (impact && element == EL_PEARL &&
6324 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6326 ResetGfxAnimation(x, y);
6328 Feld[x][y] = EL_PEARL_BREAKING;
6329 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6332 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6334 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6339 if (impact && element == EL_AMOEBA_DROP)
6341 if (object_hit && IS_PLAYER(x, y + 1))
6342 KillPlayerUnlessEnemyProtected(x, y + 1);
6343 else if (object_hit && smashed == EL_PENGUIN)
6347 Feld[x][y] = EL_AMOEBA_GROWING;
6348 Store[x][y] = EL_AMOEBA_WET;
6350 ResetRandomAnimationValue(x, y);
6355 if (object_hit) /* check which object was hit */
6357 if ((CAN_PASS_MAGIC_WALL(element) &&
6358 (smashed == EL_MAGIC_WALL ||
6359 smashed == EL_BD_MAGIC_WALL)) ||
6360 (CAN_PASS_DC_MAGIC_WALL(element) &&
6361 smashed == EL_DC_MAGIC_WALL))
6364 int activated_magic_wall =
6365 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6366 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6367 EL_DC_MAGIC_WALL_ACTIVE);
6369 /* activate magic wall / mill */
6370 SCAN_PLAYFIELD(xx, yy)
6372 if (Feld[xx][yy] == smashed)
6373 Feld[xx][yy] = activated_magic_wall;
6376 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6377 game.magic_wall_active = TRUE;
6379 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6380 SND_MAGIC_WALL_ACTIVATING :
6381 smashed == EL_BD_MAGIC_WALL ?
6382 SND_BD_MAGIC_WALL_ACTIVATING :
6383 SND_DC_MAGIC_WALL_ACTIVATING));
6386 if (IS_PLAYER(x, y + 1))
6388 if (CAN_SMASH_PLAYER(element))
6390 KillPlayerUnlessEnemyProtected(x, y + 1);
6394 else if (smashed == EL_PENGUIN)
6396 if (CAN_SMASH_PLAYER(element))
6402 else if (element == EL_BD_DIAMOND)
6404 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6410 else if (((element == EL_SP_INFOTRON ||
6411 element == EL_SP_ZONK) &&
6412 (smashed == EL_SP_SNIKSNAK ||
6413 smashed == EL_SP_ELECTRON ||
6414 smashed == EL_SP_DISK_ORANGE)) ||
6415 (element == EL_SP_INFOTRON &&
6416 smashed == EL_SP_DISK_YELLOW))
6421 else if (CAN_SMASH_EVERYTHING(element))
6423 if (IS_CLASSIC_ENEMY(smashed) ||
6424 CAN_EXPLODE_SMASHED(smashed))
6429 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6431 if (smashed == EL_LAMP ||
6432 smashed == EL_LAMP_ACTIVE)
6437 else if (smashed == EL_NUT)
6439 Feld[x][y + 1] = EL_NUT_BREAKING;
6440 PlayLevelSound(x, y, SND_NUT_BREAKING);
6441 RaiseScoreElement(EL_NUT);
6444 else if (smashed == EL_PEARL)
6446 ResetGfxAnimation(x, y);
6448 Feld[x][y + 1] = EL_PEARL_BREAKING;
6449 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6452 else if (smashed == EL_DIAMOND)
6454 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6455 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6458 else if (IS_BELT_SWITCH(smashed))
6460 ToggleBeltSwitch(x, y + 1);
6462 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6463 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6464 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6465 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6467 ToggleSwitchgateSwitch(x, y + 1);
6469 else if (smashed == EL_LIGHT_SWITCH ||
6470 smashed == EL_LIGHT_SWITCH_ACTIVE)
6472 ToggleLightSwitch(x, y + 1);
6476 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6478 CheckElementChangeBySide(x, y + 1, smashed, element,
6479 CE_SWITCHED, CH_SIDE_TOP);
6480 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6486 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6491 /* play sound of magic wall / mill */
6493 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6494 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6495 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6497 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6498 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6499 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6500 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6501 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6502 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6507 /* play sound of object that hits the ground */
6508 if (last_line || object_hit)
6509 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6512 inline static void TurnRoundExt(int x, int y)
6524 { 0, 0 }, { 0, 0 }, { 0, 0 },
6529 int left, right, back;
6533 { MV_DOWN, MV_UP, MV_RIGHT },
6534 { MV_UP, MV_DOWN, MV_LEFT },
6536 { MV_LEFT, MV_RIGHT, MV_DOWN },
6540 { MV_RIGHT, MV_LEFT, MV_UP }
6543 int element = Feld[x][y];
6544 int move_pattern = element_info[element].move_pattern;
6546 int old_move_dir = MovDir[x][y];
6547 int left_dir = turn[old_move_dir].left;
6548 int right_dir = turn[old_move_dir].right;
6549 int back_dir = turn[old_move_dir].back;
6551 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6552 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6553 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6554 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6556 int left_x = x + left_dx, left_y = y + left_dy;
6557 int right_x = x + right_dx, right_y = y + right_dy;
6558 int move_x = x + move_dx, move_y = y + move_dy;
6562 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6564 TestIfBadThingTouchesOtherBadThing(x, y);
6566 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6567 MovDir[x][y] = right_dir;
6568 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6569 MovDir[x][y] = left_dir;
6571 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6573 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6576 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6578 TestIfBadThingTouchesOtherBadThing(x, y);
6580 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6581 MovDir[x][y] = left_dir;
6582 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6583 MovDir[x][y] = right_dir;
6585 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6587 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6590 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6592 TestIfBadThingTouchesOtherBadThing(x, y);
6594 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6595 MovDir[x][y] = left_dir;
6596 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6597 MovDir[x][y] = right_dir;
6599 if (MovDir[x][y] != old_move_dir)
6602 else if (element == EL_YAMYAM)
6604 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6605 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6607 if (can_turn_left && can_turn_right)
6608 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6609 else if (can_turn_left)
6610 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6611 else if (can_turn_right)
6612 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6614 MovDir[x][y] = back_dir;
6616 MovDelay[x][y] = 16 + 16 * RND(3);
6618 else if (element == EL_DARK_YAMYAM)
6620 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6622 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6625 if (can_turn_left && can_turn_right)
6626 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6627 else if (can_turn_left)
6628 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6629 else if (can_turn_right)
6630 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6632 MovDir[x][y] = back_dir;
6634 MovDelay[x][y] = 16 + 16 * RND(3);
6636 else if (element == EL_PACMAN)
6638 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6639 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6641 if (can_turn_left && can_turn_right)
6642 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6643 else if (can_turn_left)
6644 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6645 else if (can_turn_right)
6646 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6648 MovDir[x][y] = back_dir;
6650 MovDelay[x][y] = 6 + RND(40);
6652 else if (element == EL_PIG)
6654 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6655 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6656 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6657 boolean should_turn_left, should_turn_right, should_move_on;
6659 int rnd = RND(rnd_value);
6661 should_turn_left = (can_turn_left &&
6663 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6664 y + back_dy + left_dy)));
6665 should_turn_right = (can_turn_right &&
6667 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6668 y + back_dy + right_dy)));
6669 should_move_on = (can_move_on &&
6672 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6673 y + move_dy + left_dy) ||
6674 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6675 y + move_dy + right_dy)));
6677 if (should_turn_left || should_turn_right || should_move_on)
6679 if (should_turn_left && should_turn_right && should_move_on)
6680 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6681 rnd < 2 * rnd_value / 3 ? right_dir :
6683 else if (should_turn_left && should_turn_right)
6684 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6685 else if (should_turn_left && should_move_on)
6686 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6687 else if (should_turn_right && should_move_on)
6688 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6689 else if (should_turn_left)
6690 MovDir[x][y] = left_dir;
6691 else if (should_turn_right)
6692 MovDir[x][y] = right_dir;
6693 else if (should_move_on)
6694 MovDir[x][y] = old_move_dir;
6696 else if (can_move_on && rnd > rnd_value / 8)
6697 MovDir[x][y] = old_move_dir;
6698 else if (can_turn_left && can_turn_right)
6699 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6700 else if (can_turn_left && rnd > rnd_value / 8)
6701 MovDir[x][y] = left_dir;
6702 else if (can_turn_right && rnd > rnd_value/8)
6703 MovDir[x][y] = right_dir;
6705 MovDir[x][y] = back_dir;
6707 xx = x + move_xy[MovDir[x][y]].dx;
6708 yy = y + move_xy[MovDir[x][y]].dy;
6710 if (!IN_LEV_FIELD(xx, yy) ||
6711 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6712 MovDir[x][y] = old_move_dir;
6716 else if (element == EL_DRAGON)
6718 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6719 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6720 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6722 int rnd = RND(rnd_value);
6724 if (can_move_on && rnd > rnd_value / 8)
6725 MovDir[x][y] = old_move_dir;
6726 else if (can_turn_left && can_turn_right)
6727 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6728 else if (can_turn_left && rnd > rnd_value / 8)
6729 MovDir[x][y] = left_dir;
6730 else if (can_turn_right && rnd > rnd_value / 8)
6731 MovDir[x][y] = right_dir;
6733 MovDir[x][y] = back_dir;
6735 xx = x + move_xy[MovDir[x][y]].dx;
6736 yy = y + move_xy[MovDir[x][y]].dy;
6738 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6739 MovDir[x][y] = old_move_dir;
6743 else if (element == EL_MOLE)
6745 boolean can_move_on =
6746 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6747 IS_AMOEBOID(Feld[move_x][move_y]) ||
6748 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6751 boolean can_turn_left =
6752 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6753 IS_AMOEBOID(Feld[left_x][left_y])));
6755 boolean can_turn_right =
6756 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6757 IS_AMOEBOID(Feld[right_x][right_y])));
6759 if (can_turn_left && can_turn_right)
6760 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6761 else if (can_turn_left)
6762 MovDir[x][y] = left_dir;
6764 MovDir[x][y] = right_dir;
6767 if (MovDir[x][y] != old_move_dir)
6770 else if (element == EL_BALLOON)
6772 MovDir[x][y] = game.wind_direction;
6775 else if (element == EL_SPRING)
6777 if (MovDir[x][y] & MV_HORIZONTAL)
6779 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6780 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6782 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6783 ResetGfxAnimation(move_x, move_y);
6784 TEST_DrawLevelField(move_x, move_y);
6786 MovDir[x][y] = back_dir;
6788 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6789 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6790 MovDir[x][y] = MV_NONE;
6795 else if (element == EL_ROBOT ||
6796 element == EL_SATELLITE ||
6797 element == EL_PENGUIN ||
6798 element == EL_EMC_ANDROID)
6800 int attr_x = -1, attr_y = -1;
6811 for (i = 0; i < MAX_PLAYERS; i++)
6813 struct PlayerInfo *player = &stored_player[i];
6814 int jx = player->jx, jy = player->jy;
6816 if (!player->active)
6820 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6828 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6829 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6830 game.engine_version < VERSION_IDENT(3,1,0,0)))
6836 if (element == EL_PENGUIN)
6839 static int xy[4][2] =
6847 for (i = 0; i < NUM_DIRECTIONS; i++)
6849 int ex = x + xy[i][0];
6850 int ey = y + xy[i][1];
6852 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6853 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6854 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6855 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6864 MovDir[x][y] = MV_NONE;
6866 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6867 else if (attr_x > x)
6868 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6870 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6871 else if (attr_y > y)
6872 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6874 if (element == EL_ROBOT)
6878 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6879 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6880 Moving2Blocked(x, y, &newx, &newy);
6882 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6883 MovDelay[x][y] = 8 + 8 * !RND(3);
6885 MovDelay[x][y] = 16;
6887 else if (element == EL_PENGUIN)
6893 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6895 boolean first_horiz = RND(2);
6896 int new_move_dir = MovDir[x][y];
6899 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6900 Moving2Blocked(x, y, &newx, &newy);
6902 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6906 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6907 Moving2Blocked(x, y, &newx, &newy);
6909 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6912 MovDir[x][y] = old_move_dir;
6916 else if (element == EL_SATELLITE)
6922 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6924 boolean first_horiz = RND(2);
6925 int new_move_dir = MovDir[x][y];
6928 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6929 Moving2Blocked(x, y, &newx, &newy);
6931 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6935 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6936 Moving2Blocked(x, y, &newx, &newy);
6938 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6941 MovDir[x][y] = old_move_dir;
6945 else if (element == EL_EMC_ANDROID)
6947 static int check_pos[16] =
6949 -1, /* 0 => (invalid) */
6950 7, /* 1 => MV_LEFT */
6951 3, /* 2 => MV_RIGHT */
6952 -1, /* 3 => (invalid) */
6954 0, /* 5 => MV_LEFT | MV_UP */
6955 2, /* 6 => MV_RIGHT | MV_UP */
6956 -1, /* 7 => (invalid) */
6957 5, /* 8 => MV_DOWN */
6958 6, /* 9 => MV_LEFT | MV_DOWN */
6959 4, /* 10 => MV_RIGHT | MV_DOWN */
6960 -1, /* 11 => (invalid) */
6961 -1, /* 12 => (invalid) */
6962 -1, /* 13 => (invalid) */
6963 -1, /* 14 => (invalid) */
6964 -1, /* 15 => (invalid) */
6972 { -1, -1, MV_LEFT | MV_UP },
6974 { +1, -1, MV_RIGHT | MV_UP },
6975 { +1, 0, MV_RIGHT },
6976 { +1, +1, MV_RIGHT | MV_DOWN },
6978 { -1, +1, MV_LEFT | MV_DOWN },
6981 int start_pos, check_order;
6982 boolean can_clone = FALSE;
6985 /* check if there is any free field around current position */
6986 for (i = 0; i < 8; i++)
6988 int newx = x + check_xy[i].dx;
6989 int newy = y + check_xy[i].dy;
6991 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6999 if (can_clone) /* randomly find an element to clone */
7003 start_pos = check_pos[RND(8)];
7004 check_order = (RND(2) ? -1 : +1);
7006 for (i = 0; i < 8; i++)
7008 int pos_raw = start_pos + i * check_order;
7009 int pos = (pos_raw + 8) % 8;
7010 int newx = x + check_xy[pos].dx;
7011 int newy = y + check_xy[pos].dy;
7013 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7015 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7016 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7018 Store[x][y] = Feld[newx][newy];
7027 if (can_clone) /* randomly find a direction to move */
7031 start_pos = check_pos[RND(8)];
7032 check_order = (RND(2) ? -1 : +1);
7034 for (i = 0; i < 8; i++)
7036 int pos_raw = start_pos + i * check_order;
7037 int pos = (pos_raw + 8) % 8;
7038 int newx = x + check_xy[pos].dx;
7039 int newy = y + check_xy[pos].dy;
7040 int new_move_dir = check_xy[pos].dir;
7042 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7044 MovDir[x][y] = new_move_dir;
7045 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7054 if (can_clone) /* cloning and moving successful */
7057 /* cannot clone -- try to move towards player */
7059 start_pos = check_pos[MovDir[x][y] & 0x0f];
7060 check_order = (RND(2) ? -1 : +1);
7062 for (i = 0; i < 3; i++)
7064 /* first check start_pos, then previous/next or (next/previous) pos */
7065 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7066 int pos = (pos_raw + 8) % 8;
7067 int newx = x + check_xy[pos].dx;
7068 int newy = y + check_xy[pos].dy;
7069 int new_move_dir = check_xy[pos].dir;
7071 if (IS_PLAYER(newx, newy))
7074 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7076 MovDir[x][y] = new_move_dir;
7077 MovDelay[x][y] = level.android_move_time * 8 + 1;
7084 else if (move_pattern == MV_TURNING_LEFT ||
7085 move_pattern == MV_TURNING_RIGHT ||
7086 move_pattern == MV_TURNING_LEFT_RIGHT ||
7087 move_pattern == MV_TURNING_RIGHT_LEFT ||
7088 move_pattern == MV_TURNING_RANDOM ||
7089 move_pattern == MV_ALL_DIRECTIONS)
7091 boolean can_turn_left =
7092 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7093 boolean can_turn_right =
7094 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7096 if (element_info[element].move_stepsize == 0) /* "not moving" */
7099 if (move_pattern == MV_TURNING_LEFT)
7100 MovDir[x][y] = left_dir;
7101 else if (move_pattern == MV_TURNING_RIGHT)
7102 MovDir[x][y] = right_dir;
7103 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7104 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7105 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7106 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7107 else if (move_pattern == MV_TURNING_RANDOM)
7108 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7109 can_turn_right && !can_turn_left ? right_dir :
7110 RND(2) ? left_dir : right_dir);
7111 else if (can_turn_left && can_turn_right)
7112 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7113 else if (can_turn_left)
7114 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7115 else if (can_turn_right)
7116 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7118 MovDir[x][y] = back_dir;
7120 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7122 else if (move_pattern == MV_HORIZONTAL ||
7123 move_pattern == MV_VERTICAL)
7125 if (move_pattern & old_move_dir)
7126 MovDir[x][y] = back_dir;
7127 else if (move_pattern == MV_HORIZONTAL)
7128 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7129 else if (move_pattern == MV_VERTICAL)
7130 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7132 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7134 else if (move_pattern & MV_ANY_DIRECTION)
7136 MovDir[x][y] = move_pattern;
7137 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7139 else if (move_pattern & MV_WIND_DIRECTION)
7141 MovDir[x][y] = game.wind_direction;
7142 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7144 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7146 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7147 MovDir[x][y] = left_dir;
7148 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7149 MovDir[x][y] = right_dir;
7151 if (MovDir[x][y] != old_move_dir)
7152 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7154 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7156 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7157 MovDir[x][y] = right_dir;
7158 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7159 MovDir[x][y] = left_dir;
7161 if (MovDir[x][y] != old_move_dir)
7162 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7164 else if (move_pattern == MV_TOWARDS_PLAYER ||
7165 move_pattern == MV_AWAY_FROM_PLAYER)
7167 int attr_x = -1, attr_y = -1;
7169 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7180 for (i = 0; i < MAX_PLAYERS; i++)
7182 struct PlayerInfo *player = &stored_player[i];
7183 int jx = player->jx, jy = player->jy;
7185 if (!player->active)
7189 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7197 MovDir[x][y] = MV_NONE;
7199 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7200 else if (attr_x > x)
7201 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7203 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7204 else if (attr_y > y)
7205 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7207 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7209 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7211 boolean first_horiz = RND(2);
7212 int new_move_dir = MovDir[x][y];
7214 if (element_info[element].move_stepsize == 0) /* "not moving" */
7216 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7217 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7223 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7224 Moving2Blocked(x, y, &newx, &newy);
7226 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7230 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7231 Moving2Blocked(x, y, &newx, &newy);
7233 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7236 MovDir[x][y] = old_move_dir;
7239 else if (move_pattern == MV_WHEN_PUSHED ||
7240 move_pattern == MV_WHEN_DROPPED)
7242 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7243 MovDir[x][y] = MV_NONE;
7247 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7249 static int test_xy[7][2] =
7259 static int test_dir[7] =
7269 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7270 int move_preference = -1000000; /* start with very low preference */
7271 int new_move_dir = MV_NONE;
7272 int start_test = RND(4);
7275 for (i = 0; i < NUM_DIRECTIONS; i++)
7277 int move_dir = test_dir[start_test + i];
7278 int move_dir_preference;
7280 xx = x + test_xy[start_test + i][0];
7281 yy = y + test_xy[start_test + i][1];
7283 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7284 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7286 new_move_dir = move_dir;
7291 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7294 move_dir_preference = -1 * RunnerVisit[xx][yy];
7295 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7296 move_dir_preference = PlayerVisit[xx][yy];
7298 if (move_dir_preference > move_preference)
7300 /* prefer field that has not been visited for the longest time */
7301 move_preference = move_dir_preference;
7302 new_move_dir = move_dir;
7304 else if (move_dir_preference == move_preference &&
7305 move_dir == old_move_dir)
7307 /* prefer last direction when all directions are preferred equally */
7308 move_preference = move_dir_preference;
7309 new_move_dir = move_dir;
7313 MovDir[x][y] = new_move_dir;
7314 if (old_move_dir != new_move_dir)
7315 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7319 static void TurnRound(int x, int y)
7321 int direction = MovDir[x][y];
7325 GfxDir[x][y] = MovDir[x][y];
7327 if (direction != MovDir[x][y])
7331 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7333 ResetGfxFrame(x, y);
7336 static boolean JustBeingPushed(int x, int y)
7340 for (i = 0; i < MAX_PLAYERS; i++)
7342 struct PlayerInfo *player = &stored_player[i];
7344 if (player->active && player->is_pushing && player->MovPos)
7346 int next_jx = player->jx + (player->jx - player->last_jx);
7347 int next_jy = player->jy + (player->jy - player->last_jy);
7349 if (x == next_jx && y == next_jy)
7357 void StartMoving(int x, int y)
7359 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7360 int element = Feld[x][y];
7365 if (MovDelay[x][y] == 0)
7366 GfxAction[x][y] = ACTION_DEFAULT;
7368 if (CAN_FALL(element) && y < lev_fieldy - 1)
7370 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7371 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7372 if (JustBeingPushed(x, y))
7375 if (element == EL_QUICKSAND_FULL)
7377 if (IS_FREE(x, y + 1))
7379 InitMovingField(x, y, MV_DOWN);
7380 started_moving = TRUE;
7382 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7383 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7384 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7385 Store[x][y] = EL_ROCK;
7387 Store[x][y] = EL_ROCK;
7390 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7392 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7394 if (!MovDelay[x][y])
7396 MovDelay[x][y] = TILEY + 1;
7398 ResetGfxAnimation(x, y);
7399 ResetGfxAnimation(x, y + 1);
7404 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7405 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7412 Feld[x][y] = EL_QUICKSAND_EMPTY;
7413 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7414 Store[x][y + 1] = Store[x][y];
7417 PlayLevelSoundAction(x, y, ACTION_FILLING);
7419 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7421 if (!MovDelay[x][y])
7423 MovDelay[x][y] = TILEY + 1;
7425 ResetGfxAnimation(x, y);
7426 ResetGfxAnimation(x, y + 1);
7431 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7432 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7439 Feld[x][y] = EL_QUICKSAND_EMPTY;
7440 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7441 Store[x][y + 1] = Store[x][y];
7444 PlayLevelSoundAction(x, y, ACTION_FILLING);
7447 else if (element == EL_QUICKSAND_FAST_FULL)
7449 if (IS_FREE(x, y + 1))
7451 InitMovingField(x, y, MV_DOWN);
7452 started_moving = TRUE;
7454 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7455 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7456 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7457 Store[x][y] = EL_ROCK;
7459 Store[x][y] = EL_ROCK;
7462 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7464 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7466 if (!MovDelay[x][y])
7468 MovDelay[x][y] = TILEY + 1;
7470 ResetGfxAnimation(x, y);
7471 ResetGfxAnimation(x, y + 1);
7476 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7477 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7484 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7485 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7486 Store[x][y + 1] = Store[x][y];
7489 PlayLevelSoundAction(x, y, ACTION_FILLING);
7491 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7493 if (!MovDelay[x][y])
7495 MovDelay[x][y] = TILEY + 1;
7497 ResetGfxAnimation(x, y);
7498 ResetGfxAnimation(x, y + 1);
7503 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7504 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7511 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7512 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7513 Store[x][y + 1] = Store[x][y];
7516 PlayLevelSoundAction(x, y, ACTION_FILLING);
7519 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7520 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7522 InitMovingField(x, y, MV_DOWN);
7523 started_moving = TRUE;
7525 Feld[x][y] = EL_QUICKSAND_FILLING;
7526 Store[x][y] = element;
7528 PlayLevelSoundAction(x, y, ACTION_FILLING);
7530 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7531 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7533 InitMovingField(x, y, MV_DOWN);
7534 started_moving = TRUE;
7536 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7537 Store[x][y] = element;
7539 PlayLevelSoundAction(x, y, ACTION_FILLING);
7541 else if (element == EL_MAGIC_WALL_FULL)
7543 if (IS_FREE(x, y + 1))
7545 InitMovingField(x, y, MV_DOWN);
7546 started_moving = TRUE;
7548 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7549 Store[x][y] = EL_CHANGED(Store[x][y]);
7551 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7553 if (!MovDelay[x][y])
7554 MovDelay[x][y] = TILEY / 4 + 1;
7563 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7564 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7565 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7569 else if (element == EL_BD_MAGIC_WALL_FULL)
7571 if (IS_FREE(x, y + 1))
7573 InitMovingField(x, y, MV_DOWN);
7574 started_moving = TRUE;
7576 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7577 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7579 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7581 if (!MovDelay[x][y])
7582 MovDelay[x][y] = TILEY / 4 + 1;
7591 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7592 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7593 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7597 else if (element == EL_DC_MAGIC_WALL_FULL)
7599 if (IS_FREE(x, y + 1))
7601 InitMovingField(x, y, MV_DOWN);
7602 started_moving = TRUE;
7604 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7605 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7607 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7609 if (!MovDelay[x][y])
7610 MovDelay[x][y] = TILEY / 4 + 1;
7619 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7620 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7621 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7625 else if ((CAN_PASS_MAGIC_WALL(element) &&
7626 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7627 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7628 (CAN_PASS_DC_MAGIC_WALL(element) &&
7629 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7632 InitMovingField(x, y, MV_DOWN);
7633 started_moving = TRUE;
7636 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7637 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7638 EL_DC_MAGIC_WALL_FILLING);
7639 Store[x][y] = element;
7641 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7643 SplashAcid(x, y + 1);
7645 InitMovingField(x, y, MV_DOWN);
7646 started_moving = TRUE;
7648 Store[x][y] = EL_ACID;
7651 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7652 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7653 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7654 CAN_FALL(element) && WasJustFalling[x][y] &&
7655 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7657 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7658 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7659 (Feld[x][y + 1] == EL_BLOCKED)))
7661 /* this is needed for a special case not covered by calling "Impact()"
7662 from "ContinueMoving()": if an element moves to a tile directly below
7663 another element which was just falling on that tile (which was empty
7664 in the previous frame), the falling element above would just stop
7665 instead of smashing the element below (in previous version, the above
7666 element was just checked for "moving" instead of "falling", resulting
7667 in incorrect smashes caused by horizontal movement of the above
7668 element; also, the case of the player being the element to smash was
7669 simply not covered here... :-/ ) */
7671 CheckCollision[x][y] = 0;
7672 CheckImpact[x][y] = 0;
7676 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7678 if (MovDir[x][y] == MV_NONE)
7680 InitMovingField(x, y, MV_DOWN);
7681 started_moving = TRUE;
7684 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7686 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7687 MovDir[x][y] = MV_DOWN;
7689 InitMovingField(x, y, MV_DOWN);
7690 started_moving = TRUE;
7692 else if (element == EL_AMOEBA_DROP)
7694 Feld[x][y] = EL_AMOEBA_GROWING;
7695 Store[x][y] = EL_AMOEBA_WET;
7697 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7698 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7699 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7700 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7702 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7703 (IS_FREE(x - 1, y + 1) ||
7704 Feld[x - 1][y + 1] == EL_ACID));
7705 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7706 (IS_FREE(x + 1, y + 1) ||
7707 Feld[x + 1][y + 1] == EL_ACID));
7708 boolean can_fall_any = (can_fall_left || can_fall_right);
7709 boolean can_fall_both = (can_fall_left && can_fall_right);
7710 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7712 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7714 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7715 can_fall_right = FALSE;
7716 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7717 can_fall_left = FALSE;
7718 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7719 can_fall_right = FALSE;
7720 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7721 can_fall_left = FALSE;
7723 can_fall_any = (can_fall_left || can_fall_right);
7724 can_fall_both = FALSE;
7729 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7730 can_fall_right = FALSE; /* slip down on left side */
7732 can_fall_left = !(can_fall_right = RND(2));
7734 can_fall_both = FALSE;
7739 /* if not determined otherwise, prefer left side for slipping down */
7740 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7741 started_moving = TRUE;
7744 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7746 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7747 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7748 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7749 int belt_dir = game.belt_dir[belt_nr];
7751 if ((belt_dir == MV_LEFT && left_is_free) ||
7752 (belt_dir == MV_RIGHT && right_is_free))
7754 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7756 InitMovingField(x, y, belt_dir);
7757 started_moving = TRUE;
7759 Pushed[x][y] = TRUE;
7760 Pushed[nextx][y] = TRUE;
7762 GfxAction[x][y] = ACTION_DEFAULT;
7766 MovDir[x][y] = 0; /* if element was moving, stop it */
7771 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7772 if (CAN_MOVE(element) && !started_moving)
7774 int move_pattern = element_info[element].move_pattern;
7777 Moving2Blocked(x, y, &newx, &newy);
7779 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7782 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7783 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7785 WasJustMoving[x][y] = 0;
7786 CheckCollision[x][y] = 0;
7788 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7790 if (Feld[x][y] != element) /* element has changed */
7794 if (!MovDelay[x][y]) /* start new movement phase */
7796 /* all objects that can change their move direction after each step
7797 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7799 if (element != EL_YAMYAM &&
7800 element != EL_DARK_YAMYAM &&
7801 element != EL_PACMAN &&
7802 !(move_pattern & MV_ANY_DIRECTION) &&
7803 move_pattern != MV_TURNING_LEFT &&
7804 move_pattern != MV_TURNING_RIGHT &&
7805 move_pattern != MV_TURNING_LEFT_RIGHT &&
7806 move_pattern != MV_TURNING_RIGHT_LEFT &&
7807 move_pattern != MV_TURNING_RANDOM)
7811 if (MovDelay[x][y] && (element == EL_BUG ||
7812 element == EL_SPACESHIP ||
7813 element == EL_SP_SNIKSNAK ||
7814 element == EL_SP_ELECTRON ||
7815 element == EL_MOLE))
7816 TEST_DrawLevelField(x, y);
7820 if (MovDelay[x][y]) /* wait some time before next movement */
7824 if (element == EL_ROBOT ||
7825 element == EL_YAMYAM ||
7826 element == EL_DARK_YAMYAM)
7828 DrawLevelElementAnimationIfNeeded(x, y, element);
7829 PlayLevelSoundAction(x, y, ACTION_WAITING);
7831 else if (element == EL_SP_ELECTRON)
7832 DrawLevelElementAnimationIfNeeded(x, y, element);
7833 else if (element == EL_DRAGON)
7836 int dir = MovDir[x][y];
7837 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7838 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7839 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7840 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7841 dir == MV_UP ? IMG_FLAMES_1_UP :
7842 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7843 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7845 GfxAction[x][y] = ACTION_ATTACKING;
7847 if (IS_PLAYER(x, y))
7848 DrawPlayerField(x, y);
7850 TEST_DrawLevelField(x, y);
7852 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7854 for (i = 1; i <= 3; i++)
7856 int xx = x + i * dx;
7857 int yy = y + i * dy;
7858 int sx = SCREENX(xx);
7859 int sy = SCREENY(yy);
7860 int flame_graphic = graphic + (i - 1);
7862 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7867 int flamed = MovingOrBlocked2Element(xx, yy);
7869 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7872 RemoveMovingField(xx, yy);
7874 ChangeDelay[xx][yy] = 0;
7876 Feld[xx][yy] = EL_FLAMES;
7878 if (IN_SCR_FIELD(sx, sy))
7880 TEST_DrawLevelFieldCrumbled(xx, yy);
7881 DrawGraphic(sx, sy, flame_graphic, frame);
7886 if (Feld[xx][yy] == EL_FLAMES)
7887 Feld[xx][yy] = EL_EMPTY;
7888 TEST_DrawLevelField(xx, yy);
7893 if (MovDelay[x][y]) /* element still has to wait some time */
7895 PlayLevelSoundAction(x, y, ACTION_WAITING);
7901 /* now make next step */
7903 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7905 if (DONT_COLLIDE_WITH(element) &&
7906 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7907 !PLAYER_ENEMY_PROTECTED(newx, newy))
7909 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7914 else if (CAN_MOVE_INTO_ACID(element) &&
7915 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7916 !IS_MV_DIAGONAL(MovDir[x][y]) &&
7917 (MovDir[x][y] == MV_DOWN ||
7918 game.engine_version >= VERSION_IDENT(3,1,0,0)))
7920 SplashAcid(newx, newy);
7921 Store[x][y] = EL_ACID;
7923 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7925 if (Feld[newx][newy] == EL_EXIT_OPEN ||
7926 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7927 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7928 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7931 TEST_DrawLevelField(x, y);
7933 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7934 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7935 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7937 local_player->friends_still_needed--;
7938 if (!local_player->friends_still_needed &&
7939 !local_player->GameOver && AllPlayersGone)
7940 PlayerWins(local_player);
7944 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7946 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7947 TEST_DrawLevelField(newx, newy);
7949 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7951 else if (!IS_FREE(newx, newy))
7953 GfxAction[x][y] = ACTION_WAITING;
7955 if (IS_PLAYER(x, y))
7956 DrawPlayerField(x, y);
7958 TEST_DrawLevelField(x, y);
7963 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7965 if (IS_FOOD_PIG(Feld[newx][newy]))
7967 if (IS_MOVING(newx, newy))
7968 RemoveMovingField(newx, newy);
7971 Feld[newx][newy] = EL_EMPTY;
7972 TEST_DrawLevelField(newx, newy);
7975 PlayLevelSound(x, y, SND_PIG_DIGGING);
7977 else if (!IS_FREE(newx, newy))
7979 if (IS_PLAYER(x, y))
7980 DrawPlayerField(x, y);
7982 TEST_DrawLevelField(x, y);
7987 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7989 if (Store[x][y] != EL_EMPTY)
7991 boolean can_clone = FALSE;
7994 /* check if element to clone is still there */
7995 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7997 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8005 /* cannot clone or target field not free anymore -- do not clone */
8006 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8007 Store[x][y] = EL_EMPTY;
8010 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8012 if (IS_MV_DIAGONAL(MovDir[x][y]))
8014 int diagonal_move_dir = MovDir[x][y];
8015 int stored = Store[x][y];
8016 int change_delay = 8;
8019 /* android is moving diagonally */
8021 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8023 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8024 GfxElement[x][y] = EL_EMC_ANDROID;
8025 GfxAction[x][y] = ACTION_SHRINKING;
8026 GfxDir[x][y] = diagonal_move_dir;
8027 ChangeDelay[x][y] = change_delay;
8029 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8032 DrawLevelGraphicAnimation(x, y, graphic);
8033 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8035 if (Feld[newx][newy] == EL_ACID)
8037 SplashAcid(newx, newy);
8042 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8044 Store[newx][newy] = EL_EMC_ANDROID;
8045 GfxElement[newx][newy] = EL_EMC_ANDROID;
8046 GfxAction[newx][newy] = ACTION_GROWING;
8047 GfxDir[newx][newy] = diagonal_move_dir;
8048 ChangeDelay[newx][newy] = change_delay;
8050 graphic = el_act_dir2img(GfxElement[newx][newy],
8051 GfxAction[newx][newy], GfxDir[newx][newy]);
8053 DrawLevelGraphicAnimation(newx, newy, graphic);
8054 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8060 Feld[newx][newy] = EL_EMPTY;
8061 TEST_DrawLevelField(newx, newy);
8063 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8066 else if (!IS_FREE(newx, newy))
8071 else if (IS_CUSTOM_ELEMENT(element) &&
8072 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8074 if (!DigFieldByCE(newx, newy, element))
8077 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8079 RunnerVisit[x][y] = FrameCounter;
8080 PlayerVisit[x][y] /= 8; /* expire player visit path */
8083 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8085 if (!IS_FREE(newx, newy))
8087 if (IS_PLAYER(x, y))
8088 DrawPlayerField(x, y);
8090 TEST_DrawLevelField(x, y);
8096 boolean wanna_flame = !RND(10);
8097 int dx = newx - x, dy = newy - y;
8098 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8099 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8100 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8101 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8102 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8103 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8106 IS_CLASSIC_ENEMY(element1) ||
8107 IS_CLASSIC_ENEMY(element2)) &&
8108 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8109 element1 != EL_FLAMES && element2 != EL_FLAMES)
8111 ResetGfxAnimation(x, y);
8112 GfxAction[x][y] = ACTION_ATTACKING;
8114 if (IS_PLAYER(x, y))
8115 DrawPlayerField(x, y);
8117 TEST_DrawLevelField(x, y);
8119 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8121 MovDelay[x][y] = 50;
8123 Feld[newx][newy] = EL_FLAMES;
8124 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8125 Feld[newx1][newy1] = EL_FLAMES;
8126 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8127 Feld[newx2][newy2] = EL_FLAMES;
8133 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8134 Feld[newx][newy] == EL_DIAMOND)
8136 if (IS_MOVING(newx, newy))
8137 RemoveMovingField(newx, newy);
8140 Feld[newx][newy] = EL_EMPTY;
8141 TEST_DrawLevelField(newx, newy);
8144 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8146 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8147 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8149 if (AmoebaNr[newx][newy])
8151 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8152 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8153 Feld[newx][newy] == EL_BD_AMOEBA)
8154 AmoebaCnt[AmoebaNr[newx][newy]]--;
8157 if (IS_MOVING(newx, newy))
8159 RemoveMovingField(newx, newy);
8163 Feld[newx][newy] = EL_EMPTY;
8164 TEST_DrawLevelField(newx, newy);
8167 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8169 else if ((element == EL_PACMAN || element == EL_MOLE)
8170 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8172 if (AmoebaNr[newx][newy])
8174 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8175 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8176 Feld[newx][newy] == EL_BD_AMOEBA)
8177 AmoebaCnt[AmoebaNr[newx][newy]]--;
8180 if (element == EL_MOLE)
8182 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8183 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8185 ResetGfxAnimation(x, y);
8186 GfxAction[x][y] = ACTION_DIGGING;
8187 TEST_DrawLevelField(x, y);
8189 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8191 return; /* wait for shrinking amoeba */
8193 else /* element == EL_PACMAN */
8195 Feld[newx][newy] = EL_EMPTY;
8196 TEST_DrawLevelField(newx, newy);
8197 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8200 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8201 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8202 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8204 /* wait for shrinking amoeba to completely disappear */
8207 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8209 /* object was running against a wall */
8213 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8214 DrawLevelElementAnimation(x, y, element);
8216 if (DONT_TOUCH(element))
8217 TestIfBadThingTouchesPlayer(x, y);
8222 InitMovingField(x, y, MovDir[x][y]);
8224 PlayLevelSoundAction(x, y, ACTION_MOVING);
8228 ContinueMoving(x, y);
8231 void ContinueMoving(int x, int y)
8233 int element = Feld[x][y];
8234 struct ElementInfo *ei = &element_info[element];
8235 int direction = MovDir[x][y];
8236 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8237 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8238 int newx = x + dx, newy = y + dy;
8239 int stored = Store[x][y];
8240 int stored_new = Store[newx][newy];
8241 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8242 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8243 boolean last_line = (newy == lev_fieldy - 1);
8245 MovPos[x][y] += getElementMoveStepsize(x, y);
8247 if (pushed_by_player) /* special case: moving object pushed by player */
8248 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8250 if (ABS(MovPos[x][y]) < TILEX)
8252 TEST_DrawLevelField(x, y);
8254 return; /* element is still moving */
8257 /* element reached destination field */
8259 Feld[x][y] = EL_EMPTY;
8260 Feld[newx][newy] = element;
8261 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8263 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8265 element = Feld[newx][newy] = EL_ACID;
8267 else if (element == EL_MOLE)
8269 Feld[x][y] = EL_SAND;
8271 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8273 else if (element == EL_QUICKSAND_FILLING)
8275 element = Feld[newx][newy] = get_next_element(element);
8276 Store[newx][newy] = Store[x][y];
8278 else if (element == EL_QUICKSAND_EMPTYING)
8280 Feld[x][y] = get_next_element(element);
8281 element = Feld[newx][newy] = Store[x][y];
8283 else if (element == EL_QUICKSAND_FAST_FILLING)
8285 element = Feld[newx][newy] = get_next_element(element);
8286 Store[newx][newy] = Store[x][y];
8288 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8290 Feld[x][y] = get_next_element(element);
8291 element = Feld[newx][newy] = Store[x][y];
8293 else if (element == EL_MAGIC_WALL_FILLING)
8295 element = Feld[newx][newy] = get_next_element(element);
8296 if (!game.magic_wall_active)
8297 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8298 Store[newx][newy] = Store[x][y];
8300 else if (element == EL_MAGIC_WALL_EMPTYING)
8302 Feld[x][y] = get_next_element(element);
8303 if (!game.magic_wall_active)
8304 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8305 element = Feld[newx][newy] = Store[x][y];
8307 InitField(newx, newy, FALSE);
8309 else if (element == EL_BD_MAGIC_WALL_FILLING)
8311 element = Feld[newx][newy] = get_next_element(element);
8312 if (!game.magic_wall_active)
8313 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8314 Store[newx][newy] = Store[x][y];
8316 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8318 Feld[x][y] = get_next_element(element);
8319 if (!game.magic_wall_active)
8320 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8321 element = Feld[newx][newy] = Store[x][y];
8323 InitField(newx, newy, FALSE);
8325 else if (element == EL_DC_MAGIC_WALL_FILLING)
8327 element = Feld[newx][newy] = get_next_element(element);
8328 if (!game.magic_wall_active)
8329 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8330 Store[newx][newy] = Store[x][y];
8332 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8334 Feld[x][y] = get_next_element(element);
8335 if (!game.magic_wall_active)
8336 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8337 element = Feld[newx][newy] = Store[x][y];
8339 InitField(newx, newy, FALSE);
8341 else if (element == EL_AMOEBA_DROPPING)
8343 Feld[x][y] = get_next_element(element);
8344 element = Feld[newx][newy] = Store[x][y];
8346 else if (element == EL_SOKOBAN_OBJECT)
8349 Feld[x][y] = Back[x][y];
8351 if (Back[newx][newy])
8352 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8354 Back[x][y] = Back[newx][newy] = 0;
8357 Store[x][y] = EL_EMPTY;
8362 MovDelay[newx][newy] = 0;
8364 if (CAN_CHANGE_OR_HAS_ACTION(element))
8366 /* copy element change control values to new field */
8367 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8368 ChangePage[newx][newy] = ChangePage[x][y];
8369 ChangeCount[newx][newy] = ChangeCount[x][y];
8370 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8373 CustomValue[newx][newy] = CustomValue[x][y];
8375 ChangeDelay[x][y] = 0;
8376 ChangePage[x][y] = -1;
8377 ChangeCount[x][y] = 0;
8378 ChangeEvent[x][y] = -1;
8380 CustomValue[x][y] = 0;
8382 /* copy animation control values to new field */
8383 GfxFrame[newx][newy] = GfxFrame[x][y];
8384 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8385 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8386 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8388 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8390 /* some elements can leave other elements behind after moving */
8391 if (ei->move_leave_element != EL_EMPTY &&
8392 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8393 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8395 int move_leave_element = ei->move_leave_element;
8397 /* this makes it possible to leave the removed element again */
8398 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8399 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8401 Feld[x][y] = move_leave_element;
8403 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8404 MovDir[x][y] = direction;
8406 InitField(x, y, FALSE);
8408 if (GFX_CRUMBLED(Feld[x][y]))
8409 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8411 if (ELEM_IS_PLAYER(move_leave_element))
8412 RelocatePlayer(x, y, move_leave_element);
8415 /* do this after checking for left-behind element */
8416 ResetGfxAnimation(x, y); /* reset animation values for old field */
8418 if (!CAN_MOVE(element) ||
8419 (CAN_FALL(element) && direction == MV_DOWN &&
8420 (element == EL_SPRING ||
8421 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8422 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8423 GfxDir[x][y] = MovDir[newx][newy] = 0;
8425 TEST_DrawLevelField(x, y);
8426 TEST_DrawLevelField(newx, newy);
8428 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8430 /* prevent pushed element from moving on in pushed direction */
8431 if (pushed_by_player && CAN_MOVE(element) &&
8432 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8433 !(element_info[element].move_pattern & direction))
8434 TurnRound(newx, newy);
8436 /* prevent elements on conveyor belt from moving on in last direction */
8437 if (pushed_by_conveyor && CAN_FALL(element) &&
8438 direction & MV_HORIZONTAL)
8439 MovDir[newx][newy] = 0;
8441 if (!pushed_by_player)
8443 int nextx = newx + dx, nexty = newy + dy;
8444 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8446 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8448 if (CAN_FALL(element) && direction == MV_DOWN)
8449 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8451 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8452 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8454 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8455 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8458 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8460 TestIfBadThingTouchesPlayer(newx, newy);
8461 TestIfBadThingTouchesFriend(newx, newy);
8463 if (!IS_CUSTOM_ELEMENT(element))
8464 TestIfBadThingTouchesOtherBadThing(newx, newy);
8466 else if (element == EL_PENGUIN)
8467 TestIfFriendTouchesBadThing(newx, newy);
8469 if (DONT_GET_HIT_BY(element))
8471 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8474 /* give the player one last chance (one more frame) to move away */
8475 if (CAN_FALL(element) && direction == MV_DOWN &&
8476 (last_line || (!IS_FREE(x, newy + 1) &&
8477 (!IS_PLAYER(x, newy + 1) ||
8478 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8481 if (pushed_by_player && !game.use_change_when_pushing_bug)
8483 int push_side = MV_DIR_OPPOSITE(direction);
8484 struct PlayerInfo *player = PLAYERINFO(x, y);
8486 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8487 player->index_bit, push_side);
8488 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8489 player->index_bit, push_side);
8492 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8493 MovDelay[newx][newy] = 1;
8495 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8497 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8498 TestIfElementHitsCustomElement(newx, newy, direction);
8499 TestIfPlayerTouchesCustomElement(newx, newy);
8500 TestIfElementTouchesCustomElement(newx, newy);
8502 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8503 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8504 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8505 MV_DIR_OPPOSITE(direction));
8508 int AmoebeNachbarNr(int ax, int ay)
8511 int element = Feld[ax][ay];
8513 static int xy[4][2] =
8521 for (i = 0; i < NUM_DIRECTIONS; i++)
8523 int x = ax + xy[i][0];
8524 int y = ay + xy[i][1];
8526 if (!IN_LEV_FIELD(x, y))
8529 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8530 group_nr = AmoebaNr[x][y];
8536 void AmoebenVereinigen(int ax, int ay)
8538 int i, x, y, xx, yy;
8539 int new_group_nr = AmoebaNr[ax][ay];
8540 static int xy[4][2] =
8548 if (new_group_nr == 0)
8551 for (i = 0; i < NUM_DIRECTIONS; i++)
8556 if (!IN_LEV_FIELD(x, y))
8559 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8560 Feld[x][y] == EL_BD_AMOEBA ||
8561 Feld[x][y] == EL_AMOEBA_DEAD) &&
8562 AmoebaNr[x][y] != new_group_nr)
8564 int old_group_nr = AmoebaNr[x][y];
8566 if (old_group_nr == 0)
8569 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8570 AmoebaCnt[old_group_nr] = 0;
8571 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8572 AmoebaCnt2[old_group_nr] = 0;
8574 SCAN_PLAYFIELD(xx, yy)
8576 if (AmoebaNr[xx][yy] == old_group_nr)
8577 AmoebaNr[xx][yy] = new_group_nr;
8583 void AmoebeUmwandeln(int ax, int ay)
8587 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8589 int group_nr = AmoebaNr[ax][ay];
8594 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8595 printf("AmoebeUmwandeln(): This should never happen!\n");
8600 SCAN_PLAYFIELD(x, y)
8602 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8605 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8609 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8610 SND_AMOEBA_TURNING_TO_GEM :
8611 SND_AMOEBA_TURNING_TO_ROCK));
8616 static int xy[4][2] =
8624 for (i = 0; i < NUM_DIRECTIONS; i++)
8629 if (!IN_LEV_FIELD(x, y))
8632 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8634 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8635 SND_AMOEBA_TURNING_TO_GEM :
8636 SND_AMOEBA_TURNING_TO_ROCK));
8643 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8646 int group_nr = AmoebaNr[ax][ay];
8647 boolean done = FALSE;
8652 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8653 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8658 SCAN_PLAYFIELD(x, y)
8660 if (AmoebaNr[x][y] == group_nr &&
8661 (Feld[x][y] == EL_AMOEBA_DEAD ||
8662 Feld[x][y] == EL_BD_AMOEBA ||
8663 Feld[x][y] == EL_AMOEBA_GROWING))
8666 Feld[x][y] = new_element;
8667 InitField(x, y, FALSE);
8668 TEST_DrawLevelField(x, y);
8674 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8675 SND_BD_AMOEBA_TURNING_TO_ROCK :
8676 SND_BD_AMOEBA_TURNING_TO_GEM));
8679 void AmoebeWaechst(int x, int y)
8681 static unsigned int sound_delay = 0;
8682 static unsigned int sound_delay_value = 0;
8684 if (!MovDelay[x][y]) /* start new growing cycle */
8688 if (DelayReached(&sound_delay, sound_delay_value))
8690 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8691 sound_delay_value = 30;
8695 if (MovDelay[x][y]) /* wait some time before growing bigger */
8698 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8700 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8701 6 - MovDelay[x][y]);
8703 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8706 if (!MovDelay[x][y])
8708 Feld[x][y] = Store[x][y];
8710 TEST_DrawLevelField(x, y);
8715 void AmoebaDisappearing(int x, int y)
8717 static unsigned int sound_delay = 0;
8718 static unsigned int sound_delay_value = 0;
8720 if (!MovDelay[x][y]) /* start new shrinking cycle */
8724 if (DelayReached(&sound_delay, sound_delay_value))
8725 sound_delay_value = 30;
8728 if (MovDelay[x][y]) /* wait some time before shrinking */
8731 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8733 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8734 6 - MovDelay[x][y]);
8736 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8739 if (!MovDelay[x][y])
8741 Feld[x][y] = EL_EMPTY;
8742 TEST_DrawLevelField(x, y);
8744 /* don't let mole enter this field in this cycle;
8745 (give priority to objects falling to this field from above) */
8751 void AmoebeAbleger(int ax, int ay)
8754 int element = Feld[ax][ay];
8755 int graphic = el2img(element);
8756 int newax = ax, neway = ay;
8757 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8758 static int xy[4][2] =
8766 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8768 Feld[ax][ay] = EL_AMOEBA_DEAD;
8769 TEST_DrawLevelField(ax, ay);
8773 if (IS_ANIMATED(graphic))
8774 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8776 if (!MovDelay[ax][ay]) /* start making new amoeba field */
8777 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8779 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
8782 if (MovDelay[ax][ay])
8786 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8789 int x = ax + xy[start][0];
8790 int y = ay + xy[start][1];
8792 if (!IN_LEV_FIELD(x, y))
8795 if (IS_FREE(x, y) ||
8796 CAN_GROW_INTO(Feld[x][y]) ||
8797 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8798 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8804 if (newax == ax && neway == ay)
8807 else /* normal or "filled" (BD style) amoeba */
8810 boolean waiting_for_player = FALSE;
8812 for (i = 0; i < NUM_DIRECTIONS; i++)
8814 int j = (start + i) % 4;
8815 int x = ax + xy[j][0];
8816 int y = ay + xy[j][1];
8818 if (!IN_LEV_FIELD(x, y))
8821 if (IS_FREE(x, y) ||
8822 CAN_GROW_INTO(Feld[x][y]) ||
8823 Feld[x][y] == EL_QUICKSAND_EMPTY ||
8824 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8830 else if (IS_PLAYER(x, y))
8831 waiting_for_player = TRUE;
8834 if (newax == ax && neway == ay) /* amoeba cannot grow */
8836 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8838 Feld[ax][ay] = EL_AMOEBA_DEAD;
8839 TEST_DrawLevelField(ax, ay);
8840 AmoebaCnt[AmoebaNr[ax][ay]]--;
8842 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
8844 if (element == EL_AMOEBA_FULL)
8845 AmoebeUmwandeln(ax, ay);
8846 else if (element == EL_BD_AMOEBA)
8847 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8852 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8854 /* amoeba gets larger by growing in some direction */
8856 int new_group_nr = AmoebaNr[ax][ay];
8859 if (new_group_nr == 0)
8861 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8862 printf("AmoebeAbleger(): This should never happen!\n");
8867 AmoebaNr[newax][neway] = new_group_nr;
8868 AmoebaCnt[new_group_nr]++;
8869 AmoebaCnt2[new_group_nr]++;
8871 /* if amoeba touches other amoeba(s) after growing, unify them */
8872 AmoebenVereinigen(newax, neway);
8874 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8876 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8882 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8883 (neway == lev_fieldy - 1 && newax != ax))
8885 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
8886 Store[newax][neway] = element;
8888 else if (neway == ay || element == EL_EMC_DRIPPER)
8890 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
8892 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8896 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
8897 Feld[ax][ay] = EL_AMOEBA_DROPPING;
8898 Store[ax][ay] = EL_AMOEBA_DROP;
8899 ContinueMoving(ax, ay);
8903 TEST_DrawLevelField(newax, neway);
8906 void Life(int ax, int ay)
8910 int element = Feld[ax][ay];
8911 int graphic = el2img(element);
8912 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8914 boolean changed = FALSE;
8916 if (IS_ANIMATED(graphic))
8917 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8922 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
8923 MovDelay[ax][ay] = life_time;
8925 if (MovDelay[ax][ay]) /* wait some time before next cycle */
8928 if (MovDelay[ax][ay])
8932 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8934 int xx = ax+x1, yy = ay+y1;
8937 if (!IN_LEV_FIELD(xx, yy))
8940 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8942 int x = xx+x2, y = yy+y2;
8944 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8947 if (((Feld[x][y] == element ||
8948 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8950 (IS_FREE(x, y) && Stop[x][y]))
8954 if (xx == ax && yy == ay) /* field in the middle */
8956 if (nachbarn < life_parameter[0] ||
8957 nachbarn > life_parameter[1])
8959 Feld[xx][yy] = EL_EMPTY;
8961 TEST_DrawLevelField(xx, yy);
8962 Stop[xx][yy] = TRUE;
8966 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8967 { /* free border field */
8968 if (nachbarn >= life_parameter[2] &&
8969 nachbarn <= life_parameter[3])
8971 Feld[xx][yy] = element;
8972 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8974 TEST_DrawLevelField(xx, yy);
8975 Stop[xx][yy] = TRUE;
8982 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8983 SND_GAME_OF_LIFE_GROWING);
8986 static void InitRobotWheel(int x, int y)
8988 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8991 static void RunRobotWheel(int x, int y)
8993 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8996 static void StopRobotWheel(int x, int y)
8998 if (ZX == x && ZY == y)
9002 game.robot_wheel_active = FALSE;
9006 static void InitTimegateWheel(int x, int y)
9008 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9011 static void RunTimegateWheel(int x, int y)
9013 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9016 static void InitMagicBallDelay(int x, int y)
9018 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9021 static void ActivateMagicBall(int bx, int by)
9025 if (level.ball_random)
9027 int pos_border = RND(8); /* select one of the eight border elements */
9028 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9029 int xx = pos_content % 3;
9030 int yy = pos_content / 3;
9035 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9036 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9040 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9042 int xx = x - bx + 1;
9043 int yy = y - by + 1;
9045 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9046 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9050 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9053 void CheckExit(int x, int y)
9055 if (local_player->gems_still_needed > 0 ||
9056 local_player->sokobanfields_still_needed > 0 ||
9057 local_player->lights_still_needed > 0)
9059 int element = Feld[x][y];
9060 int graphic = el2img(element);
9062 if (IS_ANIMATED(graphic))
9063 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9068 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9071 Feld[x][y] = EL_EXIT_OPENING;
9073 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9076 void CheckExitEM(int x, int y)
9078 if (local_player->gems_still_needed > 0 ||
9079 local_player->sokobanfields_still_needed > 0 ||
9080 local_player->lights_still_needed > 0)
9082 int element = Feld[x][y];
9083 int graphic = el2img(element);
9085 if (IS_ANIMATED(graphic))
9086 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9091 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9094 Feld[x][y] = EL_EM_EXIT_OPENING;
9096 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9099 void CheckExitSteel(int x, int y)
9101 if (local_player->gems_still_needed > 0 ||
9102 local_player->sokobanfields_still_needed > 0 ||
9103 local_player->lights_still_needed > 0)
9105 int element = Feld[x][y];
9106 int graphic = el2img(element);
9108 if (IS_ANIMATED(graphic))
9109 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9114 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9117 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9119 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9122 void CheckExitSteelEM(int x, int y)
9124 if (local_player->gems_still_needed > 0 ||
9125 local_player->sokobanfields_still_needed > 0 ||
9126 local_player->lights_still_needed > 0)
9128 int element = Feld[x][y];
9129 int graphic = el2img(element);
9131 if (IS_ANIMATED(graphic))
9132 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9137 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9140 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9142 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9145 void CheckExitSP(int x, int y)
9147 if (local_player->gems_still_needed > 0)
9149 int element = Feld[x][y];
9150 int graphic = el2img(element);
9152 if (IS_ANIMATED(graphic))
9153 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9158 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9161 Feld[x][y] = EL_SP_EXIT_OPENING;
9163 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9166 static void CloseAllOpenTimegates()
9170 SCAN_PLAYFIELD(x, y)
9172 int element = Feld[x][y];
9174 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9176 Feld[x][y] = EL_TIMEGATE_CLOSING;
9178 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9183 void DrawTwinkleOnField(int x, int y)
9185 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9188 if (Feld[x][y] == EL_BD_DIAMOND)
9191 if (MovDelay[x][y] == 0) /* next animation frame */
9192 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9194 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9198 DrawLevelElementAnimation(x, y, Feld[x][y]);
9200 if (MovDelay[x][y] != 0)
9202 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9203 10 - MovDelay[x][y]);
9205 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9210 void MauerWaechst(int x, int y)
9214 if (!MovDelay[x][y]) /* next animation frame */
9215 MovDelay[x][y] = 3 * delay;
9217 if (MovDelay[x][y]) /* wait some time before next frame */
9221 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9223 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9224 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9226 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9229 if (!MovDelay[x][y])
9231 if (MovDir[x][y] == MV_LEFT)
9233 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9234 TEST_DrawLevelField(x - 1, y);
9236 else if (MovDir[x][y] == MV_RIGHT)
9238 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9239 TEST_DrawLevelField(x + 1, y);
9241 else if (MovDir[x][y] == MV_UP)
9243 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9244 TEST_DrawLevelField(x, y - 1);
9248 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9249 TEST_DrawLevelField(x, y + 1);
9252 Feld[x][y] = Store[x][y];
9254 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9255 TEST_DrawLevelField(x, y);
9260 void MauerAbleger(int ax, int ay)
9262 int element = Feld[ax][ay];
9263 int graphic = el2img(element);
9264 boolean oben_frei = FALSE, unten_frei = FALSE;
9265 boolean links_frei = FALSE, rechts_frei = FALSE;
9266 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9267 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9268 boolean new_wall = FALSE;
9270 if (IS_ANIMATED(graphic))
9271 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9273 if (!MovDelay[ax][ay]) /* start building new wall */
9274 MovDelay[ax][ay] = 6;
9276 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9279 if (MovDelay[ax][ay])
9283 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9285 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9287 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9289 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9292 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9293 element == EL_EXPANDABLE_WALL_ANY)
9297 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9298 Store[ax][ay-1] = element;
9299 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9300 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9301 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9302 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9307 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9308 Store[ax][ay+1] = element;
9309 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9310 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9311 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9312 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9317 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9318 element == EL_EXPANDABLE_WALL_ANY ||
9319 element == EL_EXPANDABLE_WALL ||
9320 element == EL_BD_EXPANDABLE_WALL)
9324 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9325 Store[ax-1][ay] = element;
9326 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9327 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9328 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9329 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9335 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9336 Store[ax+1][ay] = element;
9337 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9338 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9339 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9340 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9345 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9346 TEST_DrawLevelField(ax, ay);
9348 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9350 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9351 unten_massiv = TRUE;
9352 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9353 links_massiv = TRUE;
9354 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9355 rechts_massiv = TRUE;
9357 if (((oben_massiv && unten_massiv) ||
9358 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9359 element == EL_EXPANDABLE_WALL) &&
9360 ((links_massiv && rechts_massiv) ||
9361 element == EL_EXPANDABLE_WALL_VERTICAL))
9362 Feld[ax][ay] = EL_WALL;
9365 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9368 void MauerAblegerStahl(int ax, int ay)
9370 int element = Feld[ax][ay];
9371 int graphic = el2img(element);
9372 boolean oben_frei = FALSE, unten_frei = FALSE;
9373 boolean links_frei = FALSE, rechts_frei = FALSE;
9374 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9375 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9376 boolean new_wall = FALSE;
9378 if (IS_ANIMATED(graphic))
9379 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9381 if (!MovDelay[ax][ay]) /* start building new wall */
9382 MovDelay[ax][ay] = 6;
9384 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9387 if (MovDelay[ax][ay])
9391 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9393 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9395 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9397 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9400 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9401 element == EL_EXPANDABLE_STEELWALL_ANY)
9405 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9406 Store[ax][ay-1] = element;
9407 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9408 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9409 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9410 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9415 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9416 Store[ax][ay+1] = element;
9417 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9418 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9419 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9420 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9425 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9426 element == EL_EXPANDABLE_STEELWALL_ANY)
9430 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9431 Store[ax-1][ay] = element;
9432 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9433 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9434 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9435 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9441 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9442 Store[ax+1][ay] = element;
9443 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9444 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9445 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9446 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9451 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9453 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9454 unten_massiv = TRUE;
9455 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9456 links_massiv = TRUE;
9457 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9458 rechts_massiv = TRUE;
9460 if (((oben_massiv && unten_massiv) ||
9461 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9462 ((links_massiv && rechts_massiv) ||
9463 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9464 Feld[ax][ay] = EL_STEELWALL;
9467 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9470 void CheckForDragon(int x, int y)
9473 boolean dragon_found = FALSE;
9474 static int xy[4][2] =
9482 for (i = 0; i < NUM_DIRECTIONS; i++)
9484 for (j = 0; j < 4; j++)
9486 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9488 if (IN_LEV_FIELD(xx, yy) &&
9489 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9491 if (Feld[xx][yy] == EL_DRAGON)
9492 dragon_found = TRUE;
9501 for (i = 0; i < NUM_DIRECTIONS; i++)
9503 for (j = 0; j < 3; j++)
9505 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9507 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9509 Feld[xx][yy] = EL_EMPTY;
9510 TEST_DrawLevelField(xx, yy);
9519 static void InitBuggyBase(int x, int y)
9521 int element = Feld[x][y];
9522 int activating_delay = FRAMES_PER_SECOND / 4;
9525 (element == EL_SP_BUGGY_BASE ?
9526 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9527 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9529 element == EL_SP_BUGGY_BASE_ACTIVE ?
9530 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9533 static void WarnBuggyBase(int x, int y)
9536 static int xy[4][2] =
9544 for (i = 0; i < NUM_DIRECTIONS; i++)
9546 int xx = x + xy[i][0];
9547 int yy = y + xy[i][1];
9549 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9551 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9558 static void InitTrap(int x, int y)
9560 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9563 static void ActivateTrap(int x, int y)
9565 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9568 static void ChangeActiveTrap(int x, int y)
9570 int graphic = IMG_TRAP_ACTIVE;
9572 /* if new animation frame was drawn, correct crumbled sand border */
9573 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9574 TEST_DrawLevelFieldCrumbled(x, y);
9577 static int getSpecialActionElement(int element, int number, int base_element)
9579 return (element != EL_EMPTY ? element :
9580 number != -1 ? base_element + number - 1 :
9584 static int getModifiedActionNumber(int value_old, int operator, int operand,
9585 int value_min, int value_max)
9587 int value_new = (operator == CA_MODE_SET ? operand :
9588 operator == CA_MODE_ADD ? value_old + operand :
9589 operator == CA_MODE_SUBTRACT ? value_old - operand :
9590 operator == CA_MODE_MULTIPLY ? value_old * operand :
9591 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9592 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9595 return (value_new < value_min ? value_min :
9596 value_new > value_max ? value_max :
9600 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9602 struct ElementInfo *ei = &element_info[element];
9603 struct ElementChangeInfo *change = &ei->change_page[page];
9604 int target_element = change->target_element;
9605 int action_type = change->action_type;
9606 int action_mode = change->action_mode;
9607 int action_arg = change->action_arg;
9608 int action_element = change->action_element;
9611 if (!change->has_action)
9614 /* ---------- determine action paramater values -------------------------- */
9616 int level_time_value =
9617 (level.time > 0 ? TimeLeft :
9620 int action_arg_element_raw =
9621 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9622 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9623 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9624 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
9625 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9626 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
9627 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
9629 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9631 int action_arg_direction =
9632 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9633 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9634 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9635 change->actual_trigger_side :
9636 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9637 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9640 int action_arg_number_min =
9641 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9644 int action_arg_number_max =
9645 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9646 action_type == CA_SET_LEVEL_GEMS ? 999 :
9647 action_type == CA_SET_LEVEL_TIME ? 9999 :
9648 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9649 action_type == CA_SET_CE_VALUE ? 9999 :
9650 action_type == CA_SET_CE_SCORE ? 9999 :
9653 int action_arg_number_reset =
9654 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9655 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9656 action_type == CA_SET_LEVEL_TIME ? level.time :
9657 action_type == CA_SET_LEVEL_SCORE ? 0 :
9658 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9659 action_type == CA_SET_CE_SCORE ? 0 :
9662 int action_arg_number =
9663 (action_arg <= CA_ARG_MAX ? action_arg :
9664 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9665 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9666 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9667 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9668 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9669 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9670 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9671 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9672 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9673 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9674 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9675 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9676 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9677 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9678 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9679 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9680 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9681 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9682 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9683 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9684 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
9687 int action_arg_number_old =
9688 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9689 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9690 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9691 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9692 action_type == CA_SET_CE_SCORE ? ei->collect_score :
9695 int action_arg_number_new =
9696 getModifiedActionNumber(action_arg_number_old,
9697 action_mode, action_arg_number,
9698 action_arg_number_min, action_arg_number_max);
9700 int trigger_player_bits =
9701 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9702 change->actual_trigger_player_bits : change->trigger_player);
9704 int action_arg_player_bits =
9705 (action_arg >= CA_ARG_PLAYER_1 &&
9706 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9707 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9708 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9711 /* ---------- execute action -------------------------------------------- */
9713 switch (action_type)
9720 /* ---------- level actions ------------------------------------------- */
9722 case CA_RESTART_LEVEL:
9724 game.restart_level = TRUE;
9729 case CA_SHOW_ENVELOPE:
9731 int element = getSpecialActionElement(action_arg_element,
9732 action_arg_number, EL_ENVELOPE_1);
9734 if (IS_ENVELOPE(element))
9735 local_player->show_envelope = element;
9740 case CA_SET_LEVEL_TIME:
9742 if (level.time > 0) /* only modify limited time value */
9744 TimeLeft = action_arg_number_new;
9746 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9748 DisplayGameControlValues();
9750 if (!TimeLeft && setup.time_limit)
9751 for (i = 0; i < MAX_PLAYERS; i++)
9752 KillPlayer(&stored_player[i]);
9758 case CA_SET_LEVEL_SCORE:
9760 local_player->score = action_arg_number_new;
9762 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9764 DisplayGameControlValues();
9769 case CA_SET_LEVEL_GEMS:
9771 local_player->gems_still_needed = action_arg_number_new;
9773 game.snapshot.collected_item = TRUE;
9775 game_panel_controls[GAME_PANEL_GEMS].value =
9776 local_player->gems_still_needed;
9778 DisplayGameControlValues();
9783 case CA_SET_LEVEL_WIND:
9785 game.wind_direction = action_arg_direction;
9790 case CA_SET_LEVEL_RANDOM_SEED:
9792 /* ensure that setting a new random seed while playing is predictable */
9793 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9798 /* ---------- player actions ------------------------------------------ */
9800 case CA_MOVE_PLAYER:
9802 /* automatically move to the next field in specified direction */
9803 for (i = 0; i < MAX_PLAYERS; i++)
9804 if (trigger_player_bits & (1 << i))
9805 stored_player[i].programmed_action = action_arg_direction;
9810 case CA_EXIT_PLAYER:
9812 for (i = 0; i < MAX_PLAYERS; i++)
9813 if (action_arg_player_bits & (1 << i))
9814 PlayerWins(&stored_player[i]);
9819 case CA_KILL_PLAYER:
9821 for (i = 0; i < MAX_PLAYERS; i++)
9822 if (action_arg_player_bits & (1 << i))
9823 KillPlayer(&stored_player[i]);
9828 case CA_SET_PLAYER_KEYS:
9830 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9831 int element = getSpecialActionElement(action_arg_element,
9832 action_arg_number, EL_KEY_1);
9834 if (IS_KEY(element))
9836 for (i = 0; i < MAX_PLAYERS; i++)
9838 if (trigger_player_bits & (1 << i))
9840 stored_player[i].key[KEY_NR(element)] = key_state;
9842 DrawGameDoorValues();
9850 case CA_SET_PLAYER_SPEED:
9852 for (i = 0; i < MAX_PLAYERS; i++)
9854 if (trigger_player_bits & (1 << i))
9856 int move_stepsize = TILEX / stored_player[i].move_delay_value;
9858 if (action_arg == CA_ARG_SPEED_FASTER &&
9859 stored_player[i].cannot_move)
9861 action_arg_number = STEPSIZE_VERY_SLOW;
9863 else if (action_arg == CA_ARG_SPEED_SLOWER ||
9864 action_arg == CA_ARG_SPEED_FASTER)
9866 action_arg_number = 2;
9867 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9870 else if (action_arg == CA_ARG_NUMBER_RESET)
9872 action_arg_number = level.initial_player_stepsize[i];
9876 getModifiedActionNumber(move_stepsize,
9879 action_arg_number_min,
9880 action_arg_number_max);
9882 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9889 case CA_SET_PLAYER_SHIELD:
9891 for (i = 0; i < MAX_PLAYERS; i++)
9893 if (trigger_player_bits & (1 << i))
9895 if (action_arg == CA_ARG_SHIELD_OFF)
9897 stored_player[i].shield_normal_time_left = 0;
9898 stored_player[i].shield_deadly_time_left = 0;
9900 else if (action_arg == CA_ARG_SHIELD_NORMAL)
9902 stored_player[i].shield_normal_time_left = 999999;
9904 else if (action_arg == CA_ARG_SHIELD_DEADLY)
9906 stored_player[i].shield_normal_time_left = 999999;
9907 stored_player[i].shield_deadly_time_left = 999999;
9915 case CA_SET_PLAYER_GRAVITY:
9917 for (i = 0; i < MAX_PLAYERS; i++)
9919 if (trigger_player_bits & (1 << i))
9921 stored_player[i].gravity =
9922 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
9923 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
9924 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9925 stored_player[i].gravity);
9932 case CA_SET_PLAYER_ARTWORK:
9934 for (i = 0; i < MAX_PLAYERS; i++)
9936 if (trigger_player_bits & (1 << i))
9938 int artwork_element = action_arg_element;
9940 if (action_arg == CA_ARG_ELEMENT_RESET)
9942 (level.use_artwork_element[i] ? level.artwork_element[i] :
9943 stored_player[i].element_nr);
9945 if (stored_player[i].artwork_element != artwork_element)
9946 stored_player[i].Frame = 0;
9948 stored_player[i].artwork_element = artwork_element;
9950 SetPlayerWaiting(&stored_player[i], FALSE);
9952 /* set number of special actions for bored and sleeping animation */
9953 stored_player[i].num_special_action_bored =
9954 get_num_special_action(artwork_element,
9955 ACTION_BORING_1, ACTION_BORING_LAST);
9956 stored_player[i].num_special_action_sleeping =
9957 get_num_special_action(artwork_element,
9958 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9965 case CA_SET_PLAYER_INVENTORY:
9967 for (i = 0; i < MAX_PLAYERS; i++)
9969 struct PlayerInfo *player = &stored_player[i];
9972 if (trigger_player_bits & (1 << i))
9974 int inventory_element = action_arg_element;
9976 if (action_arg == CA_ARG_ELEMENT_TARGET ||
9977 action_arg == CA_ARG_ELEMENT_TRIGGER ||
9978 action_arg == CA_ARG_ELEMENT_ACTION)
9980 int element = inventory_element;
9981 int collect_count = element_info[element].collect_count_initial;
9983 if (!IS_CUSTOM_ELEMENT(element))
9986 if (collect_count == 0)
9987 player->inventory_infinite_element = element;
9989 for (k = 0; k < collect_count; k++)
9990 if (player->inventory_size < MAX_INVENTORY_SIZE)
9991 player->inventory_element[player->inventory_size++] =
9994 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9995 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9996 action_arg == CA_ARG_INVENTORY_RM_ACTION)
9998 if (player->inventory_infinite_element != EL_UNDEFINED &&
9999 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10000 action_arg_element_raw))
10001 player->inventory_infinite_element = EL_UNDEFINED;
10003 for (k = 0, j = 0; j < player->inventory_size; j++)
10005 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10006 action_arg_element_raw))
10007 player->inventory_element[k++] = player->inventory_element[j];
10010 player->inventory_size = k;
10012 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10014 if (player->inventory_size > 0)
10016 for (j = 0; j < player->inventory_size - 1; j++)
10017 player->inventory_element[j] = player->inventory_element[j + 1];
10019 player->inventory_size--;
10022 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10024 if (player->inventory_size > 0)
10025 player->inventory_size--;
10027 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10029 player->inventory_infinite_element = EL_UNDEFINED;
10030 player->inventory_size = 0;
10032 else if (action_arg == CA_ARG_INVENTORY_RESET)
10034 player->inventory_infinite_element = EL_UNDEFINED;
10035 player->inventory_size = 0;
10037 if (level.use_initial_inventory[i])
10039 for (j = 0; j < level.initial_inventory_size[i]; j++)
10041 int element = level.initial_inventory_content[i][j];
10042 int collect_count = element_info[element].collect_count_initial;
10044 if (!IS_CUSTOM_ELEMENT(element))
10047 if (collect_count == 0)
10048 player->inventory_infinite_element = element;
10050 for (k = 0; k < collect_count; k++)
10051 if (player->inventory_size < MAX_INVENTORY_SIZE)
10052 player->inventory_element[player->inventory_size++] =
10063 /* ---------- CE actions ---------------------------------------------- */
10065 case CA_SET_CE_VALUE:
10067 int last_ce_value = CustomValue[x][y];
10069 CustomValue[x][y] = action_arg_number_new;
10071 if (CustomValue[x][y] != last_ce_value)
10073 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10074 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10076 if (CustomValue[x][y] == 0)
10078 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10079 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10086 case CA_SET_CE_SCORE:
10088 int last_ce_score = ei->collect_score;
10090 ei->collect_score = action_arg_number_new;
10092 if (ei->collect_score != last_ce_score)
10094 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10095 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10097 if (ei->collect_score == 0)
10101 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10102 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10105 This is a very special case that seems to be a mixture between
10106 CheckElementChange() and CheckTriggeredElementChange(): while
10107 the first one only affects single elements that are triggered
10108 directly, the second one affects multiple elements in the playfield
10109 that are triggered indirectly by another element. This is a third
10110 case: Changing the CE score always affects multiple identical CEs,
10111 so every affected CE must be checked, not only the single CE for
10112 which the CE score was changed in the first place (as every instance
10113 of that CE shares the same CE score, and therefore also can change)!
10115 SCAN_PLAYFIELD(xx, yy)
10117 if (Feld[xx][yy] == element)
10118 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10119 CE_SCORE_GETS_ZERO);
10127 case CA_SET_CE_ARTWORK:
10129 int artwork_element = action_arg_element;
10130 boolean reset_frame = FALSE;
10133 if (action_arg == CA_ARG_ELEMENT_RESET)
10134 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10137 if (ei->gfx_element != artwork_element)
10138 reset_frame = TRUE;
10140 ei->gfx_element = artwork_element;
10142 SCAN_PLAYFIELD(xx, yy)
10144 if (Feld[xx][yy] == element)
10148 ResetGfxAnimation(xx, yy);
10149 ResetRandomAnimationValue(xx, yy);
10152 TEST_DrawLevelField(xx, yy);
10159 /* ---------- engine actions ------------------------------------------ */
10161 case CA_SET_ENGINE_SCAN_MODE:
10163 InitPlayfieldScanMode(action_arg);
10173 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10175 int old_element = Feld[x][y];
10176 int new_element = GetElementFromGroupElement(element);
10177 int previous_move_direction = MovDir[x][y];
10178 int last_ce_value = CustomValue[x][y];
10179 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10180 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10181 boolean add_player_onto_element = (new_element_is_player &&
10182 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10183 IS_WALKABLE(old_element));
10185 if (!add_player_onto_element)
10187 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10188 RemoveMovingField(x, y);
10192 Feld[x][y] = new_element;
10194 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10195 MovDir[x][y] = previous_move_direction;
10197 if (element_info[new_element].use_last_ce_value)
10198 CustomValue[x][y] = last_ce_value;
10200 InitField_WithBug1(x, y, FALSE);
10202 new_element = Feld[x][y]; /* element may have changed */
10204 ResetGfxAnimation(x, y);
10205 ResetRandomAnimationValue(x, y);
10207 TEST_DrawLevelField(x, y);
10209 if (GFX_CRUMBLED(new_element))
10210 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10213 /* check if element under the player changes from accessible to unaccessible
10214 (needed for special case of dropping element which then changes) */
10215 /* (must be checked after creating new element for walkable group elements) */
10216 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10217 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10224 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10225 if (new_element_is_player)
10226 RelocatePlayer(x, y, new_element);
10229 ChangeCount[x][y]++; /* count number of changes in the same frame */
10231 TestIfBadThingTouchesPlayer(x, y);
10232 TestIfPlayerTouchesCustomElement(x, y);
10233 TestIfElementTouchesCustomElement(x, y);
10236 static void CreateField(int x, int y, int element)
10238 CreateFieldExt(x, y, element, FALSE);
10241 static void CreateElementFromChange(int x, int y, int element)
10243 element = GET_VALID_RUNTIME_ELEMENT(element);
10245 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10247 int old_element = Feld[x][y];
10249 /* prevent changed element from moving in same engine frame
10250 unless both old and new element can either fall or move */
10251 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10252 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10256 CreateFieldExt(x, y, element, TRUE);
10259 static boolean ChangeElement(int x, int y, int element, int page)
10261 struct ElementInfo *ei = &element_info[element];
10262 struct ElementChangeInfo *change = &ei->change_page[page];
10263 int ce_value = CustomValue[x][y];
10264 int ce_score = ei->collect_score;
10265 int target_element;
10266 int old_element = Feld[x][y];
10268 /* always use default change event to prevent running into a loop */
10269 if (ChangeEvent[x][y] == -1)
10270 ChangeEvent[x][y] = CE_DELAY;
10272 if (ChangeEvent[x][y] == CE_DELAY)
10274 /* reset actual trigger element, trigger player and action element */
10275 change->actual_trigger_element = EL_EMPTY;
10276 change->actual_trigger_player = EL_EMPTY;
10277 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10278 change->actual_trigger_side = CH_SIDE_NONE;
10279 change->actual_trigger_ce_value = 0;
10280 change->actual_trigger_ce_score = 0;
10283 /* do not change elements more than a specified maximum number of changes */
10284 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10287 ChangeCount[x][y]++; /* count number of changes in the same frame */
10289 if (change->explode)
10296 if (change->use_target_content)
10298 boolean complete_replace = TRUE;
10299 boolean can_replace[3][3];
10302 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10305 boolean is_walkable;
10306 boolean is_diggable;
10307 boolean is_collectible;
10308 boolean is_removable;
10309 boolean is_destructible;
10310 int ex = x + xx - 1;
10311 int ey = y + yy - 1;
10312 int content_element = change->target_content.e[xx][yy];
10315 can_replace[xx][yy] = TRUE;
10317 if (ex == x && ey == y) /* do not check changing element itself */
10320 if (content_element == EL_EMPTY_SPACE)
10322 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10327 if (!IN_LEV_FIELD(ex, ey))
10329 can_replace[xx][yy] = FALSE;
10330 complete_replace = FALSE;
10337 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10338 e = MovingOrBlocked2Element(ex, ey);
10340 is_empty = (IS_FREE(ex, ey) ||
10341 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10343 is_walkable = (is_empty || IS_WALKABLE(e));
10344 is_diggable = (is_empty || IS_DIGGABLE(e));
10345 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10346 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10347 is_removable = (is_diggable || is_collectible);
10349 can_replace[xx][yy] =
10350 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10351 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10352 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10353 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10354 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10355 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10356 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10358 if (!can_replace[xx][yy])
10359 complete_replace = FALSE;
10362 if (!change->only_if_complete || complete_replace)
10364 boolean something_has_changed = FALSE;
10366 if (change->only_if_complete && change->use_random_replace &&
10367 RND(100) < change->random_percentage)
10370 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10372 int ex = x + xx - 1;
10373 int ey = y + yy - 1;
10374 int content_element;
10376 if (can_replace[xx][yy] && (!change->use_random_replace ||
10377 RND(100) < change->random_percentage))
10379 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10380 RemoveMovingField(ex, ey);
10382 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10384 content_element = change->target_content.e[xx][yy];
10385 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10386 ce_value, ce_score);
10388 CreateElementFromChange(ex, ey, target_element);
10390 something_has_changed = TRUE;
10392 /* for symmetry reasons, freeze newly created border elements */
10393 if (ex != x || ey != y)
10394 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10398 if (something_has_changed)
10400 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10401 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10407 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10408 ce_value, ce_score);
10410 if (element == EL_DIAGONAL_GROWING ||
10411 element == EL_DIAGONAL_SHRINKING)
10413 target_element = Store[x][y];
10415 Store[x][y] = EL_EMPTY;
10418 CreateElementFromChange(x, y, target_element);
10420 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10421 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10424 /* this uses direct change before indirect change */
10425 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10430 static void HandleElementChange(int x, int y, int page)
10432 int element = MovingOrBlocked2Element(x, y);
10433 struct ElementInfo *ei = &element_info[element];
10434 struct ElementChangeInfo *change = &ei->change_page[page];
10435 boolean handle_action_before_change = FALSE;
10438 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10439 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10442 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10443 x, y, element, element_info[element].token_name);
10444 printf("HandleElementChange(): This should never happen!\n");
10449 /* this can happen with classic bombs on walkable, changing elements */
10450 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10455 if (ChangeDelay[x][y] == 0) /* initialize element change */
10457 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10459 if (change->can_change)
10461 /* !!! not clear why graphic animation should be reset at all here !!! */
10462 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10463 /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10466 GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10468 When using an animation frame delay of 1 (this only happens with
10469 "sp_zonk.moving.left/right" in the classic graphics), the default
10470 (non-moving) animation shows wrong animation frames (while the
10471 moving animation, like "sp_zonk.moving.left/right", is correct,
10472 so this graphical bug never shows up with the classic graphics).
10473 For an animation with 4 frames, this causes wrong frames 0,0,1,2
10474 be drawn instead of the correct frames 0,1,2,3. This is caused by
10475 "GfxFrame[][]" being reset *twice* (in two successive frames) after
10476 an element change: First when the change delay ("ChangeDelay[][]")
10477 counter has reached zero after decrementing, then a second time in
10478 the next frame (after "GfxFrame[][]" was already incremented) when
10479 "ChangeDelay[][]" is reset to the initial delay value again.
10481 This causes frame 0 to be drawn twice, while the last frame won't
10482 be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10484 As some animations may already be cleverly designed around this bug
10485 (at least the "Snake Bite" snake tail animation does this), it cannot
10486 simply be fixed here without breaking such existing animations.
10487 Unfortunately, it cannot easily be detected if a graphics set was
10488 designed "before" or "after" the bug was fixed. As a workaround,
10489 a new graphics set option "game.graphics_engine_version" was added
10490 to be able to specify the game's major release version for which the
10491 graphics set was designed, which can then be used to decide if the
10492 bugfix should be used (version 4 and above) or not (version 3 or
10493 below, or if no version was specified at all, as with old sets).
10495 (The wrong/fixed animation frames can be tested with the test level set
10496 "test_gfxframe" and level "000", which contains a specially prepared
10497 custom element at level position (x/y) == (11/9) which uses the zonk
10498 animation mentioned above. Using "game.graphics_engine_version: 4"
10499 fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10500 This can also be seen from the debug output for this test element.)
10503 /* when a custom element is about to change (for example by change delay),
10504 do not reset graphic animation when the custom element is moving */
10505 if (game.graphics_engine_version < 4 &&
10508 ResetGfxAnimation(x, y);
10509 ResetRandomAnimationValue(x, y);
10512 if (change->pre_change_function)
10513 change->pre_change_function(x, y);
10517 ChangeDelay[x][y]--;
10519 if (ChangeDelay[x][y] != 0) /* continue element change */
10521 if (change->can_change)
10523 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10525 if (IS_ANIMATED(graphic))
10526 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10528 if (change->change_function)
10529 change->change_function(x, y);
10532 else /* finish element change */
10534 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10536 page = ChangePage[x][y];
10537 ChangePage[x][y] = -1;
10539 change = &ei->change_page[page];
10542 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10544 ChangeDelay[x][y] = 1; /* try change after next move step */
10545 ChangePage[x][y] = page; /* remember page to use for change */
10550 /* special case: set new level random seed before changing element */
10551 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10552 handle_action_before_change = TRUE;
10554 if (change->has_action && handle_action_before_change)
10555 ExecuteCustomElementAction(x, y, element, page);
10557 if (change->can_change)
10559 if (ChangeElement(x, y, element, page))
10561 if (change->post_change_function)
10562 change->post_change_function(x, y);
10566 if (change->has_action && !handle_action_before_change)
10567 ExecuteCustomElementAction(x, y, element, page);
10571 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10572 int trigger_element,
10574 int trigger_player,
10578 boolean change_done_any = FALSE;
10579 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10582 if (!(trigger_events[trigger_element][trigger_event]))
10585 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10587 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10589 int element = EL_CUSTOM_START + i;
10590 boolean change_done = FALSE;
10593 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10594 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10597 for (p = 0; p < element_info[element].num_change_pages; p++)
10599 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10601 if (change->can_change_or_has_action &&
10602 change->has_event[trigger_event] &&
10603 change->trigger_side & trigger_side &&
10604 change->trigger_player & trigger_player &&
10605 change->trigger_page & trigger_page_bits &&
10606 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10608 change->actual_trigger_element = trigger_element;
10609 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10610 change->actual_trigger_player_bits = trigger_player;
10611 change->actual_trigger_side = trigger_side;
10612 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10613 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10615 if ((change->can_change && !change_done) || change->has_action)
10619 SCAN_PLAYFIELD(x, y)
10621 if (Feld[x][y] == element)
10623 if (change->can_change && !change_done)
10625 /* if element already changed in this frame, not only prevent
10626 another element change (checked in ChangeElement()), but
10627 also prevent additional element actions for this element */
10629 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10630 !level.use_action_after_change_bug)
10633 ChangeDelay[x][y] = 1;
10634 ChangeEvent[x][y] = trigger_event;
10636 HandleElementChange(x, y, p);
10638 else if (change->has_action)
10640 /* if element already changed in this frame, not only prevent
10641 another element change (checked in ChangeElement()), but
10642 also prevent additional element actions for this element */
10644 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10645 !level.use_action_after_change_bug)
10648 ExecuteCustomElementAction(x, y, element, p);
10649 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10654 if (change->can_change)
10656 change_done = TRUE;
10657 change_done_any = TRUE;
10664 RECURSION_LOOP_DETECTION_END();
10666 return change_done_any;
10669 static boolean CheckElementChangeExt(int x, int y,
10671 int trigger_element,
10673 int trigger_player,
10676 boolean change_done = FALSE;
10679 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10680 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10683 if (Feld[x][y] == EL_BLOCKED)
10685 Blocked2Moving(x, y, &x, &y);
10686 element = Feld[x][y];
10689 /* check if element has already changed or is about to change after moving */
10690 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10691 Feld[x][y] != element) ||
10693 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10694 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10695 ChangePage[x][y] != -1)))
10698 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10700 for (p = 0; p < element_info[element].num_change_pages; p++)
10702 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10704 /* check trigger element for all events where the element that is checked
10705 for changing interacts with a directly adjacent element -- this is
10706 different to element changes that affect other elements to change on the
10707 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10708 boolean check_trigger_element =
10709 (trigger_event == CE_TOUCHING_X ||
10710 trigger_event == CE_HITTING_X ||
10711 trigger_event == CE_HIT_BY_X ||
10712 trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10714 if (change->can_change_or_has_action &&
10715 change->has_event[trigger_event] &&
10716 change->trigger_side & trigger_side &&
10717 change->trigger_player & trigger_player &&
10718 (!check_trigger_element ||
10719 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10721 change->actual_trigger_element = trigger_element;
10722 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10723 change->actual_trigger_player_bits = trigger_player;
10724 change->actual_trigger_side = trigger_side;
10725 change->actual_trigger_ce_value = CustomValue[x][y];
10726 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10728 /* special case: trigger element not at (x,y) position for some events */
10729 if (check_trigger_element)
10741 { 0, 0 }, { 0, 0 }, { 0, 0 },
10745 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10746 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10748 change->actual_trigger_ce_value = CustomValue[xx][yy];
10749 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10752 if (change->can_change && !change_done)
10754 ChangeDelay[x][y] = 1;
10755 ChangeEvent[x][y] = trigger_event;
10757 HandleElementChange(x, y, p);
10759 change_done = TRUE;
10761 else if (change->has_action)
10763 ExecuteCustomElementAction(x, y, element, p);
10764 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10769 RECURSION_LOOP_DETECTION_END();
10771 return change_done;
10774 static void PlayPlayerSound(struct PlayerInfo *player)
10776 int jx = player->jx, jy = player->jy;
10777 int sound_element = player->artwork_element;
10778 int last_action = player->last_action_waiting;
10779 int action = player->action_waiting;
10781 if (player->is_waiting)
10783 if (action != last_action)
10784 PlayLevelSoundElementAction(jx, jy, sound_element, action);
10786 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10790 if (action != last_action)
10791 StopSound(element_info[sound_element].sound[last_action]);
10793 if (last_action == ACTION_SLEEPING)
10794 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10798 static void PlayAllPlayersSound()
10802 for (i = 0; i < MAX_PLAYERS; i++)
10803 if (stored_player[i].active)
10804 PlayPlayerSound(&stored_player[i]);
10807 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10809 boolean last_waiting = player->is_waiting;
10810 int move_dir = player->MovDir;
10812 player->dir_waiting = move_dir;
10813 player->last_action_waiting = player->action_waiting;
10817 if (!last_waiting) /* not waiting -> waiting */
10819 player->is_waiting = TRUE;
10821 player->frame_counter_bored =
10823 game.player_boring_delay_fixed +
10824 GetSimpleRandom(game.player_boring_delay_random);
10825 player->frame_counter_sleeping =
10827 game.player_sleeping_delay_fixed +
10828 GetSimpleRandom(game.player_sleeping_delay_random);
10830 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10833 if (game.player_sleeping_delay_fixed +
10834 game.player_sleeping_delay_random > 0 &&
10835 player->anim_delay_counter == 0 &&
10836 player->post_delay_counter == 0 &&
10837 FrameCounter >= player->frame_counter_sleeping)
10838 player->is_sleeping = TRUE;
10839 else if (game.player_boring_delay_fixed +
10840 game.player_boring_delay_random > 0 &&
10841 FrameCounter >= player->frame_counter_bored)
10842 player->is_bored = TRUE;
10844 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10845 player->is_bored ? ACTION_BORING :
10848 if (player->is_sleeping && player->use_murphy)
10850 /* special case for sleeping Murphy when leaning against non-free tile */
10852 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10853 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10854 !IS_MOVING(player->jx - 1, player->jy)))
10855 move_dir = MV_LEFT;
10856 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10857 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10858 !IS_MOVING(player->jx + 1, player->jy)))
10859 move_dir = MV_RIGHT;
10861 player->is_sleeping = FALSE;
10863 player->dir_waiting = move_dir;
10866 if (player->is_sleeping)
10868 if (player->num_special_action_sleeping > 0)
10870 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10872 int last_special_action = player->special_action_sleeping;
10873 int num_special_action = player->num_special_action_sleeping;
10874 int special_action =
10875 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10876 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10877 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10878 last_special_action + 1 : ACTION_SLEEPING);
10879 int special_graphic =
10880 el_act_dir2img(player->artwork_element, special_action, move_dir);
10882 player->anim_delay_counter =
10883 graphic_info[special_graphic].anim_delay_fixed +
10884 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10885 player->post_delay_counter =
10886 graphic_info[special_graphic].post_delay_fixed +
10887 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10889 player->special_action_sleeping = special_action;
10892 if (player->anim_delay_counter > 0)
10894 player->action_waiting = player->special_action_sleeping;
10895 player->anim_delay_counter--;
10897 else if (player->post_delay_counter > 0)
10899 player->post_delay_counter--;
10903 else if (player->is_bored)
10905 if (player->num_special_action_bored > 0)
10907 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10909 int special_action =
10910 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10911 int special_graphic =
10912 el_act_dir2img(player->artwork_element, special_action, move_dir);
10914 player->anim_delay_counter =
10915 graphic_info[special_graphic].anim_delay_fixed +
10916 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10917 player->post_delay_counter =
10918 graphic_info[special_graphic].post_delay_fixed +
10919 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10921 player->special_action_bored = special_action;
10924 if (player->anim_delay_counter > 0)
10926 player->action_waiting = player->special_action_bored;
10927 player->anim_delay_counter--;
10929 else if (player->post_delay_counter > 0)
10931 player->post_delay_counter--;
10936 else if (last_waiting) /* waiting -> not waiting */
10938 player->is_waiting = FALSE;
10939 player->is_bored = FALSE;
10940 player->is_sleeping = FALSE;
10942 player->frame_counter_bored = -1;
10943 player->frame_counter_sleeping = -1;
10945 player->anim_delay_counter = 0;
10946 player->post_delay_counter = 0;
10948 player->dir_waiting = player->MovDir;
10949 player->action_waiting = ACTION_DEFAULT;
10951 player->special_action_bored = ACTION_DEFAULT;
10952 player->special_action_sleeping = ACTION_DEFAULT;
10956 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10958 if ((!player->is_moving && player->was_moving) ||
10959 (player->MovPos == 0 && player->was_moving) ||
10960 (player->is_snapping && !player->was_snapping) ||
10961 (player->is_dropping && !player->was_dropping))
10963 if (!CheckSaveEngineSnapshotToList())
10966 player->was_moving = FALSE;
10967 player->was_snapping = TRUE;
10968 player->was_dropping = TRUE;
10972 if (player->is_moving)
10973 player->was_moving = TRUE;
10975 if (!player->is_snapping)
10976 player->was_snapping = FALSE;
10978 if (!player->is_dropping)
10979 player->was_dropping = FALSE;
10983 static void CheckSingleStepMode(struct PlayerInfo *player)
10985 if (tape.single_step && tape.recording && !tape.pausing)
10987 /* as it is called "single step mode", just return to pause mode when the
10988 player stopped moving after one tile (or never starts moving at all) */
10989 if (!player->is_moving &&
10990 !player->is_pushing &&
10991 !player->is_dropping_pressed)
10993 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10994 SnapField(player, 0, 0); /* stop snapping */
10998 CheckSaveEngineSnapshot(player);
11001 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11003 int left = player_action & JOY_LEFT;
11004 int right = player_action & JOY_RIGHT;
11005 int up = player_action & JOY_UP;
11006 int down = player_action & JOY_DOWN;
11007 int button1 = player_action & JOY_BUTTON_1;
11008 int button2 = player_action & JOY_BUTTON_2;
11009 int dx = (left ? -1 : right ? 1 : 0);
11010 int dy = (up ? -1 : down ? 1 : 0);
11012 if (!player->active || tape.pausing)
11018 SnapField(player, dx, dy);
11022 DropElement(player);
11024 MovePlayer(player, dx, dy);
11027 CheckSingleStepMode(player);
11029 SetPlayerWaiting(player, FALSE);
11031 return player_action;
11035 /* no actions for this player (no input at player's configured device) */
11037 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11038 SnapField(player, 0, 0);
11039 CheckGravityMovementWhenNotMoving(player);
11041 if (player->MovPos == 0)
11042 SetPlayerWaiting(player, TRUE);
11044 if (player->MovPos == 0) /* needed for tape.playing */
11045 player->is_moving = FALSE;
11047 player->is_dropping = FALSE;
11048 player->is_dropping_pressed = FALSE;
11049 player->drop_pressed_delay = 0;
11051 CheckSingleStepMode(player);
11057 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11060 if (!tape.use_mouse)
11063 mouse_action->lx = tape_action[TAPE_ACTION_LX];
11064 mouse_action->ly = tape_action[TAPE_ACTION_LY];
11065 mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11068 static void SetTapeActionFromMouseAction(byte *tape_action,
11069 struct MouseActionInfo *mouse_action)
11071 if (!tape.use_mouse)
11074 tape_action[TAPE_ACTION_LX] = mouse_action->lx;
11075 tape_action[TAPE_ACTION_LY] = mouse_action->ly;
11076 tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11079 static void CheckLevelTime()
11083 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11084 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11086 if (level.native_em_level->lev->home == 0) /* all players at home */
11088 PlayerWins(local_player);
11090 AllPlayersGone = TRUE;
11092 level.native_em_level->lev->home = -1;
11095 if (level.native_em_level->ply[0]->alive == 0 &&
11096 level.native_em_level->ply[1]->alive == 0 &&
11097 level.native_em_level->ply[2]->alive == 0 &&
11098 level.native_em_level->ply[3]->alive == 0) /* all dead */
11099 AllPlayersGone = TRUE;
11101 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11103 if (game_sp.LevelSolved &&
11104 !game_sp.GameOver) /* game won */
11106 PlayerWins(local_player);
11108 game_sp.GameOver = TRUE;
11110 AllPlayersGone = TRUE;
11113 if (game_sp.GameOver) /* game lost */
11114 AllPlayersGone = TRUE;
11116 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11118 if (game_mm.level_solved &&
11119 !game_mm.game_over) /* game won */
11121 PlayerWins(local_player);
11123 game_mm.game_over = TRUE;
11125 AllPlayersGone = TRUE;
11128 if (game_mm.game_over) /* game lost */
11129 AllPlayersGone = TRUE;
11132 if (TimeFrames >= FRAMES_PER_SECOND)
11137 for (i = 0; i < MAX_PLAYERS; i++)
11139 struct PlayerInfo *player = &stored_player[i];
11141 if (SHIELD_ON(player))
11143 player->shield_normal_time_left--;
11145 if (player->shield_deadly_time_left > 0)
11146 player->shield_deadly_time_left--;
11150 if (!local_player->LevelSolved && !level.use_step_counter)
11158 if (TimeLeft <= 10 && setup.time_limit)
11159 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11161 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11162 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11164 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11166 if (!TimeLeft && setup.time_limit)
11168 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11169 level.native_em_level->lev->killed_out_of_time = TRUE;
11171 for (i = 0; i < MAX_PLAYERS; i++)
11172 KillPlayer(&stored_player[i]);
11175 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11177 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11180 level.native_em_level->lev->time =
11181 (game.no_time_limit ? TimePlayed : TimeLeft);
11184 if (tape.recording || tape.playing)
11185 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11188 if (tape.recording || tape.playing)
11189 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11191 UpdateAndDisplayGameControlValues();
11194 void AdvanceFrameAndPlayerCounters(int player_nr)
11198 /* advance frame counters (global frame counter and time frame counter) */
11202 /* advance player counters (counters for move delay, move animation etc.) */
11203 for (i = 0; i < MAX_PLAYERS; i++)
11205 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11206 int move_delay_value = stored_player[i].move_delay_value;
11207 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11209 if (!advance_player_counters) /* not all players may be affected */
11212 if (move_frames == 0) /* less than one move per game frame */
11214 int stepsize = TILEX / move_delay_value;
11215 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11216 int count = (stored_player[i].is_moving ?
11217 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11219 if (count % delay == 0)
11223 stored_player[i].Frame += move_frames;
11225 if (stored_player[i].MovPos != 0)
11226 stored_player[i].StepFrame += move_frames;
11228 if (stored_player[i].move_delay > 0)
11229 stored_player[i].move_delay--;
11231 /* due to bugs in previous versions, counter must count up, not down */
11232 if (stored_player[i].push_delay != -1)
11233 stored_player[i].push_delay++;
11235 if (stored_player[i].drop_delay > 0)
11236 stored_player[i].drop_delay--;
11238 if (stored_player[i].is_dropping_pressed)
11239 stored_player[i].drop_pressed_delay++;
11243 void StartGameActions(boolean init_network_game, boolean record_tape,
11246 unsigned int new_random_seed = InitRND(random_seed);
11249 TapeStartRecording(new_random_seed);
11251 #if defined(NETWORK_AVALIABLE)
11252 if (init_network_game)
11254 SendToServer_StartPlaying();
11263 void GameActionsExt()
11266 static unsigned int game_frame_delay = 0;
11268 unsigned int game_frame_delay_value;
11269 byte *recorded_player_action;
11270 byte summarized_player_action = 0;
11271 byte tape_action[MAX_PLAYERS];
11274 /* detect endless loops, caused by custom element programming */
11275 if (recursion_loop_detected && recursion_loop_depth == 0)
11277 char *message = getStringCat3("Internal Error! Element ",
11278 EL_NAME(recursion_loop_element),
11279 " caused endless loop! Quit the game?");
11281 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11282 EL_NAME(recursion_loop_element));
11284 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11286 recursion_loop_detected = FALSE; /* if game should be continued */
11293 if (game.restart_level)
11294 StartGameActions(options.network, setup.autorecord, level.random_seed);
11296 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11297 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11299 if (level.native_em_level->lev->home == 0) /* all players at home */
11301 PlayerWins(local_player);
11303 AllPlayersGone = TRUE;
11305 level.native_em_level->lev->home = -1;
11308 if (level.native_em_level->ply[0]->alive == 0 &&
11309 level.native_em_level->ply[1]->alive == 0 &&
11310 level.native_em_level->ply[2]->alive == 0 &&
11311 level.native_em_level->ply[3]->alive == 0) /* all dead */
11312 AllPlayersGone = TRUE;
11314 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11316 if (game_sp.LevelSolved &&
11317 !game_sp.GameOver) /* game won */
11319 PlayerWins(local_player);
11321 game_sp.GameOver = TRUE;
11323 AllPlayersGone = TRUE;
11326 if (game_sp.GameOver) /* game lost */
11327 AllPlayersGone = TRUE;
11329 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11331 if (game_mm.level_solved &&
11332 !game_mm.game_over) /* game won */
11334 PlayerWins(local_player);
11336 game_mm.game_over = TRUE;
11338 AllPlayersGone = TRUE;
11341 if (game_mm.game_over) /* game lost */
11342 AllPlayersGone = TRUE;
11345 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11348 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11351 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11354 game_frame_delay_value =
11355 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11357 if (tape.playing && tape.warp_forward && !tape.pausing)
11358 game_frame_delay_value = 0;
11360 SetVideoFrameDelay(game_frame_delay_value);
11364 /* ---------- main game synchronization point ---------- */
11366 int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11368 printf("::: skip == %d\n", skip);
11371 /* ---------- main game synchronization point ---------- */
11373 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11377 if (network_playing && !network_player_action_received)
11379 /* try to get network player actions in time */
11381 #if defined(NETWORK_AVALIABLE)
11382 /* last chance to get network player actions without main loop delay */
11383 HandleNetworking();
11386 /* game was quit by network peer */
11387 if (game_status != GAME_MODE_PLAYING)
11390 if (!network_player_action_received)
11391 return; /* failed to get network player actions in time */
11393 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11399 /* at this point we know that we really continue executing the game */
11401 network_player_action_received = FALSE;
11403 /* when playing tape, read previously recorded player input from tape data */
11404 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11406 local_player->effective_mouse_action = local_player->mouse_action;
11408 if (recorded_player_action != NULL)
11409 SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11410 recorded_player_action);
11412 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11416 if (tape.set_centered_player)
11418 game.centered_player_nr_next = tape.centered_player_nr_next;
11419 game.set_centered_player = TRUE;
11422 for (i = 0; i < MAX_PLAYERS; i++)
11424 summarized_player_action |= stored_player[i].action;
11426 if (!network_playing && (game.team_mode || tape.playing))
11427 stored_player[i].effective_action = stored_player[i].action;
11430 #if defined(NETWORK_AVALIABLE)
11431 if (network_playing)
11432 SendToServer_MovePlayer(summarized_player_action);
11435 // summarize all actions at local players mapped input device position
11436 // (this allows using different input devices in single player mode)
11437 if (!options.network && !game.team_mode)
11438 stored_player[map_player_action[local_player->index_nr]].effective_action =
11439 summarized_player_action;
11441 if (tape.recording &&
11443 setup.input_on_focus &&
11444 game.centered_player_nr != -1)
11446 for (i = 0; i < MAX_PLAYERS; i++)
11447 stored_player[i].effective_action =
11448 (i == game.centered_player_nr ? summarized_player_action : 0);
11451 if (recorded_player_action != NULL)
11452 for (i = 0; i < MAX_PLAYERS; i++)
11453 stored_player[i].effective_action = recorded_player_action[i];
11455 for (i = 0; i < MAX_PLAYERS; i++)
11457 tape_action[i] = stored_player[i].effective_action;
11459 /* (this may happen in the RND game engine if a player was not present on
11460 the playfield on level start, but appeared later from a custom element */
11461 if (setup.team_mode &&
11464 !tape.player_participates[i])
11465 tape.player_participates[i] = TRUE;
11468 SetTapeActionFromMouseAction(tape_action,
11469 &local_player->effective_mouse_action);
11471 /* only record actions from input devices, but not programmed actions */
11472 if (tape.recording)
11473 TapeRecordAction(tape_action);
11475 #if USE_NEW_PLAYER_ASSIGNMENTS
11476 // !!! also map player actions in single player mode !!!
11477 // if (game.team_mode)
11480 byte mapped_action[MAX_PLAYERS];
11482 #if DEBUG_PLAYER_ACTIONS
11484 for (i = 0; i < MAX_PLAYERS; i++)
11485 printf(" %d, ", stored_player[i].effective_action);
11488 for (i = 0; i < MAX_PLAYERS; i++)
11489 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11491 for (i = 0; i < MAX_PLAYERS; i++)
11492 stored_player[i].effective_action = mapped_action[i];
11494 #if DEBUG_PLAYER_ACTIONS
11496 for (i = 0; i < MAX_PLAYERS; i++)
11497 printf(" %d, ", stored_player[i].effective_action);
11501 #if DEBUG_PLAYER_ACTIONS
11505 for (i = 0; i < MAX_PLAYERS; i++)
11506 printf(" %d, ", stored_player[i].effective_action);
11512 for (i = 0; i < MAX_PLAYERS; i++)
11514 // allow engine snapshot in case of changed movement attempt
11515 if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11516 (stored_player[i].effective_action & KEY_MOTION))
11517 game.snapshot.changed_action = TRUE;
11519 // allow engine snapshot in case of snapping/dropping attempt
11520 if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11521 (stored_player[i].effective_action & KEY_BUTTON) != 0)
11522 game.snapshot.changed_action = TRUE;
11524 game.snapshot.last_action[i] = stored_player[i].effective_action;
11527 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11529 GameActions_EM_Main();
11531 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11533 GameActions_SP_Main();
11535 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11537 GameActions_MM_Main();
11541 GameActions_RND_Main();
11544 BlitScreenToBitmap(backbuffer);
11548 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11550 if (global.show_frames_per_second)
11552 static unsigned int fps_counter = 0;
11553 static int fps_frames = 0;
11554 unsigned int fps_delay_ms = Counter() - fps_counter;
11558 if (fps_delay_ms >= 500) /* calculate FPS every 0.5 seconds */
11560 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11563 fps_counter = Counter();
11565 /* always draw FPS to screen after FPS value was updated */
11566 redraw_mask |= REDRAW_FPS;
11569 /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11570 if (GetDrawDeactivationMask() == REDRAW_NONE)
11571 redraw_mask |= REDRAW_FPS;
11575 static void GameActions_CheckSaveEngineSnapshot()
11577 if (!game.snapshot.save_snapshot)
11580 // clear flag for saving snapshot _before_ saving snapshot
11581 game.snapshot.save_snapshot = FALSE;
11583 SaveEngineSnapshotToList();
11590 GameActions_CheckSaveEngineSnapshot();
11593 void GameActions_EM_Main()
11595 byte effective_action[MAX_PLAYERS];
11596 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11599 for (i = 0; i < MAX_PLAYERS; i++)
11600 effective_action[i] = stored_player[i].effective_action;
11602 GameActions_EM(effective_action, warp_mode);
11605 void GameActions_SP_Main()
11607 byte effective_action[MAX_PLAYERS];
11608 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11611 for (i = 0; i < MAX_PLAYERS; i++)
11612 effective_action[i] = stored_player[i].effective_action;
11614 GameActions_SP(effective_action, warp_mode);
11616 for (i = 0; i < MAX_PLAYERS; i++)
11618 if (stored_player[i].force_dropping)
11619 stored_player[i].action |= KEY_BUTTON_DROP;
11621 stored_player[i].force_dropping = FALSE;
11625 void GameActions_MM_Main()
11627 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11629 GameActions_MM(local_player->effective_mouse_action, warp_mode);
11632 void GameActions_RND_Main()
11637 void GameActions_RND()
11639 int magic_wall_x = 0, magic_wall_y = 0;
11640 int i, x, y, element, graphic, last_gfx_frame;
11642 InitPlayfieldScanModeVars();
11644 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11646 SCAN_PLAYFIELD(x, y)
11648 ChangeCount[x][y] = 0;
11649 ChangeEvent[x][y] = -1;
11653 if (game.set_centered_player)
11655 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11657 /* switching to "all players" only possible if all players fit to screen */
11658 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11660 game.centered_player_nr_next = game.centered_player_nr;
11661 game.set_centered_player = FALSE;
11664 /* do not switch focus to non-existing (or non-active) player */
11665 if (game.centered_player_nr_next >= 0 &&
11666 !stored_player[game.centered_player_nr_next].active)
11668 game.centered_player_nr_next = game.centered_player_nr;
11669 game.set_centered_player = FALSE;
11673 if (game.set_centered_player &&
11674 ScreenMovPos == 0) /* screen currently aligned at tile position */
11678 if (game.centered_player_nr_next == -1)
11680 setScreenCenteredToAllPlayers(&sx, &sy);
11684 sx = stored_player[game.centered_player_nr_next].jx;
11685 sy = stored_player[game.centered_player_nr_next].jy;
11688 game.centered_player_nr = game.centered_player_nr_next;
11689 game.set_centered_player = FALSE;
11691 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11692 DrawGameDoorValues();
11695 for (i = 0; i < MAX_PLAYERS; i++)
11697 int actual_player_action = stored_player[i].effective_action;
11700 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11701 - rnd_equinox_tetrachloride 048
11702 - rnd_equinox_tetrachloride_ii 096
11703 - rnd_emanuel_schmieg 002
11704 - doctor_sloan_ww 001, 020
11706 if (stored_player[i].MovPos == 0)
11707 CheckGravityMovement(&stored_player[i]);
11710 /* overwrite programmed action with tape action */
11711 if (stored_player[i].programmed_action)
11712 actual_player_action = stored_player[i].programmed_action;
11714 PlayerActions(&stored_player[i], actual_player_action);
11716 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11719 ScrollScreen(NULL, SCROLL_GO_ON);
11721 /* for backwards compatibility, the following code emulates a fixed bug that
11722 occured when pushing elements (causing elements that just made their last
11723 pushing step to already (if possible) make their first falling step in the
11724 same game frame, which is bad); this code is also needed to use the famous
11725 "spring push bug" which is used in older levels and might be wanted to be
11726 used also in newer levels, but in this case the buggy pushing code is only
11727 affecting the "spring" element and no other elements */
11729 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11731 for (i = 0; i < MAX_PLAYERS; i++)
11733 struct PlayerInfo *player = &stored_player[i];
11734 int x = player->jx;
11735 int y = player->jy;
11737 if (player->active && player->is_pushing && player->is_moving &&
11739 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11740 Feld[x][y] == EL_SPRING))
11742 ContinueMoving(x, y);
11744 /* continue moving after pushing (this is actually a bug) */
11745 if (!IS_MOVING(x, y))
11746 Stop[x][y] = FALSE;
11751 SCAN_PLAYFIELD(x, y)
11753 ChangeCount[x][y] = 0;
11754 ChangeEvent[x][y] = -1;
11756 /* this must be handled before main playfield loop */
11757 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11760 if (MovDelay[x][y] <= 0)
11764 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11767 if (MovDelay[x][y] <= 0)
11770 TEST_DrawLevelField(x, y);
11772 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11777 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11779 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11780 printf("GameActions(): This should never happen!\n");
11782 ChangePage[x][y] = -1;
11786 Stop[x][y] = FALSE;
11787 if (WasJustMoving[x][y] > 0)
11788 WasJustMoving[x][y]--;
11789 if (WasJustFalling[x][y] > 0)
11790 WasJustFalling[x][y]--;
11791 if (CheckCollision[x][y] > 0)
11792 CheckCollision[x][y]--;
11793 if (CheckImpact[x][y] > 0)
11794 CheckImpact[x][y]--;
11798 /* reset finished pushing action (not done in ContinueMoving() to allow
11799 continuous pushing animation for elements with zero push delay) */
11800 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11802 ResetGfxAnimation(x, y);
11803 TEST_DrawLevelField(x, y);
11807 if (IS_BLOCKED(x, y))
11811 Blocked2Moving(x, y, &oldx, &oldy);
11812 if (!IS_MOVING(oldx, oldy))
11814 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11815 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11816 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11817 printf("GameActions(): This should never happen!\n");
11823 SCAN_PLAYFIELD(x, y)
11825 element = Feld[x][y];
11826 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11827 last_gfx_frame = GfxFrame[x][y];
11829 ResetGfxFrame(x, y);
11831 if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11832 DrawLevelGraphicAnimation(x, y, graphic);
11834 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11835 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11836 ResetRandomAnimationValue(x, y);
11838 SetRandomAnimationValue(x, y);
11840 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11842 if (IS_INACTIVE(element))
11844 if (IS_ANIMATED(graphic))
11845 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11850 /* this may take place after moving, so 'element' may have changed */
11851 if (IS_CHANGING(x, y) &&
11852 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11854 int page = element_info[element].event_page_nr[CE_DELAY];
11856 HandleElementChange(x, y, page);
11858 element = Feld[x][y];
11859 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11862 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11866 element = Feld[x][y];
11867 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11869 if (IS_ANIMATED(graphic) &&
11870 !IS_MOVING(x, y) &&
11872 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11874 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11875 TEST_DrawTwinkleOnField(x, y);
11877 else if (element == EL_ACID)
11880 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11882 else if ((element == EL_EXIT_OPEN ||
11883 element == EL_EM_EXIT_OPEN ||
11884 element == EL_SP_EXIT_OPEN ||
11885 element == EL_STEEL_EXIT_OPEN ||
11886 element == EL_EM_STEEL_EXIT_OPEN ||
11887 element == EL_SP_TERMINAL ||
11888 element == EL_SP_TERMINAL_ACTIVE ||
11889 element == EL_EXTRA_TIME ||
11890 element == EL_SHIELD_NORMAL ||
11891 element == EL_SHIELD_DEADLY) &&
11892 IS_ANIMATED(graphic))
11893 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11894 else if (IS_MOVING(x, y))
11895 ContinueMoving(x, y);
11896 else if (IS_ACTIVE_BOMB(element))
11897 CheckDynamite(x, y);
11898 else if (element == EL_AMOEBA_GROWING)
11899 AmoebeWaechst(x, y);
11900 else if (element == EL_AMOEBA_SHRINKING)
11901 AmoebaDisappearing(x, y);
11903 #if !USE_NEW_AMOEBA_CODE
11904 else if (IS_AMOEBALIVE(element))
11905 AmoebeAbleger(x, y);
11908 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11910 else if (element == EL_EXIT_CLOSED)
11912 else if (element == EL_EM_EXIT_CLOSED)
11914 else if (element == EL_STEEL_EXIT_CLOSED)
11915 CheckExitSteel(x, y);
11916 else if (element == EL_EM_STEEL_EXIT_CLOSED)
11917 CheckExitSteelEM(x, y);
11918 else if (element == EL_SP_EXIT_CLOSED)
11920 else if (element == EL_EXPANDABLE_WALL_GROWING ||
11921 element == EL_EXPANDABLE_STEELWALL_GROWING)
11922 MauerWaechst(x, y);
11923 else if (element == EL_EXPANDABLE_WALL ||
11924 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11925 element == EL_EXPANDABLE_WALL_VERTICAL ||
11926 element == EL_EXPANDABLE_WALL_ANY ||
11927 element == EL_BD_EXPANDABLE_WALL)
11928 MauerAbleger(x, y);
11929 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11930 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11931 element == EL_EXPANDABLE_STEELWALL_ANY)
11932 MauerAblegerStahl(x, y);
11933 else if (element == EL_FLAMES)
11934 CheckForDragon(x, y);
11935 else if (element == EL_EXPLOSION)
11936 ; /* drawing of correct explosion animation is handled separately */
11937 else if (element == EL_ELEMENT_SNAPPING ||
11938 element == EL_DIAGONAL_SHRINKING ||
11939 element == EL_DIAGONAL_GROWING)
11941 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11945 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11946 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11948 if (IS_BELT_ACTIVE(element))
11949 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11951 if (game.magic_wall_active)
11953 int jx = local_player->jx, jy = local_player->jy;
11955 /* play the element sound at the position nearest to the player */
11956 if ((element == EL_MAGIC_WALL_FULL ||
11957 element == EL_MAGIC_WALL_ACTIVE ||
11958 element == EL_MAGIC_WALL_EMPTYING ||
11959 element == EL_BD_MAGIC_WALL_FULL ||
11960 element == EL_BD_MAGIC_WALL_ACTIVE ||
11961 element == EL_BD_MAGIC_WALL_EMPTYING ||
11962 element == EL_DC_MAGIC_WALL_FULL ||
11963 element == EL_DC_MAGIC_WALL_ACTIVE ||
11964 element == EL_DC_MAGIC_WALL_EMPTYING) &&
11965 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11973 #if USE_NEW_AMOEBA_CODE
11974 /* new experimental amoeba growth stuff */
11975 if (!(FrameCounter % 8))
11977 static unsigned int random = 1684108901;
11979 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11981 x = RND(lev_fieldx);
11982 y = RND(lev_fieldy);
11983 element = Feld[x][y];
11985 if (!IS_PLAYER(x,y) &&
11986 (element == EL_EMPTY ||
11987 CAN_GROW_INTO(element) ||
11988 element == EL_QUICKSAND_EMPTY ||
11989 element == EL_QUICKSAND_FAST_EMPTY ||
11990 element == EL_ACID_SPLASH_LEFT ||
11991 element == EL_ACID_SPLASH_RIGHT))
11993 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11994 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11995 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11996 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11997 Feld[x][y] = EL_AMOEBA_DROP;
12000 random = random * 129 + 1;
12005 game.explosions_delayed = FALSE;
12007 SCAN_PLAYFIELD(x, y)
12009 element = Feld[x][y];
12011 if (ExplodeField[x][y])
12012 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12013 else if (element == EL_EXPLOSION)
12014 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12016 ExplodeField[x][y] = EX_TYPE_NONE;
12019 game.explosions_delayed = TRUE;
12021 if (game.magic_wall_active)
12023 if (!(game.magic_wall_time_left % 4))
12025 int element = Feld[magic_wall_x][magic_wall_y];
12027 if (element == EL_BD_MAGIC_WALL_FULL ||
12028 element == EL_BD_MAGIC_WALL_ACTIVE ||
12029 element == EL_BD_MAGIC_WALL_EMPTYING)
12030 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12031 else if (element == EL_DC_MAGIC_WALL_FULL ||
12032 element == EL_DC_MAGIC_WALL_ACTIVE ||
12033 element == EL_DC_MAGIC_WALL_EMPTYING)
12034 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12036 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12039 if (game.magic_wall_time_left > 0)
12041 game.magic_wall_time_left--;
12043 if (!game.magic_wall_time_left)
12045 SCAN_PLAYFIELD(x, y)
12047 element = Feld[x][y];
12049 if (element == EL_MAGIC_WALL_ACTIVE ||
12050 element == EL_MAGIC_WALL_FULL)
12052 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12053 TEST_DrawLevelField(x, y);
12055 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12056 element == EL_BD_MAGIC_WALL_FULL)
12058 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12059 TEST_DrawLevelField(x, y);
12061 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12062 element == EL_DC_MAGIC_WALL_FULL)
12064 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12065 TEST_DrawLevelField(x, y);
12069 game.magic_wall_active = FALSE;
12074 if (game.light_time_left > 0)
12076 game.light_time_left--;
12078 if (game.light_time_left == 0)
12079 RedrawAllLightSwitchesAndInvisibleElements();
12082 if (game.timegate_time_left > 0)
12084 game.timegate_time_left--;
12086 if (game.timegate_time_left == 0)
12087 CloseAllOpenTimegates();
12090 if (game.lenses_time_left > 0)
12092 game.lenses_time_left--;
12094 if (game.lenses_time_left == 0)
12095 RedrawAllInvisibleElementsForLenses();
12098 if (game.magnify_time_left > 0)
12100 game.magnify_time_left--;
12102 if (game.magnify_time_left == 0)
12103 RedrawAllInvisibleElementsForMagnifier();
12106 for (i = 0; i < MAX_PLAYERS; i++)
12108 struct PlayerInfo *player = &stored_player[i];
12110 if (SHIELD_ON(player))
12112 if (player->shield_deadly_time_left)
12113 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12114 else if (player->shield_normal_time_left)
12115 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12119 #if USE_DELAYED_GFX_REDRAW
12120 SCAN_PLAYFIELD(x, y)
12122 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12124 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12125 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12127 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12128 DrawLevelField(x, y);
12130 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12131 DrawLevelFieldCrumbled(x, y);
12133 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12134 DrawLevelFieldCrumbledNeighbours(x, y);
12136 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12137 DrawTwinkleOnField(x, y);
12140 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12145 PlayAllPlayersSound();
12147 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12149 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12151 local_player->show_envelope = 0;
12154 /* use random number generator in every frame to make it less predictable */
12155 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12159 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12161 int min_x = x, min_y = y, max_x = x, max_y = y;
12164 for (i = 0; i < MAX_PLAYERS; i++)
12166 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12168 if (!stored_player[i].active || &stored_player[i] == player)
12171 min_x = MIN(min_x, jx);
12172 min_y = MIN(min_y, jy);
12173 max_x = MAX(max_x, jx);
12174 max_y = MAX(max_y, jy);
12177 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12180 static boolean AllPlayersInVisibleScreen()
12184 for (i = 0; i < MAX_PLAYERS; i++)
12186 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12188 if (!stored_player[i].active)
12191 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12198 void ScrollLevel(int dx, int dy)
12200 int scroll_offset = 2 * TILEX_VAR;
12203 BlitBitmap(drawto_field, drawto_field,
12204 FX + TILEX_VAR * (dx == -1) - scroll_offset,
12205 FY + TILEY_VAR * (dy == -1) - scroll_offset,
12206 SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12207 SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12208 FX + TILEX_VAR * (dx == 1) - scroll_offset,
12209 FY + TILEY_VAR * (dy == 1) - scroll_offset);
12213 x = (dx == 1 ? BX1 : BX2);
12214 for (y = BY1; y <= BY2; y++)
12215 DrawScreenField(x, y);
12220 y = (dy == 1 ? BY1 : BY2);
12221 for (x = BX1; x <= BX2; x++)
12222 DrawScreenField(x, y);
12225 redraw_mask |= REDRAW_FIELD;
12228 static boolean canFallDown(struct PlayerInfo *player)
12230 int jx = player->jx, jy = player->jy;
12232 return (IN_LEV_FIELD(jx, jy + 1) &&
12233 (IS_FREE(jx, jy + 1) ||
12234 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12235 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12236 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12239 static boolean canPassField(int x, int y, int move_dir)
12241 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12242 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12243 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12244 int nextx = x + dx;
12245 int nexty = y + dy;
12246 int element = Feld[x][y];
12248 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12249 !CAN_MOVE(element) &&
12250 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12251 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12252 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12255 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12257 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12258 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12259 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12263 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12264 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12265 (IS_DIGGABLE(Feld[newx][newy]) ||
12266 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12267 canPassField(newx, newy, move_dir)));
12270 static void CheckGravityMovement(struct PlayerInfo *player)
12272 if (player->gravity && !player->programmed_action)
12274 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12275 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12276 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12277 int jx = player->jx, jy = player->jy;
12278 boolean player_is_moving_to_valid_field =
12279 (!player_is_snapping &&
12280 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12281 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12282 boolean player_can_fall_down = canFallDown(player);
12284 if (player_can_fall_down &&
12285 !player_is_moving_to_valid_field)
12286 player->programmed_action = MV_DOWN;
12290 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12292 return CheckGravityMovement(player);
12294 if (player->gravity && !player->programmed_action)
12296 int jx = player->jx, jy = player->jy;
12297 boolean field_under_player_is_free =
12298 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12299 boolean player_is_standing_on_valid_field =
12300 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12301 (IS_WALKABLE(Feld[jx][jy]) &&
12302 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12304 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12305 player->programmed_action = MV_DOWN;
12310 MovePlayerOneStep()
12311 -----------------------------------------------------------------------------
12312 dx, dy: direction (non-diagonal) to try to move the player to
12313 real_dx, real_dy: direction as read from input device (can be diagonal)
12316 boolean MovePlayerOneStep(struct PlayerInfo *player,
12317 int dx, int dy, int real_dx, int real_dy)
12319 int jx = player->jx, jy = player->jy;
12320 int new_jx = jx + dx, new_jy = jy + dy;
12322 boolean player_can_move = !player->cannot_move;
12324 if (!player->active || (!dx && !dy))
12325 return MP_NO_ACTION;
12327 player->MovDir = (dx < 0 ? MV_LEFT :
12328 dx > 0 ? MV_RIGHT :
12330 dy > 0 ? MV_DOWN : MV_NONE);
12332 if (!IN_LEV_FIELD(new_jx, new_jy))
12333 return MP_NO_ACTION;
12335 if (!player_can_move)
12337 if (player->MovPos == 0)
12339 player->is_moving = FALSE;
12340 player->is_digging = FALSE;
12341 player->is_collecting = FALSE;
12342 player->is_snapping = FALSE;
12343 player->is_pushing = FALSE;
12347 if (!options.network && game.centered_player_nr == -1 &&
12348 !AllPlayersInSight(player, new_jx, new_jy))
12349 return MP_NO_ACTION;
12351 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12352 if (can_move != MP_MOVING)
12355 /* check if DigField() has caused relocation of the player */
12356 if (player->jx != jx || player->jy != jy)
12357 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12359 StorePlayer[jx][jy] = 0;
12360 player->last_jx = jx;
12361 player->last_jy = jy;
12362 player->jx = new_jx;
12363 player->jy = new_jy;
12364 StorePlayer[new_jx][new_jy] = player->element_nr;
12366 if (player->move_delay_value_next != -1)
12368 player->move_delay_value = player->move_delay_value_next;
12369 player->move_delay_value_next = -1;
12373 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12375 player->step_counter++;
12377 PlayerVisit[jx][jy] = FrameCounter;
12379 player->is_moving = TRUE;
12382 /* should better be called in MovePlayer(), but this breaks some tapes */
12383 ScrollPlayer(player, SCROLL_INIT);
12389 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12391 int jx = player->jx, jy = player->jy;
12392 int old_jx = jx, old_jy = jy;
12393 int moved = MP_NO_ACTION;
12395 if (!player->active)
12400 if (player->MovPos == 0)
12402 player->is_moving = FALSE;
12403 player->is_digging = FALSE;
12404 player->is_collecting = FALSE;
12405 player->is_snapping = FALSE;
12406 player->is_pushing = FALSE;
12412 if (player->move_delay > 0)
12415 player->move_delay = -1; /* set to "uninitialized" value */
12417 /* store if player is automatically moved to next field */
12418 player->is_auto_moving = (player->programmed_action != MV_NONE);
12420 /* remove the last programmed player action */
12421 player->programmed_action = 0;
12423 if (player->MovPos)
12425 /* should only happen if pre-1.2 tape recordings are played */
12426 /* this is only for backward compatibility */
12428 int original_move_delay_value = player->move_delay_value;
12431 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12435 /* scroll remaining steps with finest movement resolution */
12436 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12438 while (player->MovPos)
12440 ScrollPlayer(player, SCROLL_GO_ON);
12441 ScrollScreen(NULL, SCROLL_GO_ON);
12443 AdvanceFrameAndPlayerCounters(player->index_nr);
12446 BackToFront_WithFrameDelay(0);
12449 player->move_delay_value = original_move_delay_value;
12452 player->is_active = FALSE;
12454 if (player->last_move_dir & MV_HORIZONTAL)
12456 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12457 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12461 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12462 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12465 if (!moved && !player->is_active)
12467 player->is_moving = FALSE;
12468 player->is_digging = FALSE;
12469 player->is_collecting = FALSE;
12470 player->is_snapping = FALSE;
12471 player->is_pushing = FALSE;
12477 if (moved & MP_MOVING && !ScreenMovPos &&
12478 (player->index_nr == game.centered_player_nr ||
12479 game.centered_player_nr == -1))
12481 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12482 int offset = game.scroll_delay_value;
12484 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12486 /* actual player has left the screen -- scroll in that direction */
12487 if (jx != old_jx) /* player has moved horizontally */
12488 scroll_x += (jx - old_jx);
12489 else /* player has moved vertically */
12490 scroll_y += (jy - old_jy);
12494 if (jx != old_jx) /* player has moved horizontally */
12496 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12497 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12498 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12500 /* don't scroll over playfield boundaries */
12501 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12502 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12504 /* don't scroll more than one field at a time */
12505 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12507 /* don't scroll against the player's moving direction */
12508 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12509 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12510 scroll_x = old_scroll_x;
12512 else /* player has moved vertically */
12514 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12515 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12516 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12518 /* don't scroll over playfield boundaries */
12519 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12520 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12522 /* don't scroll more than one field at a time */
12523 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12525 /* don't scroll against the player's moving direction */
12526 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12527 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12528 scroll_y = old_scroll_y;
12532 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12534 if (!options.network && game.centered_player_nr == -1 &&
12535 !AllPlayersInVisibleScreen())
12537 scroll_x = old_scroll_x;
12538 scroll_y = old_scroll_y;
12542 ScrollScreen(player, SCROLL_INIT);
12543 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12548 player->StepFrame = 0;
12550 if (moved & MP_MOVING)
12552 if (old_jx != jx && old_jy == jy)
12553 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12554 else if (old_jx == jx && old_jy != jy)
12555 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12557 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
12559 player->last_move_dir = player->MovDir;
12560 player->is_moving = TRUE;
12561 player->is_snapping = FALSE;
12562 player->is_switching = FALSE;
12563 player->is_dropping = FALSE;
12564 player->is_dropping_pressed = FALSE;
12565 player->drop_pressed_delay = 0;
12568 /* should better be called here than above, but this breaks some tapes */
12569 ScrollPlayer(player, SCROLL_INIT);
12574 CheckGravityMovementWhenNotMoving(player);
12576 player->is_moving = FALSE;
12578 /* at this point, the player is allowed to move, but cannot move right now
12579 (e.g. because of something blocking the way) -- ensure that the player
12580 is also allowed to move in the next frame (in old versions before 3.1.1,
12581 the player was forced to wait again for eight frames before next try) */
12583 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12584 player->move_delay = 0; /* allow direct movement in the next frame */
12587 if (player->move_delay == -1) /* not yet initialized by DigField() */
12588 player->move_delay = player->move_delay_value;
12590 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12592 TestIfPlayerTouchesBadThing(jx, jy);
12593 TestIfPlayerTouchesCustomElement(jx, jy);
12596 if (!player->active)
12597 RemovePlayer(player);
12602 void ScrollPlayer(struct PlayerInfo *player, int mode)
12604 int jx = player->jx, jy = player->jy;
12605 int last_jx = player->last_jx, last_jy = player->last_jy;
12606 int move_stepsize = TILEX / player->move_delay_value;
12608 if (!player->active)
12611 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12614 if (mode == SCROLL_INIT)
12616 player->actual_frame_counter = FrameCounter;
12617 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12619 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12620 Feld[last_jx][last_jy] == EL_EMPTY)
12622 int last_field_block_delay = 0; /* start with no blocking at all */
12623 int block_delay_adjustment = player->block_delay_adjustment;
12625 /* if player blocks last field, add delay for exactly one move */
12626 if (player->block_last_field)
12628 last_field_block_delay += player->move_delay_value;
12630 /* when blocking enabled, prevent moving up despite gravity */
12631 if (player->gravity && player->MovDir == MV_UP)
12632 block_delay_adjustment = -1;
12635 /* add block delay adjustment (also possible when not blocking) */
12636 last_field_block_delay += block_delay_adjustment;
12638 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12639 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12642 if (player->MovPos != 0) /* player has not yet reached destination */
12645 else if (!FrameReached(&player->actual_frame_counter, 1))
12648 if (player->MovPos != 0)
12650 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12651 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12653 /* before DrawPlayer() to draw correct player graphic for this case */
12654 if (player->MovPos == 0)
12655 CheckGravityMovement(player);
12658 if (player->MovPos == 0) /* player reached destination field */
12660 if (player->move_delay_reset_counter > 0)
12662 player->move_delay_reset_counter--;
12664 if (player->move_delay_reset_counter == 0)
12666 /* continue with normal speed after quickly moving through gate */
12667 HALVE_PLAYER_SPEED(player);
12669 /* be able to make the next move without delay */
12670 player->move_delay = 0;
12674 player->last_jx = jx;
12675 player->last_jy = jy;
12677 if (Feld[jx][jy] == EL_EXIT_OPEN ||
12678 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12679 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12680 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12681 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12682 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12683 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12684 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
12686 DrawPlayer(player); /* needed here only to cleanup last field */
12687 RemovePlayer(player);
12689 if (local_player->friends_still_needed == 0 ||
12690 IS_SP_ELEMENT(Feld[jx][jy]))
12691 PlayerWins(player);
12694 /* this breaks one level: "machine", level 000 */
12696 int move_direction = player->MovDir;
12697 int enter_side = MV_DIR_OPPOSITE(move_direction);
12698 int leave_side = move_direction;
12699 int old_jx = last_jx;
12700 int old_jy = last_jy;
12701 int old_element = Feld[old_jx][old_jy];
12702 int new_element = Feld[jx][jy];
12704 if (IS_CUSTOM_ELEMENT(old_element))
12705 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12707 player->index_bit, leave_side);
12709 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12710 CE_PLAYER_LEAVES_X,
12711 player->index_bit, leave_side);
12713 if (IS_CUSTOM_ELEMENT(new_element))
12714 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12715 player->index_bit, enter_side);
12717 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12718 CE_PLAYER_ENTERS_X,
12719 player->index_bit, enter_side);
12721 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12722 CE_MOVE_OF_X, move_direction);
12725 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12727 TestIfPlayerTouchesBadThing(jx, jy);
12728 TestIfPlayerTouchesCustomElement(jx, jy);
12730 /* needed because pushed element has not yet reached its destination,
12731 so it would trigger a change event at its previous field location */
12732 if (!player->is_pushing)
12733 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
12735 if (!player->active)
12736 RemovePlayer(player);
12739 if (!local_player->LevelSolved && level.use_step_counter)
12749 if (TimeLeft <= 10 && setup.time_limit)
12750 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12752 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12754 DisplayGameControlValues();
12756 if (!TimeLeft && setup.time_limit)
12757 for (i = 0; i < MAX_PLAYERS; i++)
12758 KillPlayer(&stored_player[i]);
12760 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12762 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12764 DisplayGameControlValues();
12768 if (tape.single_step && tape.recording && !tape.pausing &&
12769 !player->programmed_action)
12770 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12772 if (!player->programmed_action)
12773 CheckSaveEngineSnapshot(player);
12777 void ScrollScreen(struct PlayerInfo *player, int mode)
12779 static unsigned int screen_frame_counter = 0;
12781 if (mode == SCROLL_INIT)
12783 /* set scrolling step size according to actual player's moving speed */
12784 ScrollStepSize = TILEX / player->move_delay_value;
12786 screen_frame_counter = FrameCounter;
12787 ScreenMovDir = player->MovDir;
12788 ScreenMovPos = player->MovPos;
12789 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12792 else if (!FrameReached(&screen_frame_counter, 1))
12797 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12798 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12799 redraw_mask |= REDRAW_FIELD;
12802 ScreenMovDir = MV_NONE;
12805 void TestIfPlayerTouchesCustomElement(int x, int y)
12807 static int xy[4][2] =
12814 static int trigger_sides[4][2] =
12816 /* center side border side */
12817 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12818 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12819 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12820 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12822 static int touch_dir[4] =
12824 MV_LEFT | MV_RIGHT,
12829 int center_element = Feld[x][y]; /* should always be non-moving! */
12832 for (i = 0; i < NUM_DIRECTIONS; i++)
12834 int xx = x + xy[i][0];
12835 int yy = y + xy[i][1];
12836 int center_side = trigger_sides[i][0];
12837 int border_side = trigger_sides[i][1];
12838 int border_element;
12840 if (!IN_LEV_FIELD(xx, yy))
12843 if (IS_PLAYER(x, y)) /* player found at center element */
12845 struct PlayerInfo *player = PLAYERINFO(x, y);
12847 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12848 border_element = Feld[xx][yy]; /* may be moving! */
12849 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12850 border_element = Feld[xx][yy];
12851 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12852 border_element = MovingOrBlocked2Element(xx, yy);
12854 continue; /* center and border element do not touch */
12856 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12857 player->index_bit, border_side);
12858 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12859 CE_PLAYER_TOUCHES_X,
12860 player->index_bit, border_side);
12863 /* use player element that is initially defined in the level playfield,
12864 not the player element that corresponds to the runtime player number
12865 (example: a level that contains EL_PLAYER_3 as the only player would
12866 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12867 int player_element = PLAYERINFO(x, y)->initial_element;
12869 CheckElementChangeBySide(xx, yy, border_element, player_element,
12870 CE_TOUCHING_X, border_side);
12873 else if (IS_PLAYER(xx, yy)) /* player found at border element */
12875 struct PlayerInfo *player = PLAYERINFO(xx, yy);
12877 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12879 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12880 continue; /* center and border element do not touch */
12883 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12884 player->index_bit, center_side);
12885 CheckTriggeredElementChangeByPlayer(x, y, center_element,
12886 CE_PLAYER_TOUCHES_X,
12887 player->index_bit, center_side);
12890 /* use player element that is initially defined in the level playfield,
12891 not the player element that corresponds to the runtime player number
12892 (example: a level that contains EL_PLAYER_3 as the only player would
12893 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12894 int player_element = PLAYERINFO(xx, yy)->initial_element;
12896 CheckElementChangeBySide(x, y, center_element, player_element,
12897 CE_TOUCHING_X, center_side);
12905 void TestIfElementTouchesCustomElement(int x, int y)
12907 static int xy[4][2] =
12914 static int trigger_sides[4][2] =
12916 /* center side border side */
12917 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
12918 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
12919 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
12920 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
12922 static int touch_dir[4] =
12924 MV_LEFT | MV_RIGHT,
12929 boolean change_center_element = FALSE;
12930 int center_element = Feld[x][y]; /* should always be non-moving! */
12931 int border_element_old[NUM_DIRECTIONS];
12934 for (i = 0; i < NUM_DIRECTIONS; i++)
12936 int xx = x + xy[i][0];
12937 int yy = y + xy[i][1];
12938 int border_element;
12940 border_element_old[i] = -1;
12942 if (!IN_LEV_FIELD(xx, yy))
12945 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12946 border_element = Feld[xx][yy]; /* may be moving! */
12947 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12948 border_element = Feld[xx][yy];
12949 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
12950 border_element = MovingOrBlocked2Element(xx, yy);
12952 continue; /* center and border element do not touch */
12954 border_element_old[i] = border_element;
12957 for (i = 0; i < NUM_DIRECTIONS; i++)
12959 int xx = x + xy[i][0];
12960 int yy = y + xy[i][1];
12961 int center_side = trigger_sides[i][0];
12962 int border_element = border_element_old[i];
12964 if (border_element == -1)
12967 /* check for change of border element */
12968 CheckElementChangeBySide(xx, yy, border_element, center_element,
12969 CE_TOUCHING_X, center_side);
12971 /* (center element cannot be player, so we dont have to check this here) */
12974 for (i = 0; i < NUM_DIRECTIONS; i++)
12976 int xx = x + xy[i][0];
12977 int yy = y + xy[i][1];
12978 int border_side = trigger_sides[i][1];
12979 int border_element = border_element_old[i];
12981 if (border_element == -1)
12984 /* check for change of center element (but change it only once) */
12985 if (!change_center_element)
12986 change_center_element =
12987 CheckElementChangeBySide(x, y, center_element, border_element,
12988 CE_TOUCHING_X, border_side);
12990 if (IS_PLAYER(xx, yy))
12992 /* use player element that is initially defined in the level playfield,
12993 not the player element that corresponds to the runtime player number
12994 (example: a level that contains EL_PLAYER_3 as the only player would
12995 incorrectly give EL_PLAYER_1 for "player->element_nr") */
12996 int player_element = PLAYERINFO(xx, yy)->initial_element;
12998 CheckElementChangeBySide(x, y, center_element, player_element,
12999 CE_TOUCHING_X, border_side);
13004 void TestIfElementHitsCustomElement(int x, int y, int direction)
13006 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13007 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13008 int hitx = x + dx, hity = y + dy;
13009 int hitting_element = Feld[x][y];
13010 int touched_element;
13012 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13015 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13016 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13018 if (IN_LEV_FIELD(hitx, hity))
13020 int opposite_direction = MV_DIR_OPPOSITE(direction);
13021 int hitting_side = direction;
13022 int touched_side = opposite_direction;
13023 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13024 MovDir[hitx][hity] != direction ||
13025 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13031 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13032 CE_HITTING_X, touched_side);
13034 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13035 CE_HIT_BY_X, hitting_side);
13037 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13038 CE_HIT_BY_SOMETHING, opposite_direction);
13040 if (IS_PLAYER(hitx, hity))
13042 /* use player element that is initially defined in the level playfield,
13043 not the player element that corresponds to the runtime player number
13044 (example: a level that contains EL_PLAYER_3 as the only player would
13045 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13046 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13048 CheckElementChangeBySide(x, y, hitting_element, player_element,
13049 CE_HITTING_X, touched_side);
13054 /* "hitting something" is also true when hitting the playfield border */
13055 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13056 CE_HITTING_SOMETHING, direction);
13059 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13061 int i, kill_x = -1, kill_y = -1;
13063 int bad_element = -1;
13064 static int test_xy[4][2] =
13071 static int test_dir[4] =
13079 for (i = 0; i < NUM_DIRECTIONS; i++)
13081 int test_x, test_y, test_move_dir, test_element;
13083 test_x = good_x + test_xy[i][0];
13084 test_y = good_y + test_xy[i][1];
13086 if (!IN_LEV_FIELD(test_x, test_y))
13090 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13092 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13094 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13095 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13097 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13098 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13102 bad_element = test_element;
13108 if (kill_x != -1 || kill_y != -1)
13110 if (IS_PLAYER(good_x, good_y))
13112 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13114 if (player->shield_deadly_time_left > 0 &&
13115 !IS_INDESTRUCTIBLE(bad_element))
13116 Bang(kill_x, kill_y);
13117 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13118 KillPlayer(player);
13121 Bang(good_x, good_y);
13125 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13127 int i, kill_x = -1, kill_y = -1;
13128 int bad_element = Feld[bad_x][bad_y];
13129 static int test_xy[4][2] =
13136 static int touch_dir[4] =
13138 MV_LEFT | MV_RIGHT,
13143 static int test_dir[4] =
13151 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13154 for (i = 0; i < NUM_DIRECTIONS; i++)
13156 int test_x, test_y, test_move_dir, test_element;
13158 test_x = bad_x + test_xy[i][0];
13159 test_y = bad_y + test_xy[i][1];
13161 if (!IN_LEV_FIELD(test_x, test_y))
13165 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13167 test_element = Feld[test_x][test_y];
13169 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13170 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13172 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13173 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13175 /* good thing is player or penguin that does not move away */
13176 if (IS_PLAYER(test_x, test_y))
13178 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13180 if (bad_element == EL_ROBOT && player->is_moving)
13181 continue; /* robot does not kill player if he is moving */
13183 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13185 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13186 continue; /* center and border element do not touch */
13194 else if (test_element == EL_PENGUIN)
13204 if (kill_x != -1 || kill_y != -1)
13206 if (IS_PLAYER(kill_x, kill_y))
13208 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13210 if (player->shield_deadly_time_left > 0 &&
13211 !IS_INDESTRUCTIBLE(bad_element))
13212 Bang(bad_x, bad_y);
13213 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13214 KillPlayer(player);
13217 Bang(kill_x, kill_y);
13221 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13223 int bad_element = Feld[bad_x][bad_y];
13224 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13225 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
13226 int test_x = bad_x + dx, test_y = bad_y + dy;
13227 int test_move_dir, test_element;
13228 int kill_x = -1, kill_y = -1;
13230 if (!IN_LEV_FIELD(test_x, test_y))
13234 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13236 test_element = Feld[test_x][test_y];
13238 if (test_move_dir != bad_move_dir)
13240 /* good thing can be player or penguin that does not move away */
13241 if (IS_PLAYER(test_x, test_y))
13243 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13245 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13246 player as being hit when he is moving towards the bad thing, because
13247 the "get hit by" condition would be lost after the player stops) */
13248 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13249 return; /* player moves away from bad thing */
13254 else if (test_element == EL_PENGUIN)
13261 if (kill_x != -1 || kill_y != -1)
13263 if (IS_PLAYER(kill_x, kill_y))
13265 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13267 if (player->shield_deadly_time_left > 0 &&
13268 !IS_INDESTRUCTIBLE(bad_element))
13269 Bang(bad_x, bad_y);
13270 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13271 KillPlayer(player);
13274 Bang(kill_x, kill_y);
13278 void TestIfPlayerTouchesBadThing(int x, int y)
13280 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13283 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13285 TestIfGoodThingHitsBadThing(x, y, move_dir);
13288 void TestIfBadThingTouchesPlayer(int x, int y)
13290 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13293 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13295 TestIfBadThingHitsGoodThing(x, y, move_dir);
13298 void TestIfFriendTouchesBadThing(int x, int y)
13300 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13303 void TestIfBadThingTouchesFriend(int x, int y)
13305 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13308 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13310 int i, kill_x = bad_x, kill_y = bad_y;
13311 static int xy[4][2] =
13319 for (i = 0; i < NUM_DIRECTIONS; i++)
13323 x = bad_x + xy[i][0];
13324 y = bad_y + xy[i][1];
13325 if (!IN_LEV_FIELD(x, y))
13328 element = Feld[x][y];
13329 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13330 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13338 if (kill_x != bad_x || kill_y != bad_y)
13339 Bang(bad_x, bad_y);
13342 void KillPlayer(struct PlayerInfo *player)
13344 int jx = player->jx, jy = player->jy;
13346 if (!player->active)
13350 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13351 player->killed, player->active, player->reanimated);
13354 /* the following code was introduced to prevent an infinite loop when calling
13356 -> CheckTriggeredElementChangeExt()
13357 -> ExecuteCustomElementAction()
13359 -> (infinitely repeating the above sequence of function calls)
13360 which occurs when killing the player while having a CE with the setting
13361 "kill player X when explosion of <player X>"; the solution using a new
13362 field "player->killed" was chosen for backwards compatibility, although
13363 clever use of the fields "player->active" etc. would probably also work */
13365 if (player->killed)
13369 player->killed = TRUE;
13371 /* remove accessible field at the player's position */
13372 Feld[jx][jy] = EL_EMPTY;
13374 /* deactivate shield (else Bang()/Explode() would not work right) */
13375 player->shield_normal_time_left = 0;
13376 player->shield_deadly_time_left = 0;
13379 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13380 player->killed, player->active, player->reanimated);
13386 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13387 player->killed, player->active, player->reanimated);
13390 if (player->reanimated) /* killed player may have been reanimated */
13391 player->killed = player->reanimated = FALSE;
13393 BuryPlayer(player);
13396 static void KillPlayerUnlessEnemyProtected(int x, int y)
13398 if (!PLAYER_ENEMY_PROTECTED(x, y))
13399 KillPlayer(PLAYERINFO(x, y));
13402 static void KillPlayerUnlessExplosionProtected(int x, int y)
13404 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13405 KillPlayer(PLAYERINFO(x, y));
13408 void BuryPlayer(struct PlayerInfo *player)
13410 int jx = player->jx, jy = player->jy;
13412 if (!player->active)
13415 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13416 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13418 player->GameOver = TRUE;
13419 RemovePlayer(player);
13422 void RemovePlayer(struct PlayerInfo *player)
13424 int jx = player->jx, jy = player->jy;
13425 int i, found = FALSE;
13427 player->present = FALSE;
13428 player->active = FALSE;
13430 if (!ExplodeField[jx][jy])
13431 StorePlayer[jx][jy] = 0;
13433 if (player->is_moving)
13434 TEST_DrawLevelField(player->last_jx, player->last_jy);
13436 for (i = 0; i < MAX_PLAYERS; i++)
13437 if (stored_player[i].active)
13441 AllPlayersGone = TRUE;
13447 static void setFieldForSnapping(int x, int y, int element, int direction)
13449 struct ElementInfo *ei = &element_info[element];
13450 int direction_bit = MV_DIR_TO_BIT(direction);
13451 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13452 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13453 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13455 Feld[x][y] = EL_ELEMENT_SNAPPING;
13456 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13458 ResetGfxAnimation(x, y);
13460 GfxElement[x][y] = element;
13461 GfxAction[x][y] = action;
13462 GfxDir[x][y] = direction;
13463 GfxFrame[x][y] = -1;
13467 =============================================================================
13468 checkDiagonalPushing()
13469 -----------------------------------------------------------------------------
13470 check if diagonal input device direction results in pushing of object
13471 (by checking if the alternative direction is walkable, diggable, ...)
13472 =============================================================================
13475 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13476 int x, int y, int real_dx, int real_dy)
13478 int jx, jy, dx, dy, xx, yy;
13480 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13483 /* diagonal direction: check alternative direction */
13488 xx = jx + (dx == 0 ? real_dx : 0);
13489 yy = jy + (dy == 0 ? real_dy : 0);
13491 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13495 =============================================================================
13497 -----------------------------------------------------------------------------
13498 x, y: field next to player (non-diagonal) to try to dig to
13499 real_dx, real_dy: direction as read from input device (can be diagonal)
13500 =============================================================================
13503 static int DigField(struct PlayerInfo *player,
13504 int oldx, int oldy, int x, int y,
13505 int real_dx, int real_dy, int mode)
13507 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13508 boolean player_was_pushing = player->is_pushing;
13509 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13510 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13511 int jx = oldx, jy = oldy;
13512 int dx = x - jx, dy = y - jy;
13513 int nextx = x + dx, nexty = y + dy;
13514 int move_direction = (dx == -1 ? MV_LEFT :
13515 dx == +1 ? MV_RIGHT :
13517 dy == +1 ? MV_DOWN : MV_NONE);
13518 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13519 int dig_side = MV_DIR_OPPOSITE(move_direction);
13520 int old_element = Feld[jx][jy];
13521 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13524 if (is_player) /* function can also be called by EL_PENGUIN */
13526 if (player->MovPos == 0)
13528 player->is_digging = FALSE;
13529 player->is_collecting = FALSE;
13532 if (player->MovPos == 0) /* last pushing move finished */
13533 player->is_pushing = FALSE;
13535 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13537 player->is_switching = FALSE;
13538 player->push_delay = -1;
13540 return MP_NO_ACTION;
13544 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13545 old_element = Back[jx][jy];
13547 /* in case of element dropped at player position, check background */
13548 else if (Back[jx][jy] != EL_EMPTY &&
13549 game.engine_version >= VERSION_IDENT(2,2,0,0))
13550 old_element = Back[jx][jy];
13552 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13553 return MP_NO_ACTION; /* field has no opening in this direction */
13555 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13556 return MP_NO_ACTION; /* field has no opening in this direction */
13558 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13562 Feld[jx][jy] = player->artwork_element;
13563 InitMovingField(jx, jy, MV_DOWN);
13564 Store[jx][jy] = EL_ACID;
13565 ContinueMoving(jx, jy);
13566 BuryPlayer(player);
13568 return MP_DONT_RUN_INTO;
13571 if (player_can_move && DONT_RUN_INTO(element))
13573 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13575 return MP_DONT_RUN_INTO;
13578 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13579 return MP_NO_ACTION;
13581 collect_count = element_info[element].collect_count_initial;
13583 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13584 return MP_NO_ACTION;
13586 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13587 player_can_move = player_can_move_or_snap;
13589 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13590 game.engine_version >= VERSION_IDENT(2,2,0,0))
13592 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13593 player->index_bit, dig_side);
13594 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13595 player->index_bit, dig_side);
13597 if (element == EL_DC_LANDMINE)
13600 if (Feld[x][y] != element) /* field changed by snapping */
13603 return MP_NO_ACTION;
13606 if (player->gravity && is_player && !player->is_auto_moving &&
13607 canFallDown(player) && move_direction != MV_DOWN &&
13608 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13609 return MP_NO_ACTION; /* player cannot walk here due to gravity */
13611 if (player_can_move &&
13612 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13614 int sound_element = SND_ELEMENT(element);
13615 int sound_action = ACTION_WALKING;
13617 if (IS_RND_GATE(element))
13619 if (!player->key[RND_GATE_NR(element)])
13620 return MP_NO_ACTION;
13622 else if (IS_RND_GATE_GRAY(element))
13624 if (!player->key[RND_GATE_GRAY_NR(element)])
13625 return MP_NO_ACTION;
13627 else if (IS_RND_GATE_GRAY_ACTIVE(element))
13629 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13630 return MP_NO_ACTION;
13632 else if (element == EL_EXIT_OPEN ||
13633 element == EL_EM_EXIT_OPEN ||
13634 element == EL_EM_EXIT_OPENING ||
13635 element == EL_STEEL_EXIT_OPEN ||
13636 element == EL_EM_STEEL_EXIT_OPEN ||
13637 element == EL_EM_STEEL_EXIT_OPENING ||
13638 element == EL_SP_EXIT_OPEN ||
13639 element == EL_SP_EXIT_OPENING)
13641 sound_action = ACTION_PASSING; /* player is passing exit */
13643 else if (element == EL_EMPTY)
13645 sound_action = ACTION_MOVING; /* nothing to walk on */
13648 /* play sound from background or player, whatever is available */
13649 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13650 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13652 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13654 else if (player_can_move &&
13655 IS_PASSABLE(element) && canPassField(x, y, move_direction))
13657 if (!ACCESS_FROM(element, opposite_direction))
13658 return MP_NO_ACTION; /* field not accessible from this direction */
13660 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
13661 return MP_NO_ACTION;
13663 if (IS_EM_GATE(element))
13665 if (!player->key[EM_GATE_NR(element)])
13666 return MP_NO_ACTION;
13668 else if (IS_EM_GATE_GRAY(element))
13670 if (!player->key[EM_GATE_GRAY_NR(element)])
13671 return MP_NO_ACTION;
13673 else if (IS_EM_GATE_GRAY_ACTIVE(element))
13675 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13676 return MP_NO_ACTION;
13678 else if (IS_EMC_GATE(element))
13680 if (!player->key[EMC_GATE_NR(element)])
13681 return MP_NO_ACTION;
13683 else if (IS_EMC_GATE_GRAY(element))
13685 if (!player->key[EMC_GATE_GRAY_NR(element)])
13686 return MP_NO_ACTION;
13688 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13690 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13691 return MP_NO_ACTION;
13693 else if (element == EL_DC_GATE_WHITE ||
13694 element == EL_DC_GATE_WHITE_GRAY ||
13695 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13697 if (player->num_white_keys == 0)
13698 return MP_NO_ACTION;
13700 player->num_white_keys--;
13702 else if (IS_SP_PORT(element))
13704 if (element == EL_SP_GRAVITY_PORT_LEFT ||
13705 element == EL_SP_GRAVITY_PORT_RIGHT ||
13706 element == EL_SP_GRAVITY_PORT_UP ||
13707 element == EL_SP_GRAVITY_PORT_DOWN)
13708 player->gravity = !player->gravity;
13709 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13710 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13711 element == EL_SP_GRAVITY_ON_PORT_UP ||
13712 element == EL_SP_GRAVITY_ON_PORT_DOWN)
13713 player->gravity = TRUE;
13714 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13715 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13716 element == EL_SP_GRAVITY_OFF_PORT_UP ||
13717 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13718 player->gravity = FALSE;
13721 /* automatically move to the next field with double speed */
13722 player->programmed_action = move_direction;
13724 if (player->move_delay_reset_counter == 0)
13726 player->move_delay_reset_counter = 2; /* two double speed steps */
13728 DOUBLE_PLAYER_SPEED(player);
13731 PlayLevelSoundAction(x, y, ACTION_PASSING);
13733 else if (player_can_move_or_snap && IS_DIGGABLE(element))
13737 if (mode != DF_SNAP)
13739 GfxElement[x][y] = GFX_ELEMENT(element);
13740 player->is_digging = TRUE;
13743 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13745 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13746 player->index_bit, dig_side);
13748 if (mode == DF_SNAP)
13750 if (level.block_snap_field)
13751 setFieldForSnapping(x, y, element, move_direction);
13753 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13755 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13756 player->index_bit, dig_side);
13759 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13763 if (is_player && mode != DF_SNAP)
13765 GfxElement[x][y] = element;
13766 player->is_collecting = TRUE;
13769 if (element == EL_SPEED_PILL)
13771 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13773 else if (element == EL_EXTRA_TIME && level.time > 0)
13775 TimeLeft += level.extra_time;
13777 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13779 DisplayGameControlValues();
13781 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13783 player->shield_normal_time_left += level.shield_normal_time;
13784 if (element == EL_SHIELD_DEADLY)
13785 player->shield_deadly_time_left += level.shield_deadly_time;
13787 else if (element == EL_DYNAMITE ||
13788 element == EL_EM_DYNAMITE ||
13789 element == EL_SP_DISK_RED)
13791 if (player->inventory_size < MAX_INVENTORY_SIZE)
13792 player->inventory_element[player->inventory_size++] = element;
13794 DrawGameDoorValues();
13796 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13798 player->dynabomb_count++;
13799 player->dynabombs_left++;
13801 else if (element == EL_DYNABOMB_INCREASE_SIZE)
13803 player->dynabomb_size++;
13805 else if (element == EL_DYNABOMB_INCREASE_POWER)
13807 player->dynabomb_xl = TRUE;
13809 else if (IS_KEY(element))
13811 player->key[KEY_NR(element)] = TRUE;
13813 DrawGameDoorValues();
13815 else if (element == EL_DC_KEY_WHITE)
13817 player->num_white_keys++;
13819 /* display white keys? */
13820 /* DrawGameDoorValues(); */
13822 else if (IS_ENVELOPE(element))
13824 player->show_envelope = element;
13826 else if (element == EL_EMC_LENSES)
13828 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13830 RedrawAllInvisibleElementsForLenses();
13832 else if (element == EL_EMC_MAGNIFIER)
13834 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13836 RedrawAllInvisibleElementsForMagnifier();
13838 else if (IS_DROPPABLE(element) ||
13839 IS_THROWABLE(element)) /* can be collected and dropped */
13843 if (collect_count == 0)
13844 player->inventory_infinite_element = element;
13846 for (i = 0; i < collect_count; i++)
13847 if (player->inventory_size < MAX_INVENTORY_SIZE)
13848 player->inventory_element[player->inventory_size++] = element;
13850 DrawGameDoorValues();
13852 else if (collect_count > 0)
13854 local_player->gems_still_needed -= collect_count;
13855 if (local_player->gems_still_needed < 0)
13856 local_player->gems_still_needed = 0;
13858 game.snapshot.collected_item = TRUE;
13860 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13862 DisplayGameControlValues();
13865 RaiseScoreElement(element);
13866 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13869 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13870 player->index_bit, dig_side);
13872 if (mode == DF_SNAP)
13874 if (level.block_snap_field)
13875 setFieldForSnapping(x, y, element, move_direction);
13877 TestIfElementTouchesCustomElement(x, y); /* for empty space */
13879 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13880 player->index_bit, dig_side);
13883 else if (player_can_move_or_snap && IS_PUSHABLE(element))
13885 if (mode == DF_SNAP && element != EL_BD_ROCK)
13886 return MP_NO_ACTION;
13888 if (CAN_FALL(element) && dy)
13889 return MP_NO_ACTION;
13891 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13892 !(element == EL_SPRING && level.use_spring_bug))
13893 return MP_NO_ACTION;
13895 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13896 ((move_direction & MV_VERTICAL &&
13897 ((element_info[element].move_pattern & MV_LEFT &&
13898 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13899 (element_info[element].move_pattern & MV_RIGHT &&
13900 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13901 (move_direction & MV_HORIZONTAL &&
13902 ((element_info[element].move_pattern & MV_UP &&
13903 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13904 (element_info[element].move_pattern & MV_DOWN &&
13905 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13906 return MP_NO_ACTION;
13908 /* do not push elements already moving away faster than player */
13909 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13910 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13911 return MP_NO_ACTION;
13913 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13915 if (player->push_delay_value == -1 || !player_was_pushing)
13916 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13918 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13920 if (player->push_delay_value == -1)
13921 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13923 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13925 if (!player->is_pushing)
13926 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13929 player->is_pushing = TRUE;
13930 player->is_active = TRUE;
13932 if (!(IN_LEV_FIELD(nextx, nexty) &&
13933 (IS_FREE(nextx, nexty) ||
13934 (IS_SB_ELEMENT(element) &&
13935 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13936 (IS_CUSTOM_ELEMENT(element) &&
13937 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13938 return MP_NO_ACTION;
13940 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13941 return MP_NO_ACTION;
13943 if (player->push_delay == -1) /* new pushing; restart delay */
13944 player->push_delay = 0;
13946 if (player->push_delay < player->push_delay_value &&
13947 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13948 element != EL_SPRING && element != EL_BALLOON)
13950 /* make sure that there is no move delay before next try to push */
13951 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13952 player->move_delay = 0;
13954 return MP_NO_ACTION;
13957 if (IS_CUSTOM_ELEMENT(element) &&
13958 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13960 if (!DigFieldByCE(nextx, nexty, element))
13961 return MP_NO_ACTION;
13964 if (IS_SB_ELEMENT(element))
13966 if (element == EL_SOKOBAN_FIELD_FULL)
13968 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13969 local_player->sokobanfields_still_needed++;
13972 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13974 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13975 local_player->sokobanfields_still_needed--;
13978 Feld[x][y] = EL_SOKOBAN_OBJECT;
13980 if (Back[x][y] == Back[nextx][nexty])
13981 PlayLevelSoundAction(x, y, ACTION_PUSHING);
13982 else if (Back[x][y] != 0)
13983 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13986 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13989 if (local_player->sokobanfields_still_needed == 0 &&
13990 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13992 PlayerWins(player);
13994 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13998 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14000 InitMovingField(x, y, move_direction);
14001 GfxAction[x][y] = ACTION_PUSHING;
14003 if (mode == DF_SNAP)
14004 ContinueMoving(x, y);
14006 MovPos[x][y] = (dx != 0 ? dx : dy);
14008 Pushed[x][y] = TRUE;
14009 Pushed[nextx][nexty] = TRUE;
14011 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14012 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14014 player->push_delay_value = -1; /* get new value later */
14016 /* check for element change _after_ element has been pushed */
14017 if (game.use_change_when_pushing_bug)
14019 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14020 player->index_bit, dig_side);
14021 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14022 player->index_bit, dig_side);
14025 else if (IS_SWITCHABLE(element))
14027 if (PLAYER_SWITCHING(player, x, y))
14029 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14030 player->index_bit, dig_side);
14035 player->is_switching = TRUE;
14036 player->switch_x = x;
14037 player->switch_y = y;
14039 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14041 if (element == EL_ROBOT_WHEEL)
14043 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14047 game.robot_wheel_active = TRUE;
14049 TEST_DrawLevelField(x, y);
14051 else if (element == EL_SP_TERMINAL)
14055 SCAN_PLAYFIELD(xx, yy)
14057 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14061 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14063 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14065 ResetGfxAnimation(xx, yy);
14066 TEST_DrawLevelField(xx, yy);
14070 else if (IS_BELT_SWITCH(element))
14072 ToggleBeltSwitch(x, y);
14074 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14075 element == EL_SWITCHGATE_SWITCH_DOWN ||
14076 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14077 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14079 ToggleSwitchgateSwitch(x, y);
14081 else if (element == EL_LIGHT_SWITCH ||
14082 element == EL_LIGHT_SWITCH_ACTIVE)
14084 ToggleLightSwitch(x, y);
14086 else if (element == EL_TIMEGATE_SWITCH ||
14087 element == EL_DC_TIMEGATE_SWITCH)
14089 ActivateTimegateSwitch(x, y);
14091 else if (element == EL_BALLOON_SWITCH_LEFT ||
14092 element == EL_BALLOON_SWITCH_RIGHT ||
14093 element == EL_BALLOON_SWITCH_UP ||
14094 element == EL_BALLOON_SWITCH_DOWN ||
14095 element == EL_BALLOON_SWITCH_NONE ||
14096 element == EL_BALLOON_SWITCH_ANY)
14098 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14099 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14100 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14101 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14102 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14105 else if (element == EL_LAMP)
14107 Feld[x][y] = EL_LAMP_ACTIVE;
14108 local_player->lights_still_needed--;
14110 ResetGfxAnimation(x, y);
14111 TEST_DrawLevelField(x, y);
14113 else if (element == EL_TIME_ORB_FULL)
14115 Feld[x][y] = EL_TIME_ORB_EMPTY;
14117 if (level.time > 0 || level.use_time_orb_bug)
14119 TimeLeft += level.time_orb_time;
14120 game.no_time_limit = FALSE;
14122 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14124 DisplayGameControlValues();
14127 ResetGfxAnimation(x, y);
14128 TEST_DrawLevelField(x, y);
14130 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14131 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14135 game.ball_state = !game.ball_state;
14137 SCAN_PLAYFIELD(xx, yy)
14139 int e = Feld[xx][yy];
14141 if (game.ball_state)
14143 if (e == EL_EMC_MAGIC_BALL)
14144 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14145 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14146 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14150 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14151 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14152 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14153 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14158 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14159 player->index_bit, dig_side);
14161 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14162 player->index_bit, dig_side);
14164 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14165 player->index_bit, dig_side);
14171 if (!PLAYER_SWITCHING(player, x, y))
14173 player->is_switching = TRUE;
14174 player->switch_x = x;
14175 player->switch_y = y;
14177 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14178 player->index_bit, dig_side);
14179 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14180 player->index_bit, dig_side);
14182 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14183 player->index_bit, dig_side);
14184 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14185 player->index_bit, dig_side);
14188 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14189 player->index_bit, dig_side);
14190 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14191 player->index_bit, dig_side);
14193 return MP_NO_ACTION;
14196 player->push_delay = -1;
14198 if (is_player) /* function can also be called by EL_PENGUIN */
14200 if (Feld[x][y] != element) /* really digged/collected something */
14202 player->is_collecting = !player->is_digging;
14203 player->is_active = TRUE;
14210 static boolean DigFieldByCE(int x, int y, int digging_element)
14212 int element = Feld[x][y];
14214 if (!IS_FREE(x, y))
14216 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14217 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14220 /* no element can dig solid indestructible elements */
14221 if (IS_INDESTRUCTIBLE(element) &&
14222 !IS_DIGGABLE(element) &&
14223 !IS_COLLECTIBLE(element))
14226 if (AmoebaNr[x][y] &&
14227 (element == EL_AMOEBA_FULL ||
14228 element == EL_BD_AMOEBA ||
14229 element == EL_AMOEBA_GROWING))
14231 AmoebaCnt[AmoebaNr[x][y]]--;
14232 AmoebaCnt2[AmoebaNr[x][y]]--;
14235 if (IS_MOVING(x, y))
14236 RemoveMovingField(x, y);
14240 TEST_DrawLevelField(x, y);
14243 /* if digged element was about to explode, prevent the explosion */
14244 ExplodeField[x][y] = EX_TYPE_NONE;
14246 PlayLevelSoundAction(x, y, action);
14249 Store[x][y] = EL_EMPTY;
14251 /* this makes it possible to leave the removed element again */
14252 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14253 Store[x][y] = element;
14258 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14260 int jx = player->jx, jy = player->jy;
14261 int x = jx + dx, y = jy + dy;
14262 int snap_direction = (dx == -1 ? MV_LEFT :
14263 dx == +1 ? MV_RIGHT :
14265 dy == +1 ? MV_DOWN : MV_NONE);
14266 boolean can_continue_snapping = (level.continuous_snapping &&
14267 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14269 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14272 if (!player->active || !IN_LEV_FIELD(x, y))
14280 if (player->MovPos == 0)
14281 player->is_pushing = FALSE;
14283 player->is_snapping = FALSE;
14285 if (player->MovPos == 0)
14287 player->is_moving = FALSE;
14288 player->is_digging = FALSE;
14289 player->is_collecting = FALSE;
14295 /* prevent snapping with already pressed snap key when not allowed */
14296 if (player->is_snapping && !can_continue_snapping)
14299 player->MovDir = snap_direction;
14301 if (player->MovPos == 0)
14303 player->is_moving = FALSE;
14304 player->is_digging = FALSE;
14305 player->is_collecting = FALSE;
14308 player->is_dropping = FALSE;
14309 player->is_dropping_pressed = FALSE;
14310 player->drop_pressed_delay = 0;
14312 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14315 player->is_snapping = TRUE;
14316 player->is_active = TRUE;
14318 if (player->MovPos == 0)
14320 player->is_moving = FALSE;
14321 player->is_digging = FALSE;
14322 player->is_collecting = FALSE;
14325 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14326 TEST_DrawLevelField(player->last_jx, player->last_jy);
14328 TEST_DrawLevelField(x, y);
14333 static boolean DropElement(struct PlayerInfo *player)
14335 int old_element, new_element;
14336 int dropx = player->jx, dropy = player->jy;
14337 int drop_direction = player->MovDir;
14338 int drop_side = drop_direction;
14339 int drop_element = get_next_dropped_element(player);
14341 /* do not drop an element on top of another element; when holding drop key
14342 pressed without moving, dropped element must move away before the next
14343 element can be dropped (this is especially important if the next element
14344 is dynamite, which can be placed on background for historical reasons) */
14345 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14348 if (IS_THROWABLE(drop_element))
14350 dropx += GET_DX_FROM_DIR(drop_direction);
14351 dropy += GET_DY_FROM_DIR(drop_direction);
14353 if (!IN_LEV_FIELD(dropx, dropy))
14357 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14358 new_element = drop_element; /* default: no change when dropping */
14360 /* check if player is active, not moving and ready to drop */
14361 if (!player->active || player->MovPos || player->drop_delay > 0)
14364 /* check if player has anything that can be dropped */
14365 if (new_element == EL_UNDEFINED)
14368 /* only set if player has anything that can be dropped */
14369 player->is_dropping_pressed = TRUE;
14371 /* check if drop key was pressed long enough for EM style dynamite */
14372 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14375 /* check if anything can be dropped at the current position */
14376 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14379 /* collected custom elements can only be dropped on empty fields */
14380 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14383 if (old_element != EL_EMPTY)
14384 Back[dropx][dropy] = old_element; /* store old element on this field */
14386 ResetGfxAnimation(dropx, dropy);
14387 ResetRandomAnimationValue(dropx, dropy);
14389 if (player->inventory_size > 0 ||
14390 player->inventory_infinite_element != EL_UNDEFINED)
14392 if (player->inventory_size > 0)
14394 player->inventory_size--;
14396 DrawGameDoorValues();
14398 if (new_element == EL_DYNAMITE)
14399 new_element = EL_DYNAMITE_ACTIVE;
14400 else if (new_element == EL_EM_DYNAMITE)
14401 new_element = EL_EM_DYNAMITE_ACTIVE;
14402 else if (new_element == EL_SP_DISK_RED)
14403 new_element = EL_SP_DISK_RED_ACTIVE;
14406 Feld[dropx][dropy] = new_element;
14408 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14409 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14410 el2img(Feld[dropx][dropy]), 0);
14412 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14414 /* needed if previous element just changed to "empty" in the last frame */
14415 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14417 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14418 player->index_bit, drop_side);
14419 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14421 player->index_bit, drop_side);
14423 TestIfElementTouchesCustomElement(dropx, dropy);
14425 else /* player is dropping a dyna bomb */
14427 player->dynabombs_left--;
14429 Feld[dropx][dropy] = new_element;
14431 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14432 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14433 el2img(Feld[dropx][dropy]), 0);
14435 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14438 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14439 InitField_WithBug1(dropx, dropy, FALSE);
14441 new_element = Feld[dropx][dropy]; /* element might have changed */
14443 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14444 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14446 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14447 MovDir[dropx][dropy] = drop_direction;
14449 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14451 /* do not cause impact style collision by dropping elements that can fall */
14452 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14455 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14456 player->is_dropping = TRUE;
14458 player->drop_pressed_delay = 0;
14459 player->is_dropping_pressed = FALSE;
14461 player->drop_x = dropx;
14462 player->drop_y = dropy;
14467 /* ------------------------------------------------------------------------- */
14468 /* game sound playing functions */
14469 /* ------------------------------------------------------------------------- */
14471 static int *loop_sound_frame = NULL;
14472 static int *loop_sound_volume = NULL;
14474 void InitPlayLevelSound()
14476 int num_sounds = getSoundListSize();
14478 checked_free(loop_sound_frame);
14479 checked_free(loop_sound_volume);
14481 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14482 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14485 static void PlayLevelSound(int x, int y, int nr)
14487 int sx = SCREENX(x), sy = SCREENY(y);
14488 int volume, stereo_position;
14489 int max_distance = 8;
14490 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14492 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14493 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14496 if (!IN_LEV_FIELD(x, y) ||
14497 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14498 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14501 volume = SOUND_MAX_VOLUME;
14503 if (!IN_SCR_FIELD(sx, sy))
14505 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14506 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14508 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14511 stereo_position = (SOUND_MAX_LEFT +
14512 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14513 (SCR_FIELDX + 2 * max_distance));
14515 if (IS_LOOP_SOUND(nr))
14517 /* This assures that quieter loop sounds do not overwrite louder ones,
14518 while restarting sound volume comparison with each new game frame. */
14520 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14523 loop_sound_volume[nr] = volume;
14524 loop_sound_frame[nr] = FrameCounter;
14527 PlaySoundExt(nr, volume, stereo_position, type);
14530 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14532 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14533 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14534 y < LEVELY(BY1) ? LEVELY(BY1) :
14535 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14539 static void PlayLevelSoundAction(int x, int y, int action)
14541 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14544 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14546 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14548 if (sound_effect != SND_UNDEFINED)
14549 PlayLevelSound(x, y, sound_effect);
14552 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14555 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14557 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14558 PlayLevelSound(x, y, sound_effect);
14561 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14563 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14565 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14566 PlayLevelSound(x, y, sound_effect);
14569 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14571 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14573 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14574 StopSound(sound_effect);
14577 static int getLevelMusicNr()
14579 if (levelset.music[level_nr] != MUS_UNDEFINED)
14580 return levelset.music[level_nr]; /* from config file */
14582 return MAP_NOCONF_MUSIC(level_nr); /* from music dir */
14585 static void FadeLevelSounds()
14590 static void FadeLevelMusic()
14592 int music_nr = getLevelMusicNr();
14593 char *curr_music = getCurrentlyPlayingMusicFilename();
14594 char *next_music = getMusicInfoEntryFilename(music_nr);
14596 if (!strEqual(curr_music, next_music))
14600 void FadeLevelSoundsAndMusic()
14606 static void PlayLevelMusic()
14608 int music_nr = getLevelMusicNr();
14609 char *curr_music = getCurrentlyPlayingMusicFilename();
14610 char *next_music = getMusicInfoEntryFilename(music_nr);
14612 if (!strEqual(curr_music, next_music))
14613 PlayMusic(music_nr);
14616 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14618 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14619 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14620 int x = xx - 1 - offset;
14621 int y = yy - 1 - offset;
14626 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14630 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14634 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14638 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14642 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14646 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14650 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14653 case SAMPLE_android_clone:
14654 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14657 case SAMPLE_android_move:
14658 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14661 case SAMPLE_spring:
14662 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14666 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14670 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14673 case SAMPLE_eater_eat:
14674 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14678 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14681 case SAMPLE_collect:
14682 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14685 case SAMPLE_diamond:
14686 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14689 case SAMPLE_squash:
14690 /* !!! CHECK THIS !!! */
14692 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14694 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14698 case SAMPLE_wonderfall:
14699 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14703 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14707 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14711 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14715 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14719 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14723 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14726 case SAMPLE_wonder:
14727 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14731 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14734 case SAMPLE_exit_open:
14735 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14738 case SAMPLE_exit_leave:
14739 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14742 case SAMPLE_dynamite:
14743 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14747 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14751 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14755 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14759 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14763 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14767 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14771 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14776 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14778 int element = map_element_SP_to_RND(element_sp);
14779 int action = map_action_SP_to_RND(action_sp);
14780 int offset = (setup.sp_show_border_elements ? 0 : 1);
14781 int x = xx - offset;
14782 int y = yy - offset;
14784 PlayLevelSoundElementAction(x, y, element, action);
14787 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14789 int element = map_element_MM_to_RND(element_mm);
14790 int action = map_action_MM_to_RND(action_mm);
14792 int x = xx - offset;
14793 int y = yy - offset;
14795 if (!IS_MM_ELEMENT(element))
14796 element = EL_MM_DEFAULT;
14798 PlayLevelSoundElementAction(x, y, element, action);
14801 void PlaySound_MM(int sound_mm)
14803 int sound = map_sound_MM_to_RND(sound_mm);
14805 if (sound == SND_UNDEFINED)
14811 void PlaySoundLoop_MM(int sound_mm)
14813 int sound = map_sound_MM_to_RND(sound_mm);
14815 if (sound == SND_UNDEFINED)
14818 PlaySoundLoop(sound);
14821 void StopSound_MM(int sound_mm)
14823 int sound = map_sound_MM_to_RND(sound_mm);
14825 if (sound == SND_UNDEFINED)
14831 void RaiseScore(int value)
14833 local_player->score += value;
14835 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14837 DisplayGameControlValues();
14840 void RaiseScoreElement(int element)
14845 case EL_BD_DIAMOND:
14846 case EL_EMERALD_YELLOW:
14847 case EL_EMERALD_RED:
14848 case EL_EMERALD_PURPLE:
14849 case EL_SP_INFOTRON:
14850 RaiseScore(level.score[SC_EMERALD]);
14853 RaiseScore(level.score[SC_DIAMOND]);
14856 RaiseScore(level.score[SC_CRYSTAL]);
14859 RaiseScore(level.score[SC_PEARL]);
14862 case EL_BD_BUTTERFLY:
14863 case EL_SP_ELECTRON:
14864 RaiseScore(level.score[SC_BUG]);
14867 case EL_BD_FIREFLY:
14868 case EL_SP_SNIKSNAK:
14869 RaiseScore(level.score[SC_SPACESHIP]);
14872 case EL_DARK_YAMYAM:
14873 RaiseScore(level.score[SC_YAMYAM]);
14876 RaiseScore(level.score[SC_ROBOT]);
14879 RaiseScore(level.score[SC_PACMAN]);
14882 RaiseScore(level.score[SC_NUT]);
14885 case EL_EM_DYNAMITE:
14886 case EL_SP_DISK_RED:
14887 case EL_DYNABOMB_INCREASE_NUMBER:
14888 case EL_DYNABOMB_INCREASE_SIZE:
14889 case EL_DYNABOMB_INCREASE_POWER:
14890 RaiseScore(level.score[SC_DYNAMITE]);
14892 case EL_SHIELD_NORMAL:
14893 case EL_SHIELD_DEADLY:
14894 RaiseScore(level.score[SC_SHIELD]);
14896 case EL_EXTRA_TIME:
14897 RaiseScore(level.extra_time_score);
14911 case EL_DC_KEY_WHITE:
14912 RaiseScore(level.score[SC_KEY]);
14915 RaiseScore(element_info[element].collect_score);
14920 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14922 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14924 /* closing door required in case of envelope style request dialogs */
14926 CloseDoor(DOOR_CLOSE_1);
14928 #if defined(NETWORK_AVALIABLE)
14929 if (options.network)
14930 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14935 FadeSkipNextFadeIn();
14937 SetGameStatus(GAME_MODE_MAIN);
14942 else /* continue playing the game */
14944 if (tape.playing && tape.deactivate_display)
14945 TapeDeactivateDisplayOff(TRUE);
14947 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14949 if (tape.playing && tape.deactivate_display)
14950 TapeDeactivateDisplayOn();
14954 void RequestQuitGame(boolean ask_if_really_quit)
14956 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14957 boolean skip_request = AllPlayersGone || quick_quit;
14959 RequestQuitGameExt(skip_request, quick_quit,
14960 "Do you really want to quit the game?");
14963 void RequestRestartGame(char *message)
14965 game.restart_game_message = NULL;
14967 if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14969 StartGameActions(options.network, setup.autorecord, level.random_seed);
14973 SetGameStatus(GAME_MODE_MAIN);
14980 /* ------------------------------------------------------------------------- */
14981 /* random generator functions */
14982 /* ------------------------------------------------------------------------- */
14984 unsigned int InitEngineRandom_RND(int seed)
14986 game.num_random_calls = 0;
14988 return InitEngineRandom(seed);
14991 unsigned int RND(int max)
14995 game.num_random_calls++;
14997 return GetEngineRandom(max);
15004 /* ------------------------------------------------------------------------- */
15005 /* game engine snapshot handling functions */
15006 /* ------------------------------------------------------------------------- */
15008 struct EngineSnapshotInfo
15010 /* runtime values for custom element collect score */
15011 int collect_score[NUM_CUSTOM_ELEMENTS];
15013 /* runtime values for group element choice position */
15014 int choice_pos[NUM_GROUP_ELEMENTS];
15016 /* runtime values for belt position animations */
15017 int belt_graphic[4][NUM_BELT_PARTS];
15018 int belt_anim_mode[4][NUM_BELT_PARTS];
15021 static struct EngineSnapshotInfo engine_snapshot_rnd;
15022 static char *snapshot_level_identifier = NULL;
15023 static int snapshot_level_nr = -1;
15025 static void SaveEngineSnapshotValues_RND()
15027 static int belt_base_active_element[4] =
15029 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15030 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15031 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15032 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15036 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15038 int element = EL_CUSTOM_START + i;
15040 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15043 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15045 int element = EL_GROUP_START + i;
15047 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15050 for (i = 0; i < 4; i++)
15052 for (j = 0; j < NUM_BELT_PARTS; j++)
15054 int element = belt_base_active_element[i] + j;
15055 int graphic = el2img(element);
15056 int anim_mode = graphic_info[graphic].anim_mode;
15058 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15059 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15064 static void LoadEngineSnapshotValues_RND()
15066 unsigned int num_random_calls = game.num_random_calls;
15069 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15071 int element = EL_CUSTOM_START + i;
15073 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15076 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15078 int element = EL_GROUP_START + i;
15080 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15083 for (i = 0; i < 4; i++)
15085 for (j = 0; j < NUM_BELT_PARTS; j++)
15087 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15088 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15090 graphic_info[graphic].anim_mode = anim_mode;
15094 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15096 InitRND(tape.random_seed);
15097 for (i = 0; i < num_random_calls; i++)
15101 if (game.num_random_calls != num_random_calls)
15103 Error(ERR_INFO, "number of random calls out of sync");
15104 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15105 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15106 Error(ERR_EXIT, "this should not happen -- please debug");
15110 void FreeEngineSnapshotSingle()
15112 FreeSnapshotSingle();
15114 setString(&snapshot_level_identifier, NULL);
15115 snapshot_level_nr = -1;
15118 void FreeEngineSnapshotList()
15120 FreeSnapshotList();
15123 ListNode *SaveEngineSnapshotBuffers()
15125 ListNode *buffers = NULL;
15127 /* copy some special values to a structure better suited for the snapshot */
15129 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15130 SaveEngineSnapshotValues_RND();
15131 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15132 SaveEngineSnapshotValues_EM();
15133 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15134 SaveEngineSnapshotValues_SP(&buffers);
15135 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15136 SaveEngineSnapshotValues_MM(&buffers);
15138 /* save values stored in special snapshot structure */
15140 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15141 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15142 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15143 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15144 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15145 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15146 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15147 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15149 /* save further RND engine values */
15151 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15152 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15153 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15155 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15156 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15157 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15158 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15160 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15161 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15162 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15163 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15164 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15166 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15167 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15168 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15170 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15172 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15174 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15175 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15177 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15178 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15179 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15180 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15181 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15182 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15183 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15184 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15185 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15186 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15187 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15188 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15189 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15190 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15191 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15192 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15193 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15194 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15196 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15197 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15199 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15200 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15201 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15203 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15204 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15206 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15207 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15208 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15209 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15210 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15212 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15213 SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15216 ListNode *node = engine_snapshot_list_rnd;
15219 while (node != NULL)
15221 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15226 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15232 void SaveEngineSnapshotSingle()
15234 ListNode *buffers = SaveEngineSnapshotBuffers();
15236 /* finally save all snapshot buffers to single snapshot */
15237 SaveSnapshotSingle(buffers);
15239 /* save level identification information */
15240 setString(&snapshot_level_identifier, leveldir_current->identifier);
15241 snapshot_level_nr = level_nr;
15244 boolean CheckSaveEngineSnapshotToList()
15246 boolean save_snapshot =
15247 ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15248 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15249 game.snapshot.changed_action) ||
15250 (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15251 game.snapshot.collected_item));
15253 game.snapshot.changed_action = FALSE;
15254 game.snapshot.collected_item = FALSE;
15255 game.snapshot.save_snapshot = save_snapshot;
15257 return save_snapshot;
15260 void SaveEngineSnapshotToList()
15262 if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15266 ListNode *buffers = SaveEngineSnapshotBuffers();
15268 /* finally save all snapshot buffers to snapshot list */
15269 SaveSnapshotToList(buffers);
15272 void SaveEngineSnapshotToListInitial()
15274 FreeEngineSnapshotList();
15276 SaveEngineSnapshotToList();
15279 void LoadEngineSnapshotValues()
15281 /* restore special values from snapshot structure */
15283 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15284 LoadEngineSnapshotValues_RND();
15285 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15286 LoadEngineSnapshotValues_EM();
15287 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15288 LoadEngineSnapshotValues_SP();
15289 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15290 LoadEngineSnapshotValues_MM();
15293 void LoadEngineSnapshotSingle()
15295 LoadSnapshotSingle();
15297 LoadEngineSnapshotValues();
15300 void LoadEngineSnapshot_Undo(int steps)
15302 LoadSnapshotFromList_Older(steps);
15304 LoadEngineSnapshotValues();
15307 void LoadEngineSnapshot_Redo(int steps)
15309 LoadSnapshotFromList_Newer(steps);
15311 LoadEngineSnapshotValues();
15314 boolean CheckEngineSnapshotSingle()
15316 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15317 snapshot_level_nr == level_nr);
15320 boolean CheckEngineSnapshotList()
15322 return CheckSnapshotList();
15326 /* ---------- new game button stuff ---------------------------------------- */
15333 boolean *setup_value;
15334 boolean allowed_on_tape;
15336 } gamebutton_info[NUM_GAME_BUTTONS] =
15339 IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
15340 GAME_CTRL_ID_STOP, NULL,
15344 IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
15345 GAME_CTRL_ID_PAUSE, NULL,
15349 IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
15350 GAME_CTRL_ID_PLAY, NULL,
15354 IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
15355 GAME_CTRL_ID_UNDO, NULL,
15359 IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
15360 GAME_CTRL_ID_REDO, NULL,
15364 IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
15365 GAME_CTRL_ID_SAVE, NULL,
15369 IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
15370 GAME_CTRL_ID_PAUSE2, NULL,
15374 IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
15375 GAME_CTRL_ID_LOAD, NULL,
15379 IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop,
15380 GAME_CTRL_ID_PANEL_STOP, NULL,
15384 IMG_GFX_GAME_BUTTON_PANEL_PAUSE, &game.button.panel_pause,
15385 GAME_CTRL_ID_PANEL_PAUSE, NULL,
15386 FALSE, "pause game"
15389 IMG_GFX_GAME_BUTTON_PANEL_PLAY, &game.button.panel_play,
15390 GAME_CTRL_ID_PANEL_PLAY, NULL,
15394 IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
15395 SOUND_CTRL_ID_MUSIC, &setup.sound_music,
15396 TRUE, "background music on/off"
15399 IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
15400 SOUND_CTRL_ID_LOOPS, &setup.sound_loops,
15401 TRUE, "sound loops on/off"
15404 IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
15405 SOUND_CTRL_ID_SIMPLE, &setup.sound_simple,
15406 TRUE, "normal sounds on/off"
15409 IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC, &game.button.panel_sound_music,
15410 SOUND_CTRL_ID_PANEL_MUSIC, &setup.sound_music,
15411 FALSE, "background music on/off"
15414 IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS, &game.button.panel_sound_loops,
15415 SOUND_CTRL_ID_PANEL_LOOPS, &setup.sound_loops,
15416 FALSE, "sound loops on/off"
15419 IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE, &game.button.panel_sound_simple,
15420 SOUND_CTRL_ID_PANEL_SIMPLE, &setup.sound_simple,
15421 FALSE, "normal sounds on/off"
15425 void CreateGameButtons()
15429 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15431 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15432 struct XY *pos = gamebutton_info[i].pos;
15433 struct GadgetInfo *gi;
15436 unsigned int event_mask;
15437 boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15438 boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15439 int base_x = (on_tape ? VX : DX);
15440 int base_y = (on_tape ? VY : DY);
15441 int gd_x = gfx->src_x;
15442 int gd_y = gfx->src_y;
15443 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
15444 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
15445 int gd_xa = gfx->src_x + gfx->active_xoffset;
15446 int gd_ya = gfx->src_y + gfx->active_yoffset;
15447 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15448 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15451 if (gfx->bitmap == NULL)
15453 game_gadget[id] = NULL;
15458 if (id == GAME_CTRL_ID_STOP ||
15459 id == GAME_CTRL_ID_PANEL_STOP ||
15460 id == GAME_CTRL_ID_PLAY ||
15461 id == GAME_CTRL_ID_PANEL_PLAY ||
15462 id == GAME_CTRL_ID_SAVE ||
15463 id == GAME_CTRL_ID_LOAD)
15465 button_type = GD_TYPE_NORMAL_BUTTON;
15467 event_mask = GD_EVENT_RELEASED;
15469 else if (id == GAME_CTRL_ID_UNDO ||
15470 id == GAME_CTRL_ID_REDO)
15472 button_type = GD_TYPE_NORMAL_BUTTON;
15474 event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15478 button_type = GD_TYPE_CHECK_BUTTON;
15479 checked = (gamebutton_info[i].setup_value != NULL ?
15480 *gamebutton_info[i].setup_value : FALSE);
15481 event_mask = GD_EVENT_PRESSED;
15484 gi = CreateGadget(GDI_CUSTOM_ID, id,
15485 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15486 GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15487 GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15488 GDI_WIDTH, gfx->width,
15489 GDI_HEIGHT, gfx->height,
15490 GDI_TYPE, button_type,
15491 GDI_STATE, GD_BUTTON_UNPRESSED,
15492 GDI_CHECKED, checked,
15493 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15494 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15495 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15496 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15497 GDI_DIRECT_DRAW, FALSE,
15498 GDI_EVENT_MASK, event_mask,
15499 GDI_CALLBACK_ACTION, HandleGameButtons,
15503 Error(ERR_EXIT, "cannot create gadget");
15505 game_gadget[id] = gi;
15509 void FreeGameButtons()
15513 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15514 FreeGadget(game_gadget[i]);
15517 static void UnmapGameButtonsAtSamePosition(int id)
15521 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15523 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15524 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15525 UnmapGadget(game_gadget[i]);
15528 static void UnmapGameButtonsAtSamePosition_All()
15530 if (setup.show_snapshot_buttons)
15532 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15533 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15534 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15538 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15539 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15540 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15542 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15543 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15544 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15548 static void MapGameButtonsAtSamePosition(int id)
15552 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15554 gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15555 gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15556 MapGadget(game_gadget[i]);
15558 UnmapGameButtonsAtSamePosition_All();
15561 void MapUndoRedoButtons()
15563 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15564 UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15566 MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15567 MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15569 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15572 void UnmapUndoRedoButtons()
15574 UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15575 UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15577 MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15578 MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15580 ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15583 void MapGameButtonsExt(boolean on_tape)
15587 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15588 if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15589 i != GAME_CTRL_ID_UNDO &&
15590 i != GAME_CTRL_ID_REDO)
15591 MapGadget(game_gadget[i]);
15593 UnmapGameButtonsAtSamePosition_All();
15595 RedrawGameButtons();
15598 void UnmapGameButtonsExt(boolean on_tape)
15602 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15603 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15604 UnmapGadget(game_gadget[i]);
15607 void RedrawGameButtonsExt(boolean on_tape)
15611 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15612 if (!on_tape || gamebutton_info[i].allowed_on_tape)
15613 RedrawGadget(game_gadget[i]);
15615 // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15616 redraw_mask &= ~REDRAW_ALL;
15619 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15624 gi->checked = state;
15627 void RedrawSoundButtonGadget(int id)
15629 int id2 = (id == SOUND_CTRL_ID_MUSIC ? SOUND_CTRL_ID_PANEL_MUSIC :
15630 id == SOUND_CTRL_ID_LOOPS ? SOUND_CTRL_ID_PANEL_LOOPS :
15631 id == SOUND_CTRL_ID_SIMPLE ? SOUND_CTRL_ID_PANEL_SIMPLE :
15632 id == SOUND_CTRL_ID_PANEL_MUSIC ? SOUND_CTRL_ID_MUSIC :
15633 id == SOUND_CTRL_ID_PANEL_LOOPS ? SOUND_CTRL_ID_LOOPS :
15634 id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15637 SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15638 RedrawGadget(game_gadget[id2]);
15641 void MapGameButtons()
15643 MapGameButtonsExt(FALSE);
15646 void UnmapGameButtons()
15648 UnmapGameButtonsExt(FALSE);
15651 void RedrawGameButtons()
15653 RedrawGameButtonsExt(FALSE);
15656 void MapGameButtonsOnTape()
15658 MapGameButtonsExt(TRUE);
15661 void UnmapGameButtonsOnTape()
15663 UnmapGameButtonsExt(TRUE);
15666 void RedrawGameButtonsOnTape()
15668 RedrawGameButtonsExt(TRUE);
15671 void GameUndoRedoExt()
15673 ClearPlayerAction();
15675 tape.pausing = TRUE;
15678 UpdateAndDisplayGameControlValues();
15680 DrawCompleteVideoDisplay();
15681 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15682 DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15683 DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15688 void GameUndo(int steps)
15690 if (!CheckEngineSnapshotList())
15693 LoadEngineSnapshot_Undo(steps);
15698 void GameRedo(int steps)
15700 if (!CheckEngineSnapshotList())
15703 LoadEngineSnapshot_Redo(steps);
15708 static void HandleGameButtonsExt(int id, int button)
15710 static boolean game_undo_executed = FALSE;
15711 int steps = BUTTON_STEPSIZE(button);
15712 boolean handle_game_buttons =
15713 (game_status == GAME_MODE_PLAYING ||
15714 (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15716 if (!handle_game_buttons)
15721 case GAME_CTRL_ID_STOP:
15722 case GAME_CTRL_ID_PANEL_STOP:
15723 if (game_status == GAME_MODE_MAIN)
15729 RequestQuitGame(TRUE);
15733 case GAME_CTRL_ID_PAUSE:
15734 case GAME_CTRL_ID_PAUSE2:
15735 case GAME_CTRL_ID_PANEL_PAUSE:
15736 if (options.network && game_status == GAME_MODE_PLAYING)
15738 #if defined(NETWORK_AVALIABLE)
15740 SendToServer_ContinuePlaying();
15742 SendToServer_PausePlaying();
15746 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15748 game_undo_executed = FALSE;
15752 case GAME_CTRL_ID_PLAY:
15753 case GAME_CTRL_ID_PANEL_PLAY:
15754 if (game_status == GAME_MODE_MAIN)
15756 StartGameActions(options.network, setup.autorecord, level.random_seed);
15758 else if (tape.pausing)
15760 #if defined(NETWORK_AVALIABLE)
15761 if (options.network)
15762 SendToServer_ContinuePlaying();
15765 TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15769 case GAME_CTRL_ID_UNDO:
15770 // Important: When using "save snapshot when collecting an item" mode,
15771 // load last (current) snapshot for first "undo" after pressing "pause"
15772 // (else the last-but-one snapshot would be loaded, because the snapshot
15773 // pointer already points to the last snapshot when pressing "pause",
15774 // which is fine for "every step/move" mode, but not for "every collect")
15775 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15776 !game_undo_executed)
15779 game_undo_executed = TRUE;
15784 case GAME_CTRL_ID_REDO:
15788 case GAME_CTRL_ID_SAVE:
15792 case GAME_CTRL_ID_LOAD:
15796 case SOUND_CTRL_ID_MUSIC:
15797 case SOUND_CTRL_ID_PANEL_MUSIC:
15798 if (setup.sound_music)
15800 setup.sound_music = FALSE;
15804 else if (audio.music_available)
15806 setup.sound = setup.sound_music = TRUE;
15808 SetAudioMode(setup.sound);
15810 if (game_status == GAME_MODE_PLAYING)
15814 RedrawSoundButtonGadget(id);
15818 case SOUND_CTRL_ID_LOOPS:
15819 case SOUND_CTRL_ID_PANEL_LOOPS:
15820 if (setup.sound_loops)
15821 setup.sound_loops = FALSE;
15822 else if (audio.loops_available)
15824 setup.sound = setup.sound_loops = TRUE;
15826 SetAudioMode(setup.sound);
15829 RedrawSoundButtonGadget(id);
15833 case SOUND_CTRL_ID_SIMPLE:
15834 case SOUND_CTRL_ID_PANEL_SIMPLE:
15835 if (setup.sound_simple)
15836 setup.sound_simple = FALSE;
15837 else if (audio.sound_available)
15839 setup.sound = setup.sound_simple = TRUE;
15841 SetAudioMode(setup.sound);
15844 RedrawSoundButtonGadget(id);
15853 static void HandleGameButtons(struct GadgetInfo *gi)
15855 HandleGameButtonsExt(gi->custom_id, gi->event.button);
15858 void HandleSoundButtonKeys(Key key)
15860 if (key == setup.shortcut.sound_simple)
15861 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15862 else if (key == setup.shortcut.sound_loops)
15863 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15864 else if (key == setup.shortcut.sound_music)
15865 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);